From 01df258bfc82d51bb12c269ebc18223b5d4df60a Mon Sep 17 00:00:00 2001 From: albi3ro Date: Mon, 24 Jul 2023 13:36:11 -0400 Subject: [PATCH 01/15] default shots on new device interface --- doc/releases/changelog-dev.md | 6 + .../devices/experimental/default_qubit_2.py | 6 +- pennylane/devices/experimental/device_api.py | 15 +- pennylane/interfaces/execution.py | 20 +-- pennylane/interfaces/set_shots.py | 8 ++ pennylane/measurements/shots.py | 6 +- pennylane/qnode.py | 128 +++++++----------- tests/test_qnode.py | 18 +-- 8 files changed, 92 insertions(+), 115 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 16e061079c8..a012c9c816b 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -109,6 +109,12 @@ input. [(#4322)](https://github.com/PennyLaneAI/pennylane/pull/4322) +* `qml.interfaces.set_shots` now has null behaviour for the experimental Device and accepts + a `Shots` object as well as `int`'s and tuples of `int`'s. + +* `QNode.construct` now accepts a `shots` keyword argument, instead of excepting `shots` to be merged + into the `kwargs` dictinary. +

Deprecations 👋

* ``qml.qchem.jordan_wigner`` is deprecated, use ``qml.jordan_wigner`` instead. diff --git a/pennylane/devices/experimental/default_qubit_2.py b/pennylane/devices/experimental/default_qubit_2.py index 63a5cf72a8d..f7f258420b6 100644 --- a/pennylane/devices/experimental/default_qubit_2.py +++ b/pennylane/devices/experimental/default_qubit_2.py @@ -44,6 +44,8 @@ class DefaultQubit2(Device): """A PennyLane device written in Python and capable of backpropagation derivatives. Args: + shots (int, Tuple[int], Tuple[Tuple[int]]): The default number of shots to use in executions involving + this device. seed (Union[None, int, array_like[int], SeedSequence, BitGenerator, Generator]): A seed-like parameter matching that of ``seed`` for ``numpy.random.default_rng``. If no value is provided, a default RNG will be used. @@ -134,8 +136,8 @@ def name(self): """The name of the device.""" return "default.qubit.2" - def __init__(self, seed=None, max_workers=None) -> None: - super().__init__() + def __init__(self, shots=None, seed=None, max_workers=None) -> None: + super().__init__(shots=shots) self._max_workers = max_workers self._rng = np.random.default_rng(seed) self._debugger = None diff --git a/pennylane/devices/experimental/device_api.py b/pennylane/devices/experimental/device_api.py index cbcfbcbe882..036b011e81d 100644 --- a/pennylane/devices/experimental/device_api.py +++ b/pennylane/devices/experimental/device_api.py @@ -20,6 +20,7 @@ from numbers import Number from typing import Callable, Union, Sequence, Tuple, Optional +from pennylane.measurements import Shots from pennylane.tape import QuantumTape, QuantumScript from pennylane.typing import Result, ResultBatch from pennylane import Tracker @@ -134,9 +135,21 @@ def name(self) -> str: self.tracker.record() """ - def __init__(self) -> None: + def __init__(self, shots=None) -> None: # each instance should have its own Tracker. self.tracker = Tracker() + self._shots = Shots(shots) + + @property + def shots(self) -> Shots: + """Default shots for execution workflows containing this device. + + Note that the device itself should **always** pull shots from the provided :class:`QuantumTape` at + :property:`~.QuantumTape.shots`, not from this property. This property is merely to provide consistency + with the old interface. + + """ + return self._shots def preprocess( self, diff --git a/pennylane/interfaces/execution.py b/pennylane/interfaces/execution.py index 76922fb1567..25ee2769ebd 100644 --- a/pennylane/interfaces/execution.py +++ b/pennylane/interfaces/execution.py @@ -23,9 +23,7 @@ # pylint: disable=unused-argument,unnecessary-lambda-assignment,inconsistent-return-statements, # pylint: disable=too-many-statements, invalid-unary-operand-type, function-redefined -import inspect import warnings -from contextlib import _GeneratorContextManager from functools import wraps, partial from typing import Callable, Sequence, Optional, Union, Tuple @@ -244,23 +242,7 @@ def wrapper(tapes: Sequence[QuantumTape], **kwargs): # Tape exists within the cache, store the cached result cached_results[i] = cache[hashes[i]] - # Introspect the set_shots decorator of the input function: - # warn the user in case of finite shots with cached results - finite_shots = False - - closure = inspect.getclosurevars(fn).nonlocals - if "original_fn" in closure: # deal with expand_fn wrapper above - closure = inspect.getclosurevars(closure["original_fn"]).nonlocals - - # retrieve the captured context manager instance (for set_shots) - if "self" in closure and isinstance(closure["self"], _GeneratorContextManager): - # retrieve the shots from the arguments or device instance - if closure["self"].func.__name__ == "set_shots": - dev, shots = closure["self"].args - shots = dev.shots if shots is False else shots - finite_shots = isinstance(shots, int) - - if finite_shots and getattr(cache, "_persistent_cache", True): + if tape.shots and getattr(cache, "_persistent_cache", True): warnings.warn( "Cached execution with finite shots detected!\n" "Note that samples as well as all noisy quantities computed via sampling " diff --git a/pennylane/interfaces/set_shots.py b/pennylane/interfaces/set_shots.py index 3d3643dc1af..3b76ba88937 100644 --- a/pennylane/interfaces/set_shots.py +++ b/pennylane/interfaces/set_shots.py @@ -18,6 +18,9 @@ # pylint: disable=protected-access import contextlib +import pennylane as qml +from pennylane.measurements import Shots + @contextlib.contextmanager def set_shots(device, shots): @@ -40,6 +43,11 @@ def set_shots(device, shots): >>> set_shots(dev, shots=100)(lambda: dev.shots)() 100 """ + if isinstance(device, qml.devices.experimental.Device): + yield + return + if isinstance(shots, Shots): + shots = shots.shot_vector if shots.has_partitioned_shots else shots.total_shots if shots == device.shots: yield return diff --git a/pennylane/measurements/shots.py b/pennylane/measurements/shots.py index 88d6f473000..2d4a4bafe26 100644 --- a/pennylane/measurements/shots.py +++ b/pennylane/measurements/shots.py @@ -190,7 +190,11 @@ def __repr__(self): def __eq__(self, other): """Equality between Shot instances.""" - return self.total_shots == other.total_shots and self.shot_vector == other.shot_vector + return ( + isinstance(other, Shots) + and self.total_shots == other.total_shots + and self.shot_vector == other.shot_vector + ) def __hash__(self): """Hash for a given Shot instance.""" diff --git a/pennylane/qnode.py b/pennylane/qnode.py index d5bc695ae56..ecc89f6e12f 100644 --- a/pennylane/qnode.py +++ b/pennylane/qnode.py @@ -27,7 +27,7 @@ import pennylane as qml from pennylane import Device from pennylane.interfaces import INTERFACE_MAP, SUPPORTED_INTERFACES, set_shots -from pennylane.measurements import ClassicalShadowMP, CountsMP, MidMeasureMP +from pennylane.measurements import ClassicalShadowMP, CountsMP, MidMeasureMP, Shots from pennylane.tape import QuantumTape, make_qscript @@ -49,6 +49,14 @@ def _convert_to_interface(res, interface): return qml.math.asarray(res, like=interface if interface != "tf" else "tensorflow") +def _get_device_shots(device) -> Shots: + if isinstance(device, qml.devices.experimental.Device): + return device.shots + if device.shot_vector: + return Shots(device._raw_shot_sequence) + return Shots(device.shots) + + class QNode: """Represents a quantum node in the hybrid computational graph. @@ -473,7 +481,9 @@ def __init__( self.gradient_kwargs = {} self._tape_cached = False - self._update_gradient_fn() + original_device = self.device + self._update_gradient_fn(shots=_get_device_shots(self.device)) + self.device = original_device functools.update_wrapper(self, func) self._transform_program = qml.transforms.core.TransformProgram() @@ -503,7 +513,7 @@ def interface(self, value): ) self._interface = INTERFACE_MAP[value] - self._update_gradient_fn() + self._update_gradient_fn(shots=_get_device_shots(self.device)) @property def transform_program(self): @@ -520,7 +530,7 @@ def add_transform(self, transform_container): """ self._transform_program.push_back(transform_container=transform_container) - def _update_gradient_fn(self, shots=None): + def _update_gradient_fn(self, shots=Shots(None)): if self.diff_method is None: self._interface = None self.gradient_fn = None @@ -539,31 +549,30 @@ def _update_gradient_fn(self, shots=None): return self.gradient_fn, self.gradient_kwargs, self.device = self.get_gradient_fn( - self._original_device, self.interface, self.diff_method, shots=shots + self.device, self.interface, self.diff_method, shots=shots ) self.gradient_kwargs.update(self._user_gradient_kwargs or {}) - def _update_original_device(self): + def _update_original_device(self, original_device, substituted_device): # FIX: If the qnode swapped the device, increase the num_execution value on the original device. # In the long run, we should make sure that the user's device is the one # actually run so she has full control. This could be done by changing the class # of the user's device before and after executing the tape. - - if self.device is not self._original_device: + if substituted_device is not original_device: if not self._tape_cached: - self._original_device._num_executions += 1 # pylint: disable=protected-access + original_device._num_executions += 1 # pylint: disable=protected-access # Update for state vector simulators that have the _pre_rotated_state attribute - if hasattr(self._original_device, "_pre_rotated_state"): - self._original_device._pre_rotated_state = self.device._pre_rotated_state + if hasattr(original_device, "_pre_rotated_state"): + original_device._pre_rotated_state = substituted_device._pre_rotated_state # Update for state vector simulators that have the _state attribute - if hasattr(self._original_device, "_state"): - self._original_device._state = self.device._state + if hasattr(original_device, "_state"): + original_device._state = substituted_device._state # pylint: disable=too-many-return-statements @staticmethod - def get_gradient_fn(device, interface, diff_method="best", shots=None): + def get_gradient_fn(device, interface, diff_method="best", shots=Shots(None)): """Determine the best differentiation method, interface, and device for a requested device, interface, and diff method. @@ -618,7 +627,7 @@ def get_gradient_fn(device, interface, diff_method="best", shots=None): ) @staticmethod - def get_best_method(device, interface, shots=None): + def get_best_method(device, interface, shots=Shots(None)): """Returns the 'best' differentiation method for a particular device and interface combination. @@ -692,8 +701,8 @@ def best_method_str(device, interface): return transform @staticmethod - def _validate_backprop_method(device, interface, shots=None): - if shots is not None or getattr(device, "shots", None) is not None: + def _validate_backprop_method(device, interface, shots=Shots(None)): + if shots or device.shots: raise qml.QuantumFunctionError("Backpropagation is only supported when shots=None.") if isinstance(device, qml.devices.experimental.Device): @@ -708,7 +717,6 @@ def _validate_backprop_method(device, interface, shots=None): # determine if the device supports backpropagation backprop_interface = device.capabilities().get("passthru_interface", None) - if backprop_interface is not None: # device supports backpropagation natively @@ -733,16 +741,12 @@ def _validate_backprop_method(device, interface, shots=None): # TODO: need a better way of passing existing device init options # to a new device? - expand_fn = device.expand_fn - batch_transform = device.batch_transform - - device = qml.device( - backprop_devices[mapped_interface], wires=device.wires, shots=device.shots - ) - device.expand_fn = expand_fn - device.batch_transform = batch_transform - - return "backprop", {}, device + new_device = qml.device(backprop_devices[mapped_interface], wires=device.wires) + new_device.expand_fn = device.expand_fn + new_device.batch_transform = device.batch_transform + if device._debugger: + new_device._debugger = device._debugger + return "backprop", {}, new_device raise qml.QuantumFunctionError( f"Device {device.short_name} only supports diff_method='backprop' when using the " @@ -824,23 +828,16 @@ def tape(self) -> QuantumTape: qtape = tape # for backwards compatibility - def construct(self, args, kwargs): # pylint: disable=too-many-branches + def construct(self, args, kwargs, shots="unset"): # pylint: disable=too-many-branches """Call the quantum function with a tape context, ensuring the operations get queued.""" old_interface = self.interface - if not self._qfunc_uses_shots_arg: - shots = kwargs.pop("shots", None) - else: - shots = ( - self._original_device._raw_shot_sequence - if self._original_device._shot_vector - else self._original_device.shots - ) + shots = Shots(self.device.shots) if shots == "unset" else shots if old_interface == "auto": self.interface = qml.math.get_interface(*args, *list(kwargs.values())) - self._tape = make_qscript(self.func, shots)(*args, **kwargs) + self._tape = make_qscript(self.func, shots=shots)(*args, **kwargs) self._qfunc_output = self.tape._qfunc_output params = self.tape.get_parameters(trainable_only=False) @@ -910,46 +907,23 @@ def construct(self, args, kwargs): # pylint: disable=too-many-branches if old_interface == "auto": self.interface = "auto" + # pylint: disable=not-callable def __call__(self, *args, **kwargs) -> qml.typing.Result: - override_shots = False + original_grad_fn = [self.gradient_fn, self.gradient_kwargs, self.device] old_interface = self.interface if old_interface == "auto": self.interface = qml.math.get_interface(*args, *list(kwargs.values())) self.device.tracker = self._original_device.tracker - if not self._qfunc_uses_shots_arg: - # If shots specified in call but not in qfunc signature, - # interpret it as device shots value for this call. - override_shots = kwargs.get("shots", False) - - if override_shots is not False: - # Since shots has changed, we need to update the preferred gradient function. - # This is because the gradient function chosen at initialization may - # no longer be applicable. - - # store the initialization gradient function - original_grad_fn = [self.gradient_fn, self.gradient_kwargs, self.device] - - # pylint: disable=not-callable - # update the gradient function - if isinstance(self._original_device, Device): - set_shots(self._original_device, override_shots)(self._update_gradient_fn)() - else: - self._update_gradient_fn(shots=override_shots) - - else: - if isinstance(self._original_device, Device): - kwargs["shots"] = ( - self._original_device._raw_shot_sequence - if self._original_device._shot_vector - else self._original_device.shots - ) - else: - kwargs["shots"] = None + if self._qfunc_uses_shots_arg: + override_shots = _get_device_shots(self.device) + else: + override_shots = Shots(kwargs.pop("shots", _get_device_shots(self.device))) + set_shots(self.device, override_shots)(self._update_gradient_fn)(shots=override_shots) # construct the tape - self.construct(args, kwargs) + self.construct(args, kwargs, shots=override_shots) cache = self.execute_kwargs.get("cache", False) using_custom_cache = ( @@ -996,11 +970,11 @@ def __call__(self, *args, **kwargs) -> qml.typing.Result: else: res = type(self.tape._qfunc_output)(res) - if override_shots is not False: - # restore the initialization gradient function - self.gradient_fn, self.gradient_kwargs, self.device = original_grad_fn + self._update_original_device( + original_device=original_grad_fn[2], substituted_device=self.device + ) - self._update_original_device() + self.gradient_fn, self.gradient_kwargs, self.device = original_grad_fn return res if "mode" in self.execute_kwargs: @@ -1057,11 +1031,9 @@ def __call__(self, *args, **kwargs) -> qml.typing.Result: qfunc_output_type = type(self._qfunc_output) return qfunc_output_type(res) - if override_shots is not False: - # restore the initialization gradient function - self.gradient_fn, self.gradient_kwargs, self.device = original_grad_fn - - self._update_original_device() + self._update_original_device(original_grad_fn[2], self.device) + # restore the initialization gradient function + self.gradient_fn, self.gradient_kwargs, self.device = original_grad_fn if isinstance(self._qfunc_output, Sequence) or ( self.tape.is_sampled and self.device._has_partitioned_shots() diff --git a/tests/test_qnode.py b/tests/test_qnode.py index 9ab072e246b..fae14a097ed 100644 --- a/tests/test_qnode.py +++ b/tests/test_qnode.py @@ -76,7 +76,7 @@ def circuit(x): qml.RX(x, wires=0) return qml.probs(wires=0) - assert circuit.device.short_name == "default.qubit.autograd" + assert circuit.device.short_name == "default.qubit" assert circuit.gradient_fn == "backprop" circuit.interface = "torch" @@ -537,7 +537,7 @@ def func(x): assert ( repr(qn) - == "" + == "" ) @pytest.mark.autograd @@ -1268,7 +1268,7 @@ def circuit(): with qml.queuing.AnnotatedQueue() as q: circuit() - assert q.queue == [] + assert q.queue == [] # pylint: disable=use-implicit-booleaness-not-comparison assert len(circuit.tape.operations) == 1 @@ -1426,7 +1426,7 @@ def test_warning_finite_shots_tape(self): qml.RZ(0.3, wires=0) qml.expval(qml.PauliZ(0)) - tape = QuantumScript.from_queue(q) + tape = QuantumScript.from_queue(q, shots=5) # no warning on the first execution cache = {} qml.execute([tape], dev, None, cache=cache) @@ -1696,16 +1696,6 @@ def supports_derivatives(self, execution_config=None, circuit=None) -> bool: assert not kwargs assert new_dev is dev - def test_shots_not_set_on_device(self): - """Test that shots are not set on the device when override shots are passed on a call.""" - - def f(): - return qml.expval(qml.PauliZ(0)) - - qn = QNode(f, self.dev) - qn(shots=10) - assert getattr(self.dev, "shots", "not here") == "not here" - def test_shots_integration(self): """Test that shots provided at call time are passed through the workflow.""" From 0d07d0632aa66a3c5d6501e220d89625dab201ef Mon Sep 17 00:00:00 2001 From: albi3ro Date: Tue, 25 Jul 2023 14:58:02 -0400 Subject: [PATCH 02/15] make fewer changes and clean stuff up later --- doc/releases/changelog-dev.md | 13 +- pennylane/qnode.py | 118 +++++++++++------- .../experimental/test_default_qubit_2.py | 9 ++ tests/devices/experimental/test_device_api.py | 11 ++ tests/test_qnode.py | 27 ++-- 5 files changed, 108 insertions(+), 70 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index a012c9c816b..bd7dbbea0fd 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -76,6 +76,13 @@ * When given a callable, `qml.ctrl` now does its custom pre-processing on all queued operators from the callable. [(#4370)](https://github.com/PennyLaneAI/pennylane/pull/4370) +* `qml.interfaces.set_shots` now has null behaviour for the experimental Device and accepts + a `Shots` object as well as `int`'s and tuples of `int`'s. + +* `pennylane.devices.experimental.Device` now accepts a shots keyword argument and has a `shots` + property. This property is merely used to set defaults for a workflow, and does not directly influence the number of shots used in executions or derivatives. + [(#4388)](https://github.com/PennyLaneAI/pennylane/pull/4388) +

Breaking changes 💔

* The `do_queue` keyword argument in `qml.operation.Operator` has been removed. Instead of @@ -109,12 +116,6 @@ input. [(#4322)](https://github.com/PennyLaneAI/pennylane/pull/4322) -* `qml.interfaces.set_shots` now has null behaviour for the experimental Device and accepts - a `Shots` object as well as `int`'s and tuples of `int`'s. - -* `QNode.construct` now accepts a `shots` keyword argument, instead of excepting `shots` to be merged - into the `kwargs` dictinary. -

Deprecations 👋

* ``qml.qchem.jordan_wigner`` is deprecated, use ``qml.jordan_wigner`` instead. diff --git a/pennylane/qnode.py b/pennylane/qnode.py index ecc89f6e12f..8f5652d8126 100644 --- a/pennylane/qnode.py +++ b/pennylane/qnode.py @@ -49,12 +49,13 @@ def _convert_to_interface(res, interface): return qml.math.asarray(res, like=interface if interface != "tf" else "tensorflow") +# pylint: disable=protected-access def _get_device_shots(device) -> Shots: - if isinstance(device, qml.devices.experimental.Device): - return device.shots - if device.shot_vector: - return Shots(device._raw_shot_sequence) - return Shots(device.shots) + if isinstance(device, Device): + if device._shot_vector: + return Shots(device._raw_shot_sequence) + return Shots(device.shots) + return device.shots class QNode: @@ -481,9 +482,7 @@ def __init__( self.gradient_kwargs = {} self._tape_cached = False - original_device = self.device - self._update_gradient_fn(shots=_get_device_shots(self.device)) - self.device = original_device + self._update_gradient_fn() functools.update_wrapper(self, func) self._transform_program = qml.transforms.core.TransformProgram() @@ -513,7 +512,7 @@ def interface(self, value): ) self._interface = INTERFACE_MAP[value] - self._update_gradient_fn(shots=_get_device_shots(self.device)) + self._update_gradient_fn() @property def transform_program(self): @@ -530,7 +529,7 @@ def add_transform(self, transform_container): """ self._transform_program.push_back(transform_container=transform_container) - def _update_gradient_fn(self, shots=Shots(None)): + def _update_gradient_fn(self, shots=None): if self.diff_method is None: self._interface = None self.gradient_fn = None @@ -549,30 +548,31 @@ def _update_gradient_fn(self, shots=Shots(None)): return self.gradient_fn, self.gradient_kwargs, self.device = self.get_gradient_fn( - self.device, self.interface, self.diff_method, shots=shots + self._original_device, self.interface, self.diff_method, shots=shots ) self.gradient_kwargs.update(self._user_gradient_kwargs or {}) - def _update_original_device(self, original_device, substituted_device): + def _update_original_device(self): # FIX: If the qnode swapped the device, increase the num_execution value on the original device. # In the long run, we should make sure that the user's device is the one # actually run so she has full control. This could be done by changing the class # of the user's device before and after executing the tape. - if substituted_device is not original_device: + + if self.device is not self._original_device: if not self._tape_cached: - original_device._num_executions += 1 # pylint: disable=protected-access + self._original_device._num_executions += 1 # pylint: disable=protected-access # Update for state vector simulators that have the _pre_rotated_state attribute - if hasattr(original_device, "_pre_rotated_state"): - original_device._pre_rotated_state = substituted_device._pre_rotated_state + if hasattr(self._original_device, "_pre_rotated_state"): + self._original_device._pre_rotated_state = self.device._pre_rotated_state # Update for state vector simulators that have the _state attribute - if hasattr(original_device, "_state"): - original_device._state = substituted_device._state + if hasattr(self._original_device, "_state"): + self._original_device._state = self.device._state # pylint: disable=too-many-return-statements @staticmethod - def get_gradient_fn(device, interface, diff_method="best", shots=Shots(None)): + def get_gradient_fn(device, interface, diff_method="best", shots=None): """Determine the best differentiation method, interface, and device for a requested device, interface, and diff method. @@ -627,7 +627,7 @@ def get_gradient_fn(device, interface, diff_method="best", shots=Shots(None)): ) @staticmethod - def get_best_method(device, interface, shots=Shots(None)): + def get_best_method(device, interface, shots=None): """Returns the 'best' differentiation method for a particular device and interface combination. @@ -701,8 +701,9 @@ def best_method_str(device, interface): return transform @staticmethod - def _validate_backprop_method(device, interface, shots=Shots(None)): - if shots or device.shots: + def _validate_backprop_method(device, interface, shots=None): + if shots is not None or _get_device_shots(device): + print(shots, _get_device_shots(device)) raise qml.QuantumFunctionError("Backpropagation is only supported when shots=None.") if isinstance(device, qml.devices.experimental.Device): @@ -717,6 +718,7 @@ def _validate_backprop_method(device, interface, shots=Shots(None)): # determine if the device supports backpropagation backprop_interface = device.capabilities().get("passthru_interface", None) + if backprop_interface is not None: # device supports backpropagation natively @@ -741,12 +743,16 @@ def _validate_backprop_method(device, interface, shots=Shots(None)): # TODO: need a better way of passing existing device init options # to a new device? - new_device = qml.device(backprop_devices[mapped_interface], wires=device.wires) - new_device.expand_fn = device.expand_fn - new_device.batch_transform = device.batch_transform - if device._debugger: - new_device._debugger = device._debugger - return "backprop", {}, new_device + expand_fn = device.expand_fn + batch_transform = device.batch_transform + + device = qml.device( + backprop_devices[mapped_interface], wires=device.wires, shots=device.shots + ) + device.expand_fn = expand_fn + device.batch_transform = batch_transform + + return "backprop", {}, device raise qml.QuantumFunctionError( f"Device {device.short_name} only supports diff_method='backprop' when using the " @@ -828,16 +834,19 @@ def tape(self) -> QuantumTape: qtape = tape # for backwards compatibility - def construct(self, args, kwargs, shots="unset"): # pylint: disable=too-many-branches + def construct(self, args, kwargs): # pylint: disable=too-many-branches """Call the quantum function with a tape context, ensuring the operations get queued.""" old_interface = self.interface - shots = Shots(self.device.shots) if shots == "unset" else shots + if self._qfunc_uses_shots_arg: + shots = _get_device_shots(self._original_device) + else: + shots = kwargs.pop("shots", _get_device_shots(self._original_device)) if old_interface == "auto": self.interface = qml.math.get_interface(*args, *list(kwargs.values())) - self._tape = make_qscript(self.func, shots=shots)(*args, **kwargs) + self._tape = make_qscript(self.func, shots)(*args, **kwargs) self._qfunc_output = self.tape._qfunc_output params = self.tape.get_parameters(trainable_only=False) @@ -907,23 +916,38 @@ def construct(self, args, kwargs, shots="unset"): # pylint: disable=too-many-br if old_interface == "auto": self.interface = "auto" - # pylint: disable=not-callable def __call__(self, *args, **kwargs) -> qml.typing.Result: - original_grad_fn = [self.gradient_fn, self.gradient_kwargs, self.device] + override_shots = False old_interface = self.interface if old_interface == "auto": self.interface = qml.math.get_interface(*args, *list(kwargs.values())) self.device.tracker = self._original_device.tracker - if self._qfunc_uses_shots_arg: - override_shots = _get_device_shots(self.device) - else: - override_shots = Shots(kwargs.pop("shots", _get_device_shots(self.device))) - set_shots(self.device, override_shots)(self._update_gradient_fn)(shots=override_shots) + if not self._qfunc_uses_shots_arg: + # If shots specified in call but not in qfunc signature, + # interpret it as device shots value for this call. + override_shots = kwargs.get("shots", False) + + if override_shots is not False: + # Since shots has changed, we need to update the preferred gradient function. + # This is because the gradient function chosen at initialization may + # no longer be applicable. + + # store the initialization gradient function + original_grad_fn = [self.gradient_fn, self.gradient_kwargs, self.device] + + # pylint: disable=not-callable + # update the gradient function + set_shots(self._original_device, override_shots)(self._update_gradient_fn)( + shots=override_shots + ) + + else: + kwargs["shots"] = _get_device_shots(self._original_device) # construct the tape - self.construct(args, kwargs, shots=override_shots) + self.construct(args, kwargs) cache = self.execute_kwargs.get("cache", False) using_custom_cache = ( @@ -970,11 +994,11 @@ def __call__(self, *args, **kwargs) -> qml.typing.Result: else: res = type(self.tape._qfunc_output)(res) - self._update_original_device( - original_device=original_grad_fn[2], substituted_device=self.device - ) + if override_shots is not False: + # restore the initialization gradient function + self.gradient_fn, self.gradient_kwargs, self.device = original_grad_fn - self.gradient_fn, self.gradient_kwargs, self.device = original_grad_fn + self._update_original_device() return res if "mode" in self.execute_kwargs: @@ -1031,9 +1055,11 @@ def __call__(self, *args, **kwargs) -> qml.typing.Result: qfunc_output_type = type(self._qfunc_output) return qfunc_output_type(res) - self._update_original_device(original_grad_fn[2], self.device) - # restore the initialization gradient function - self.gradient_fn, self.gradient_kwargs, self.device = original_grad_fn + if override_shots is not False: + # restore the initialization gradient function + self.gradient_fn, self.gradient_kwargs, self.device = original_grad_fn + + self._update_original_device() if isinstance(self._qfunc_output, Sequence) or ( self.tape.is_sampled and self.device._has_partitioned_shots() diff --git a/tests/devices/experimental/test_default_qubit_2.py b/tests/devices/experimental/test_default_qubit_2.py index ae5b03fdb35..d94efbfc69f 100644 --- a/tests/devices/experimental/test_default_qubit_2.py +++ b/tests/devices/experimental/test_default_qubit_2.py @@ -29,6 +29,15 @@ def test_name(): assert DefaultQubit2().name == "default.qubit.2" +def test_shots(): + """Test the shots property of DefaultQubit2.""" + assert DefaultQubit2().shots == qml.measurements.Shots(None) + assert DefaultQubit2(shots=100).shots == qml.measurements.Shots(100) + + with pytest.raises(AttributeError): + DefaultQubit2().shots = 10 + + def test_no_jvp_functionality(): """Test that jvp is not supported on DefaultQubit2.""" dev = DefaultQubit2() diff --git a/tests/devices/experimental/test_device_api.py b/tests/devices/experimental/test_device_api.py index fbfb6722d28..48bc7bcdf08 100644 --- a/tests/devices/experimental/test_device_api.py +++ b/tests/devices/experimental/test_device_api.py @@ -49,6 +49,17 @@ def test_device_name(self): """Test the default name is the name of the class""" assert self.dev.name == "MinimalDevice" + def test_shots(self): + """Test default behavior for shots.""" + + assert self.dev.shots == qml.measurements.Shots(None) + + shots_dev = self.MinimalDevice(shots=100) + assert shots_dev.shots == qml.measurements.Shots(100) + + with pytest.raises(AttributeError): + self.dev.shots = 100 + def test_tracker_set_on_initialization(self): """Test that a new tracker instance is initialized with the class.""" assert isinstance(self.dev.tracker, qml.Tracker) diff --git a/tests/test_qnode.py b/tests/test_qnode.py index fae14a097ed..8489b48e29d 100644 --- a/tests/test_qnode.py +++ b/tests/test_qnode.py @@ -76,7 +76,7 @@ def circuit(x): qml.RX(x, wires=0) return qml.probs(wires=0) - assert circuit.device.short_name == "default.qubit" + assert circuit.device.short_name == "default.qubit.autograd" assert circuit.gradient_fn == "backprop" circuit.interface = "torch" @@ -537,7 +537,7 @@ def func(x): assert ( repr(qn) - == "" + == "" ) @pytest.mark.autograd @@ -1268,7 +1268,7 @@ def circuit(): with qml.queuing.AnnotatedQueue() as q: circuit() - assert q.queue == [] # pylint: disable=use-implicit-booleaness-not-comparison + assert q.queue == [] assert len(circuit.tape.operations) == 1 @@ -1734,11 +1734,8 @@ class UnsupportedOp(qml.operation.Operation): num_wires = 1 - def expand(self): - with qml.queuing.AnnotatedQueue() as q: - qml.RX(3 * self.data[0], wires=self.wires) - tape = QuantumScript.from_queue(q) - return tape + def decomposition(self): + return [qml.RX(3 * self.data[0], wires=self.wires)] @qnode(dev, diff_method=diff_method, mode=mode) def circuit(x): @@ -1773,11 +1770,8 @@ class UnsupportedOp(qml.operation.Operation): grad_method = "A" grad_recipe = ([[3 / 2, 1, np.pi / 6], [-3 / 2, 1, -np.pi / 6]],) - def expand(self): - with qml.queuing.AnnotatedQueue() as q: - qml.RX(3 * self.data[0], wires=self.wires) - tape = QuantumScript.from_queue(q) - return tape + def decomposition(self): + return [qml.RX(3 * self.data[0], wires=self.wires)] @qnode(dev, interface="autograd", diff_method="parameter-shift", max_diff=2) def circuit(x): @@ -1818,11 +1812,8 @@ class PhaseShift(qml.PhaseShift): grad_method = None - def expand(self): - with qml.queuing.AnnotatedQueue() as q: - qml.RY(3 * self.data[0], wires=self.wires) - tape = QuantumScript.from_queue(q) - return tape + def decomposition(self): + return [qml.RY(3 * self.data[0], wires=self.wires)] @qnode(dev, diff_method="parameter-shift", max_diff=2) def circuit(x): From 399e49211d66e2188c22cd534da1577253c8c610 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Tue, 25 Jul 2023 17:32:57 -0400 Subject: [PATCH 03/15] fix tests, lint, and sphinx --- pennylane/devices/experimental/device_api.py | 5 +- tests/devices/experimental/test_device_api.py | 2 +- tests/interfaces/test_set_shots.py | 55 +++++++++++++++++++ 3 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 tests/interfaces/test_set_shots.py diff --git a/pennylane/devices/experimental/device_api.py b/pennylane/devices/experimental/device_api.py index 036b011e81d..064bff2077c 100644 --- a/pennylane/devices/experimental/device_api.py +++ b/pennylane/devices/experimental/device_api.py @@ -144,9 +144,8 @@ def __init__(self, shots=None) -> None: def shots(self) -> Shots: """Default shots for execution workflows containing this device. - Note that the device itself should **always** pull shots from the provided :class:`QuantumTape` at - :property:`~.QuantumTape.shots`, not from this property. This property is merely to provide consistency - with the old interface. + Note that the device itself should **always** pull shots from the provided :class:`~.QuantumTape` and its + :attr:`~.QuantumTape.shots`, not from this property. This property is used to provide a default at the start of a workflow. """ return self._shots diff --git a/tests/devices/experimental/test_device_api.py b/tests/devices/experimental/test_device_api.py index 48bc7bcdf08..05c6eb449c3 100644 --- a/tests/devices/experimental/test_device_api.py +++ b/tests/devices/experimental/test_device_api.py @@ -58,7 +58,7 @@ def test_shots(self): assert shots_dev.shots == qml.measurements.Shots(100) with pytest.raises(AttributeError): - self.dev.shots = 100 + self.dev.shots = 100 # pylint: disable=attribute-defined-outside-init def test_tracker_set_on_initialization(self): """Test that a new tracker instance is initialized with the class.""" diff --git a/tests/interfaces/test_set_shots.py b/tests/interfaces/test_set_shots.py new file mode 100644 index 00000000000..88143960906 --- /dev/null +++ b/tests/interfaces/test_set_shots.py @@ -0,0 +1,55 @@ +# Copyright 2018-2023 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Tests for interfaces.set_shots +""" + +import pennylane as qml +from pennylane.interfaces import set_shots +from pennylane.measurements import Shots + + +def test_shots_new_device_interface(): + """Test that calling set shots on something in new new device interface leaves it + untouched. + """ + dev = qml.devices.experimental.DefaultQubit2() + with set_shots(dev, 10): + assert dev.shots == Shots(None) + + +def test_set_with_shots_class(): + """Test that shots can be set on the old device interface with a Shots class.""" + + dev = qml.devices.DefaultQubit(wires=1) + with set_shots(dev, Shots(10)): + assert dev.shots == 10 + + assert dev.shots is None + + shot_tuples = Shots((10, 10)) + with set_shots(dev, shot_tuples): + assert dev.shots == 20 + assert dev.shot_vector == list(shot_tuples.shot_vector) + + assert dev.shots is None + + +def test_shots_not_altered_if_False(): + """Test a value of False can be passed to shots, indicating to not override + shots on the device.""" + + dev = qml.devices.DefaultQubit(wires=1) + with set_shots(dev, False): + assert dev.shots is None From 80fd531f1532d9b598d0f31e4f8d710514c9d3fb Mon Sep 17 00:00:00 2001 From: Christina Lee Date: Mon, 31 Jul 2023 09:09:17 -0400 Subject: [PATCH 04/15] Update doc/releases/changelog-dev.md --- doc/releases/changelog-dev.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index d728973a9e9..1560b7a9867 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -83,8 +83,9 @@ * When given a callable, `qml.ctrl` now does its custom pre-processing on all queued operators from the callable. [(#4370)](https://github.com/PennyLaneAI/pennylane/pull/4370) -* `qml.interfaces.set_shots` now has null behaviour for the experimental Device and accepts - a `Shots` object as well as `int`'s and tuples of `int`'s. +* `qml.interfaces.set_shots` can now accepts shots specified as a `qml.measurements.Shots` object. + If a `pennylane.devices.experimental.Device` is provided, the `set_shots` will no longer raise an error, but will simply return without doing anything. + [(#4388)](https://github.com/PennyLaneAI/pennylane/pull/4388) * `pennylane.devices.experimental.Device` now accepts a shots keyword argument and has a `shots` property. This property is merely used to set defaults for a workflow, and does not directly influence the number of shots used in executions or derivatives. From cf02d83099f6d6b1c49d29582449e5eb756cd299 Mon Sep 17 00:00:00 2001 From: Christina Lee Date: Mon, 31 Jul 2023 17:28:11 -0400 Subject: [PATCH 05/15] Update tests/interfaces/test_set_shots.py Co-authored-by: Matthew Silverman --- tests/interfaces/test_set_shots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/interfaces/test_set_shots.py b/tests/interfaces/test_set_shots.py index 88143960906..c67ddb6390f 100644 --- a/tests/interfaces/test_set_shots.py +++ b/tests/interfaces/test_set_shots.py @@ -21,7 +21,7 @@ def test_shots_new_device_interface(): - """Test that calling set shots on something in new new device interface leaves it + """Test that calling set_shots on a device implementing the new interface leaves it untouched. """ dev = qml.devices.experimental.DefaultQubit2() From 2e776ac7ba5a71dcbab599828607563c08794333 Mon Sep 17 00:00:00 2001 From: Christina Lee Date: Mon, 31 Jul 2023 17:28:53 -0400 Subject: [PATCH 06/15] Update tests/devices/experimental/test_default_qubit_2.py --- .../experimental/test_default_qubit_2.py | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/tests/devices/experimental/test_default_qubit_2.py b/tests/devices/experimental/test_default_qubit_2.py index 59fe56ecb47..6bc09bfb0cc 100644 --- a/tests/devices/experimental/test_default_qubit_2.py +++ b/tests/devices/experimental/test_default_qubit_2.py @@ -38,30 +38,6 @@ def test_shots(): DefaultQubit2().shots = 10 -def test_no_jvp_functionality(): - """Test that jvp is not supported on DefaultQubit2.""" - dev = DefaultQubit2() - - assert not dev.supports_jvp(ExecutionConfig()) - - with pytest.raises(NotImplementedError): - dev.compute_jvp(qml.tape.QuantumScript(), (10, 10)) - - with pytest.raises(NotImplementedError): - dev.execute_and_compute_jvp(qml.tape.QuantumScript(), (10, 10)) - - -def test_no_vjp_functionality(): - """Test that vjp is not supported on DefaultQubit2.""" - dev = DefaultQubit2() - - assert not dev.supports_vjp(ExecutionConfig()) - - with pytest.raises(NotImplementedError): - dev.compute_vjp(qml.tape.QuantumScript(), (10.0, 10.0)) - - with pytest.raises(NotImplementedError): - dev.execute_and_compute_vjp(qml.tape.QuantumScript(), (10.0, 10.0)) def test_no_device_derivatives(): From a4f4c2505063886a7ea2a97d473ddeb6a63199f9 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Tue, 1 Aug 2023 09:26:32 -0400 Subject: [PATCH 07/15] make set_shots error with new device, shots type hinting --- doc/releases/changelog-dev.md | 7 ++++--- pennylane/devices/experimental/default_qubit_2.py | 2 +- pennylane/interfaces/set_shots.py | 6 ++++-- pennylane/qnode.py | 10 ++++++---- tests/interfaces/test_set_shots.py | 7 +++++-- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index d728973a9e9..1cbc9afec3b 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -83,11 +83,12 @@ * When given a callable, `qml.ctrl` now does its custom pre-processing on all queued operators from the callable. [(#4370)](https://github.com/PennyLaneAI/pennylane/pull/4370) -* `qml.interfaces.set_shots` now has null behaviour for the experimental Device and accepts - a `Shots` object as well as `int`'s and tuples of `int`'s. +* `qml.interfaces.set_shots` accepts `Shots` object as well as `int`'s and tuples of `int`'s. + [(#4388)](https://github.com/PennyLaneAI/pennylane/pull/4388) * `pennylane.devices.experimental.Device` now accepts a shots keyword argument and has a `shots` - property. This property is merely used to set defaults for a workflow, and does not directly influence the number of shots used in executions or derivatives. + property. This property is merely used to set defaults for a workflow, and does not directly + influence the number of shots used in executions or derivatives. [(#4388)](https://github.com/PennyLaneAI/pennylane/pull/4388)

Breaking changes 💔

diff --git a/pennylane/devices/experimental/default_qubit_2.py b/pennylane/devices/experimental/default_qubit_2.py index f7f258420b6..9f482c98f5f 100644 --- a/pennylane/devices/experimental/default_qubit_2.py +++ b/pennylane/devices/experimental/default_qubit_2.py @@ -44,7 +44,7 @@ class DefaultQubit2(Device): """A PennyLane device written in Python and capable of backpropagation derivatives. Args: - shots (int, Tuple[int], Tuple[Tuple[int]]): The default number of shots to use in executions involving + shots (int, Sequence[int], Sequence[Union[int, Sequence[int]]]): The default number of shots to use in executions involving this device. seed (Union[None, int, array_like[int], SeedSequence, BitGenerator, Generator]): A seed-like parameter matching that of ``seed`` for ``numpy.random.default_rng``. diff --git a/pennylane/interfaces/set_shots.py b/pennylane/interfaces/set_shots.py index 3b76ba88937..43cebd2e83c 100644 --- a/pennylane/interfaces/set_shots.py +++ b/pennylane/interfaces/set_shots.py @@ -44,8 +44,10 @@ def set_shots(device, shots): 100 """ if isinstance(device, qml.devices.experimental.Device): - yield - return + raise ValueError( + "The new device interface is not compatible with `set_shots`." + "Set shots when calling the qnode or put the shots on the QuantumTape." + ) if isinstance(shots, Shots): shots = shots.shot_vector if shots.has_partitioned_shots else shots.total_shots if shots == device.shots: diff --git a/pennylane/qnode.py b/pennylane/qnode.py index 8f5652d8126..0836e015d2b 100644 --- a/pennylane/qnode.py +++ b/pennylane/qnode.py @@ -939,10 +939,12 @@ def __call__(self, *args, **kwargs) -> qml.typing.Result: # pylint: disable=not-callable # update the gradient function - set_shots(self._original_device, override_shots)(self._update_gradient_fn)( - shots=override_shots - ) - + if isinstance(self._original_device, qml.Device): + set_shots(self._original_device, override_shots)(self._update_gradient_fn)( + shots=override_shots + ) + else: + self._update_gradient_fn(shots=override_shots) else: kwargs["shots"] = _get_device_shots(self._original_device) diff --git a/tests/interfaces/test_set_shots.py b/tests/interfaces/test_set_shots.py index 88143960906..5c9de074451 100644 --- a/tests/interfaces/test_set_shots.py +++ b/tests/interfaces/test_set_shots.py @@ -15,6 +15,8 @@ Tests for interfaces.set_shots """ +import pytest + import pennylane as qml from pennylane.interfaces import set_shots from pennylane.measurements import Shots @@ -25,8 +27,9 @@ def test_shots_new_device_interface(): untouched. """ dev = qml.devices.experimental.DefaultQubit2() - with set_shots(dev, 10): - assert dev.shots == Shots(None) + with pytest.raises(ValueError): + with set_shots(dev, 10): + pass def test_set_with_shots_class(): From 18a43cf57c93c2c160a3e45fcf2bc2fe950bbef8 Mon Sep 17 00:00:00 2001 From: Christina Lee Date: Tue, 1 Aug 2023 09:33:57 -0400 Subject: [PATCH 08/15] Update doc/releases/changelog-dev.md Co-authored-by: Tom Bromley <49409390+trbromley@users.noreply.github.com> --- doc/releases/changelog-dev.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index e95fba3f417..4ad976e6b60 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -90,7 +90,7 @@ <<<<<<< HEAD * `qml.interfaces.set_shots` accepts `Shots` object as well as `int`'s and tuples of `int`'s. ======= -* `qml.interfaces.set_shots` can now accepts shots specified as a `qml.measurements.Shots` object. +* `qml.interfaces.set_shots` can now accept shots specified as a `qml.measurements.Shots` object. If a `pennylane.devices.experimental.Device` is provided, the `set_shots` will no longer raise an error, but will simply return without doing anything. >>>>>>> 2e776ac7ba5a71dcbab599828607563c08794333 [(#4388)](https://github.com/PennyLaneAI/pennylane/pull/4388) From 7fd30ad4423905dd7c614be5f8a89d03382ed1d9 Mon Sep 17 00:00:00 2001 From: Christina Lee Date: Tue, 1 Aug 2023 10:02:39 -0400 Subject: [PATCH 09/15] Update pennylane/interfaces/set_shots.py Co-authored-by: Matthew Silverman --- pennylane/interfaces/set_shots.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pennylane/interfaces/set_shots.py b/pennylane/interfaces/set_shots.py index 43cebd2e83c..e45a194e90d 100644 --- a/pennylane/interfaces/set_shots.py +++ b/pennylane/interfaces/set_shots.py @@ -45,7 +45,8 @@ def set_shots(device, shots): """ if isinstance(device, qml.devices.experimental.Device): raise ValueError( - "The new device interface is not compatible with `set_shots`." + "The new device interface is not compatible with `set_shots`. " + "Set shots when calling the qnode or put the shots on the QuantumTape." ) if isinstance(shots, Shots): From 7641dbcad74fc7cd3d9b32c809f3557e0fbe7573 Mon Sep 17 00:00:00 2001 From: Christina Lee Date: Tue, 1 Aug 2023 10:04:01 -0400 Subject: [PATCH 10/15] Apply suggestions from code review --- doc/releases/changelog-dev.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 4ad976e6b60..ce715008407 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -87,12 +87,12 @@ * When given a callable, `qml.ctrl` now does its custom pre-processing on all queued operators from the callable. [(#4370)](https://github.com/PennyLaneAI/pennylane/pull/4370) -<<<<<<< HEAD + * `qml.interfaces.set_shots` accepts `Shots` object as well as `int`'s and tuples of `int`'s. -======= + [(#4388)](https://github.com/PennyLaneAI/pennylane/pull/4388) + * `qml.interfaces.set_shots` can now accept shots specified as a `qml.measurements.Shots` object. If a `pennylane.devices.experimental.Device` is provided, the `set_shots` will no longer raise an error, but will simply return without doing anything. ->>>>>>> 2e776ac7ba5a71dcbab599828607563c08794333 [(#4388)](https://github.com/PennyLaneAI/pennylane/pull/4388) * `pennylane.devices.experimental.Device` now accepts a shots keyword argument and has a `shots` From 51dbceb53412ebd507857e1a3b98f258d4ff8abd Mon Sep 17 00:00:00 2001 From: Christina Lee Date: Tue, 1 Aug 2023 14:02:05 -0400 Subject: [PATCH 11/15] Update tests/devices/experimental/test_default_qubit_2.py --- tests/devices/experimental/test_default_qubit_2.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tests/devices/experimental/test_default_qubit_2.py b/tests/devices/experimental/test_default_qubit_2.py index 6bc09bfb0cc..6e2e21d5f0d 100644 --- a/tests/devices/experimental/test_default_qubit_2.py +++ b/tests/devices/experimental/test_default_qubit_2.py @@ -38,19 +38,6 @@ def test_shots(): DefaultQubit2().shots = 10 - - -def test_no_device_derivatives(): - """Test that DefaultQubit2 currently doesn't support device derivatives.""" - dev = DefaultQubit2() - - with pytest.raises(NotImplementedError): - dev.compute_derivatives(qml.tape.QuantumScript()) - - with pytest.raises(NotImplementedError): - dev.execute_and_compute_derivatives(qml.tape.QuantumScript()) - - def test_debugger_attribute(): """Test that DefaultQubit2 has a debugger attribute and that it is `None`""" # pylint: disable=protected-access From fc61700356f6ecd8289cea5082a3031d603052b2 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Tue, 1 Aug 2023 14:03:42 -0400 Subject: [PATCH 12/15] merge problem --- tests/devices/experimental/test_default_qubit_2.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tests/devices/experimental/test_default_qubit_2.py b/tests/devices/experimental/test_default_qubit_2.py index 6bc09bfb0cc..6e2e21d5f0d 100644 --- a/tests/devices/experimental/test_default_qubit_2.py +++ b/tests/devices/experimental/test_default_qubit_2.py @@ -38,19 +38,6 @@ def test_shots(): DefaultQubit2().shots = 10 - - -def test_no_device_derivatives(): - """Test that DefaultQubit2 currently doesn't support device derivatives.""" - dev = DefaultQubit2() - - with pytest.raises(NotImplementedError): - dev.compute_derivatives(qml.tape.QuantumScript()) - - with pytest.raises(NotImplementedError): - dev.execute_and_compute_derivatives(qml.tape.QuantumScript()) - - def test_debugger_attribute(): """Test that DefaultQubit2 has a debugger attribute and that it is `None`""" # pylint: disable=protected-access From 560ffbcae4e93d02ae49f766e5b6bc7870722ffb Mon Sep 17 00:00:00 2001 From: albi3ro Date: Tue, 1 Aug 2023 14:04:32 -0400 Subject: [PATCH 13/15] black --- pennylane/interfaces/set_shots.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane/interfaces/set_shots.py b/pennylane/interfaces/set_shots.py index e45a194e90d..779cb3246c8 100644 --- a/pennylane/interfaces/set_shots.py +++ b/pennylane/interfaces/set_shots.py @@ -46,7 +46,6 @@ def set_shots(device, shots): if isinstance(device, qml.devices.experimental.Device): raise ValueError( "The new device interface is not compatible with `set_shots`. " - "Set shots when calling the qnode or put the shots on the QuantumTape." ) if isinstance(shots, Shots): From 6cf23c8055b0f8594cbc9e7f72a881eeccceff8c Mon Sep 17 00:00:00 2001 From: Christina Lee Date: Tue, 1 Aug 2023 14:05:01 -0400 Subject: [PATCH 14/15] Update doc/releases/changelog-dev.md --- doc/releases/changelog-dev.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 280d2e12a0c..230904748ab 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -91,9 +91,6 @@ * `qml.interfaces.set_shots` accepts `Shots` object as well as `int`'s and tuples of `int`'s. [(#4388)](https://github.com/PennyLaneAI/pennylane/pull/4388) -* `qml.interfaces.set_shots` can now accept shots specified as a `qml.measurements.Shots` object. - If a `pennylane.devices.experimental.Device` is provided, the `set_shots` will no longer raise an error, but will simply return without doing anything. - [(#4388)](https://github.com/PennyLaneAI/pennylane/pull/4388) * `pennylane.devices.experimental.Device` now accepts a shots keyword argument and has a `shots` property. This property is merely used to set defaults for a workflow, and does not directly From 0bd6cf7cfa07ae6bb40b977c05f4c0bcd47920f7 Mon Sep 17 00:00:00 2001 From: Christina Lee Date: Wed, 2 Aug 2023 12:39:55 -0400 Subject: [PATCH 15/15] Update pennylane/qnode.py Co-authored-by: Edward Jiang <34989448+eddddddy@users.noreply.github.com> --- pennylane/qnode.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane/qnode.py b/pennylane/qnode.py index 52df589d598..4a02926e49b 100644 --- a/pennylane/qnode.py +++ b/pennylane/qnode.py @@ -703,7 +703,6 @@ def best_method_str(device, interface): @staticmethod def _validate_backprop_method(device, interface, shots=None): if shots is not None or _get_device_shots(device): - print(shots, _get_device_shots(device)) raise qml.QuantumFunctionError("Backpropagation is only supported when shots=None.") if isinstance(device, qml.devices.experimental.Device):