PennyLaneAI / pennylane

PennyLane is a cross-platform Python library for quantum computing, quantum machine learning, and quantum chemistry. Train a quantum computer the same way as a neural network.
https://pennylane.ai
Apache License 2.0
2.29k stars 590 forks source link

[BUG] Hamiltonian with `grouping_type="qwc"` breaks noisy device #2800

Open Qottmann opened 2 years ago

Qottmann commented 2 years ago

Expected behavior

qfunc to execute as expected (i.e. as without setting grouping_type="qwc"

Actual behavior

Adding grouping breaks in the noisy simulator. Seems to be queuing issue.

Additional information

No response

Source code

import pennylane as qml
import pennylane.numpy as np

n_wires = 2

# Describe noise
noise_gate = qml.DepolarizingChannel
noise_strength = 0.05

# Load devices
dev_ideal = qml.device("default.mixed", wires=n_wires)
dev_noisy = qml.transforms.insert(noise_gate, noise_strength, position="all")(dev_ideal)

H = qml.Hamiltonian([1., 1., 1.], [qml.PauliX(0), qml.PauliZ(0), qml.PauliX(0) @ qml.PauliX(1)], grouping_type="qwc")
@qml.qnode(dev_noisy)
def qfunc(x):
    qml.RY(x[0], wires=0)
    qml.RZ(x[1], wires=0)
    return qml.expval(H)

x = np.ones(2)
qfunc(x)

Tracebacks

OrderedDict([(expval(PauliZ(wires=[0])), {'owner': expval(PauliZ(wires=[0]))}), (PauliZ(wires=[0]), {'owner': expval(PauliZ(wires=[0]) @ PauliZ(wires=[1]))}), (PauliZ(wires=[1]), {'owner': expval(PauliZ(wires=[0]) @ PauliZ(wires=[1]))}), (expval(PauliZ(wires=[0]) @ PauliZ(wires=[1])), {'owns': (PauliZ(wires=[0]), PauliZ(wires=[1])), 'owner': expval(PauliZ(wires=[0]) @ PauliZ(wires=[1]))}), (expval(PauliZ(wires=[0])), {'owns': expval(PauliZ(wires=[0]))}), (expval(PauliZ(wires=[0]) @ PauliZ(wires=[1])), {'owns': expval(PauliZ(wires=[0]) @ PauliZ(wires=[1]))}), (RY(tensor(1., requires_grad=True), wires=[0]), {}), (DepolarizingChannel(0.05, wires=[0]), {}), (RZ(tensor(1., requires_grad=True), wires=[0]), {}), (DepolarizingChannel(0.05, wires=[0]), {}), (RY(-1.5707963267948966, wires=[0]), {}), (DepolarizingChannel(0.05, wires=[0]), {}), (RY(-1.5707963267948966, wires=[1]), {}), (DepolarizingChannel(0.05, wires=[1]), {}), (expval(PauliZ(wires=[0])), {'owns': expval(PauliZ(wires=[0]))}), (expval(PauliZ(wires=[0]) @ PauliZ(wires=[1])), {'owns': PauliZ(wires=[0]) @ PauliZ(wires=[1])})])
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/home/qottmann/Qottmann/Xanadu/Sandbox.ipynb Cell 2' in <cell line: 26>()
     23     return qml.expval(H)
     25 x = np.ones(2)
---> 26 qfunc(x)

File ~/Qottmann/Xanadu/pennylane/pennylane/qnode.py:619, in QNode.__call__(self, *args, **kwargs)
    612 using_custom_cache = (
    613     hasattr(cache, "__getitem__")
    614     and hasattr(cache, "__setitem__")
    615     and hasattr(cache, "__delitem__")
    616 )
    617 self._tape_cached = using_custom_cache and self.tape.hash in cache
--> 619 res = qml.execute(
    620     [self.tape],
    621     device=self.device,
    622     gradient_fn=self.gradient_fn,
    623     interface=self.interface,
    624     gradient_kwargs=self.gradient_kwargs,
    625     override_shots=override_shots,
    626     **self.execute_kwargs,
    627 )
    629 if autograd.isinstance(res, (tuple, list)) and len(res) == 1:
    630     # If a device batch transform was applied, we need to 'unpack'
    631     # the returned tuple/list to a float.
   (...)
    638     # TODO: find a more explicit way of determining that a batch transform
    639     # was applied.
    641     res = res[0]

File ~/Qottmann/Xanadu/pennylane/pennylane/interfaces/execution.py:344, in execute(tapes, device, gradient_fn, interface, mode, gradient_kwargs, cache, cachesize, max_diff, override_shots, expand_fn, max_expansion, device_batch_transform)
    340     return batch_fn(res)
    342 if gradient_fn == "backprop" or interface is None:
    343     return batch_fn(
--> 344         qml.interfaces.cache_execute(
    345             batch_execute, cache, return_tuple=False, expand_fn=expand_fn
    346         )(tapes)
    347     )
    349 # the default execution function is batch_execute
    350 execute_fn = qml.interfaces.cache_execute(batch_execute, cache, expand_fn=expand_fn)

File ~/Qottmann/Xanadu/pennylane/pennylane/interfaces/execution.py:172, in cache_execute.<locals>.wrapper(tapes, **kwargs)
    168         return (res, []) if return_tuple else res
    170 else:
    171     # execute all unique tapes that do not exist in the cache
--> 172     res = fn(execution_tapes.values(), **kwargs)
    174 final_res = []
    176 for i, tape in enumerate(tapes):

File ~/Qottmann/Xanadu/pennylane/pennylane/interfaces/execution.py:96, in cache_execute.<locals>.fn(tapes, **kwargs)
     95 def fn(tapes, **kwargs):  # pylint: disable=function-redefined
---> 96     tapes = [expand_fn(tape) for tape in tapes]
     97     return original_fn(tapes, **kwargs)

File ~/Qottmann/Xanadu/pennylane/pennylane/interfaces/execution.py:96, in <listcomp>(.0)
     95 def fn(tapes, **kwargs):  # pylint: disable=function-redefined
---> 96     tapes = [expand_fn(tape) for tape in tapes]
     97     return original_fn(tapes, **kwargs)

File ~/Qottmann/Xanadu/pennylane/pennylane/interfaces/execution.py:325, in execute.<locals>.<lambda>(tape)
    322 batch_execute = set_shots(device, override_shots)(device.batch_execute)
    324 if expand_fn == "device":
--> 325     expand_fn = lambda tape: device.expand_fn(tape, max_expansion=max_expansion)
    327 if gradient_fn is None:
    328     # don't unwrap if it's an interface device
    329     if "passthru_interface" in device.capabilities():

File ~/Qottmann/Xanadu/pennylane/pennylane/_device.py:678, in Device.expand_fn(self, circuit, max_expansion)
    660 """Method for expanding or decomposing an input circuit.
    661 Can be the default or a custom expansion method, see
    662 :meth:`.Device.default_expand_fn` and :meth:`.Device.custom_expand` for more
   (...)
    675     will natively support all operations.
    676 """
    677 if self.custom_expand_fn is not None:
--> 678     return self.custom_expand_fn(circuit, max_expansion=max_expansion)
    680 return self.default_expand_fn(circuit, max_expansion=max_expansion)

File ~/Qottmann/Xanadu/pennylane/pennylane/transforms/qfunc_transforms.py:185, in _create_qfunc_internal_wrapper.<locals>.new_expand_fn(self, tape, *args, **kwargs)
    183 @new_dev.custom_expand
    184 def new_expand_fn(self, tape, *args, **kwargs):  # pylint: disable=unused-variable
--> 185     tape = tape_transform(tape, *transform_args, **transform_kwargs)
    186     return self.default_expand_fn(tape, *args, **kwargs)

File ~/Qottmann/Xanadu/pennylane/pennylane/transforms/qfunc_transforms.py:170, in single_tape_transform.__call__(self, tape, *args, **kwargs)
    167 # new_tape, when first created, is of the class (NonQueuingTape, tape.__class__), so that it
    168 # doesn't result in a nested tape
    169 with tape_class() as new_tape:
--> 170     self.transform_fn(tape, *args, **kwargs)
    172 # Once we're done, revert it back to be simply an instance of tape.__class__.
    173 new_tape.__class__ = tape.__class__

File ~/Qottmann/Xanadu/pennylane/pennylane/tape/tape.py:359, in QuantumTape.__exit__(self, exception_type, exception_value, traceback)
    357 try:
    358     super().__exit__(exception_type, exception_value, traceback)
--> 359     self._process_queue()
    360 finally:
    361     QuantumTape._lock.release()

File ~/Qottmann/Xanadu/pennylane/pennylane/transforms/qfunc_transforms.py:87, in NonQueuingTape._process_queue(self)
     85 def _process_queue(self):
     86     # accesses the parent of the target class that NonQueingTape is mixed into
---> 87     super()._process_queue()  # pylint:disable=no-member
     89     for obj, info in self._queue.items():
     90         qml.queuing.QueuingContext.append(obj, **info)

File ~/Qottmann/Xanadu/pennylane/pennylane/tape/tape.py:463, in QuantumTape._process_queue(self)
    461         elif list_order[obj._queue_category] < list_order[current_list]:
    462             print(self._queue)
--> 463             raise ValueError(
    464                 f"{obj._queue_category[1:]} operation {obj} must occur prior "
    465                 f"to {current_list[1:]}. Please place earlier in the queue."
    466             )
    467         getattr(self, obj._queue_category).append(obj)
    469 self._update()

ValueError: ops operation RY(tensor(1., requires_grad=True), wires=[0]) must occur prior to measurements. Please place earlier in the queue.

System information

0.24/master

Existing GitHub issues

Jaybsoni commented 2 years ago

Interesting, if you use the following syntax it seems to work perfectly:

    @qml.qnode(dev_ideal)
    @qml.transforms.insert(noise_gate, noise_strength, position="all")
    def qfunc(x):
        qml.RY(x[0], wires=0)
        qml.RZ(x[1], wires=0)
        return qml.expval(H)

    x = np.ones(2)
    print(qfunc(x))

This is the intended syntax for the tape transform insert as described in the docs. Was this syntax suggested somewhere else in a demo or the docs?

Jaybsoni commented 2 years ago

[sc-22556]