From 0a019157a1dba45cd461831dcd57d207dbfe32b9 Mon Sep 17 00:00:00 2001 From: Guillermo Alonso-Linaje <65235481+KetpuntoG@users.noreply.github.com> Date: Thu, 19 Sep 2024 15:32:04 -0400 Subject: [PATCH 01/11] recursive approach --- pennylane/ops/op_math/adjoint.py | 8 ++++++++ pennylane/ops/op_math/condition.py | 7 +++++++ pennylane/ops/op_math/controlled.py | 18 ++++++++++++++++++ pennylane/ops/op_math/prod.py | 8 ++++++++ 4 files changed, 41 insertions(+) diff --git a/pennylane/ops/op_math/adjoint.py b/pennylane/ops/op_math/adjoint.py index 325b80c1b7e..4b2b0e9b897 100644 --- a/pennylane/ops/op_math/adjoint.py +++ b/pennylane/ops/op_math/adjoint.py @@ -22,6 +22,7 @@ from pennylane.compiler import compiler from pennylane.math import conj, moveaxis, transpose from pennylane.operation import Observable, Operation, Operator +from pennylane.ops.op_math.controlled import remove_from_queue_args_and_kwargs from pennylane.queuing import QueuingManager from pennylane.tape import make_qscript @@ -236,6 +237,13 @@ def _adjoint_transform(qfunc: Callable, lazy=True) -> Callable: @wraps(qfunc) def wrapper(*args, **kwargs): qscript = make_qscript(qfunc)(*args, **kwargs) + + for arg in args: + remove_from_queue_args_and_kwargs(arg) + + for key, value in kwargs.items(): + remove_from_queue_args_and_kwargs(value) + if lazy: adjoint_ops = [Adjoint(op) for op in reversed(qscript.operations)] else: diff --git a/pennylane/ops/op_math/condition.py b/pennylane/ops/op_math/condition.py index f12ea2bc1f7..84dbe54e4ed 100644 --- a/pennylane/ops/op_math/condition.py +++ b/pennylane/ops/op_math/condition.py @@ -25,6 +25,7 @@ from pennylane.compiler import compiler from pennylane.measurements import MeasurementValue from pennylane.operation import AnyWires, Operation, Operator +from pennylane.ops.op_math.controlled import remove_from_queue_args_and_kwargs from pennylane.ops.op_math.symbolicop import SymbolicOp from pennylane.tape import make_qscript @@ -623,6 +624,12 @@ def wrapper(*args, **kwargs): # 1. Apply true_fn conditionally qscript = make_qscript(true_fn)(*args, **kwargs) + for arg in args: + remove_from_queue_args_and_kwargs(arg) + + for key, value in kwargs.items(): + remove_from_queue_args_and_kwargs(value) + if qscript.measurements: raise ConditionalTransformError(with_meas_err) diff --git a/pennylane/ops/op_math/controlled.py b/pennylane/ops/op_math/controlled.py index 6448f1dc061..564cd0e0e4d 100644 --- a/pennylane/ops/op_math/controlled.py +++ b/pennylane/ops/op_math/controlled.py @@ -199,11 +199,29 @@ def create_controlled_op(op, control, control_values=None, work_wires=None): return _ctrl_transform(op, control, control_values, work_wires) +def remove_from_queue_args_and_kwargs(item): + """function used to recursively remove operators that have been added to the queue in an argument or kwarg.""" + if isinstance(item, (list, tuple, set)): + for elem in item: + remove_from_queue_args_and_kwargs(elem) + elif isinstance(item, dict): + for key, value in item.items(): + remove_from_queue_args_and_kwargs(value) + else: + qml.queuing.QueuingManager.remove(item) + + def _ctrl_transform(op, control, control_values, work_wires): @wraps(op) def wrapper(*args, **kwargs): qscript = qml.tape.make_qscript(op)(*args, **kwargs) + for arg in args: + remove_from_queue_args_and_kwargs(arg) + + for key, value in kwargs.items(): + remove_from_queue_args_and_kwargs(value) + # flip control_values == 0 wires here, so we don't have to do it for each individual op. flip_control_on_zero = (len(qscript) > 1) and (control_values is not None) op_control_values = None if flip_control_on_zero else control_values diff --git a/pennylane/ops/op_math/prod.py b/pennylane/ops/op_math/prod.py index 1bbe30e29f2..3ad96608ec8 100644 --- a/pennylane/ops/op_math/prod.py +++ b/pennylane/ops/op_math/prod.py @@ -27,6 +27,7 @@ import pennylane as qml from pennylane import math from pennylane.operation import Operator, convert_to_opmath +from pennylane.ops.op_math.controlled import remove_from_queue_args_and_kwargs from pennylane.ops.op_math.pow import Pow from pennylane.ops.op_math.sprod import SProd from pennylane.ops.op_math.sum import Sum @@ -111,6 +112,13 @@ def prod(*ops, id=None, lazy=True): @wraps(fn) def wrapper(*args, **kwargs): qs = qml.tape.make_qscript(fn)(*args, **kwargs) + + for arg in args: + remove_from_queue_args_and_kwargs(arg) + + for key, value in kwargs.items(): + remove_from_queue_args_and_kwargs(value) + if len(qs.operations) == 1: if qml.QueuingManager.recording(): qml.apply(qs[0]) From e8d20487d6ae3ae14a0ac7fa6cd442c8d7a3defc Mon Sep 17 00:00:00 2001 From: Guillermo Alonso-Linaje <65235481+KetpuntoG@users.noreply.github.com> Date: Thu, 19 Sep 2024 15:41:09 -0400 Subject: [PATCH 02/11] Update controlled.py --- pennylane/ops/op_math/controlled.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/ops/op_math/controlled.py b/pennylane/ops/op_math/controlled.py index 564cd0e0e4d..441d54e7b36 100644 --- a/pennylane/ops/op_math/controlled.py +++ b/pennylane/ops/op_math/controlled.py @@ -207,7 +207,7 @@ def remove_from_queue_args_and_kwargs(item): elif isinstance(item, dict): for key, value in item.items(): remove_from_queue_args_and_kwargs(value) - else: + elif isinstance(item, qml.Operator): qml.queuing.QueuingManager.remove(item) From 4de0ac3f388c9e76318de217eaeb935a62734835 Mon Sep 17 00:00:00 2001 From: Guillermo Alonso-Linaje <65235481+KetpuntoG@users.noreply.github.com> Date: Thu, 19 Sep 2024 15:42:53 -0400 Subject: [PATCH 03/11] Update controlled.py --- pennylane/ops/op_math/controlled.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/ops/op_math/controlled.py b/pennylane/ops/op_math/controlled.py index 441d54e7b36..2b26cc96715 100644 --- a/pennylane/ops/op_math/controlled.py +++ b/pennylane/ops/op_math/controlled.py @@ -207,7 +207,7 @@ def remove_from_queue_args_and_kwargs(item): elif isinstance(item, dict): for key, value in item.items(): remove_from_queue_args_and_kwargs(value) - elif isinstance(item, qml.Operator): + elif isinstance(item, Operator): qml.queuing.QueuingManager.remove(item) From 000b88ddc6d38d3e1fcbdbeed95311f35877bb97 Mon Sep 17 00:00:00 2001 From: Guillermo Alonso-Linaje <65235481+KetpuntoG@users.noreply.github.com> Date: Thu, 19 Sep 2024 16:56:18 -0400 Subject: [PATCH 04/11] adding tests --- pennylane/ops/op_math/adjoint.py | 2 +- pennylane/ops/op_math/condition.py | 7 ------- pennylane/ops/op_math/controlled.py | 4 ++-- pennylane/ops/op_math/prod.py | 6 ------ tests/ops/op_math/test_adjoint.py | 15 +++++++++++++++ tests/ops/op_math/test_controlled.py | 15 +++++++++++++++ tests/ops/op_math/test_prod.py | 15 +++++++++++++++ 7 files changed, 48 insertions(+), 16 deletions(-) diff --git a/pennylane/ops/op_math/adjoint.py b/pennylane/ops/op_math/adjoint.py index 4b2b0e9b897..3d547a73c84 100644 --- a/pennylane/ops/op_math/adjoint.py +++ b/pennylane/ops/op_math/adjoint.py @@ -241,7 +241,7 @@ def wrapper(*args, **kwargs): for arg in args: remove_from_queue_args_and_kwargs(arg) - for key, value in kwargs.items(): + for value in kwargs.values(): remove_from_queue_args_and_kwargs(value) if lazy: diff --git a/pennylane/ops/op_math/condition.py b/pennylane/ops/op_math/condition.py index 84dbe54e4ed..f12ea2bc1f7 100644 --- a/pennylane/ops/op_math/condition.py +++ b/pennylane/ops/op_math/condition.py @@ -25,7 +25,6 @@ from pennylane.compiler import compiler from pennylane.measurements import MeasurementValue from pennylane.operation import AnyWires, Operation, Operator -from pennylane.ops.op_math.controlled import remove_from_queue_args_and_kwargs from pennylane.ops.op_math.symbolicop import SymbolicOp from pennylane.tape import make_qscript @@ -624,12 +623,6 @@ def wrapper(*args, **kwargs): # 1. Apply true_fn conditionally qscript = make_qscript(true_fn)(*args, **kwargs) - for arg in args: - remove_from_queue_args_and_kwargs(arg) - - for key, value in kwargs.items(): - remove_from_queue_args_and_kwargs(value) - if qscript.measurements: raise ConditionalTransformError(with_meas_err) diff --git a/pennylane/ops/op_math/controlled.py b/pennylane/ops/op_math/controlled.py index 2b26cc96715..8a5ddc15af7 100644 --- a/pennylane/ops/op_math/controlled.py +++ b/pennylane/ops/op_math/controlled.py @@ -205,7 +205,7 @@ def remove_from_queue_args_and_kwargs(item): for elem in item: remove_from_queue_args_and_kwargs(elem) elif isinstance(item, dict): - for key, value in item.items(): + for value in item.values(): remove_from_queue_args_and_kwargs(value) elif isinstance(item, Operator): qml.queuing.QueuingManager.remove(item) @@ -219,7 +219,7 @@ def wrapper(*args, **kwargs): for arg in args: remove_from_queue_args_and_kwargs(arg) - for key, value in kwargs.items(): + for value in kwargs.values(): remove_from_queue_args_and_kwargs(value) # flip control_values == 0 wires here, so we don't have to do it for each individual op. diff --git a/pennylane/ops/op_math/prod.py b/pennylane/ops/op_math/prod.py index 3ad96608ec8..ddff2afd84c 100644 --- a/pennylane/ops/op_math/prod.py +++ b/pennylane/ops/op_math/prod.py @@ -113,12 +113,6 @@ def prod(*ops, id=None, lazy=True): def wrapper(*args, **kwargs): qs = qml.tape.make_qscript(fn)(*args, **kwargs) - for arg in args: - remove_from_queue_args_and_kwargs(arg) - - for key, value in kwargs.items(): - remove_from_queue_args_and_kwargs(value) - if len(qs.operations) == 1: if qml.QueuingManager.recording(): qml.apply(qs[0]) diff --git a/tests/ops/op_math/test_adjoint.py b/tests/ops/op_math/test_adjoint.py index c8d20ead0b7..3054c217c0f 100644 --- a/tests/ops/op_math/test_adjoint.py +++ b/tests/ops/op_math/test_adjoint.py @@ -868,6 +868,21 @@ def test_single_op_defined_outside_queue_eager(self): assert len(q) == 1 assert q.queue[0] is out + def test_correct_queued_operators(self): + """Test that args and kwargs do not add operators to the queue.""" + + dev = qml.device("default.qubit") + + @qml.qnode(dev) + def circuit(): + qml.adjoint(qml.QSVT)(qml.X(1), [qml.Z(1)]) + qml.adjoint(qml.QSVT(qml.X(1), [qml.Z(1)])) + return qml.state() + + circuit() + for op in circuit.tape.operations: + assert op.name == "Adjoint(QSVT)" + @pytest.mark.usefixtures("use_legacy_opmath") def test_single_observable(self): """Test passing a single preconstructed observable in a queuing context.""" diff --git a/tests/ops/op_math/test_controlled.py b/tests/ops/op_math/test_controlled.py index 40e315449b1..77428d03412 100644 --- a/tests/ops/op_math/test_controlled.py +++ b/tests/ops/op_math/test_controlled.py @@ -1899,6 +1899,21 @@ def test_nested_pauli_x_based_ctrl_ops(self): expected = qml.MultiControlledX(wires=[3, 2, 1, 0], control_values=[1, 0, 1]) assert op == expected + def test_correct_queued_operators(self): + """Test that args and kwargs do not add operators to the queue.""" + + dev = qml.device("default.qubit") + + @qml.qnode(dev) + def circuit(): + qml.ctrl(qml.QSVT, control=0)(qml.X(1), [qml.Z(1)]) + qml.ctrl(qml.QSVT(qml.X(1), [qml.Z(1)]), control=0) + return qml.state() + + circuit() + for op in circuit.tape.operations: + assert op.name == "C(QSVT)" + class _Rot(Operation): """A rotation operation that is not an instance of Rot diff --git a/tests/ops/op_math/test_prod.py b/tests/ops/op_math/test_prod.py index 7963ce3fb6c..a26459ad010 100644 --- a/tests/ops/op_math/test_prod.py +++ b/tests/ops/op_math/test_prod.py @@ -1425,6 +1425,21 @@ def test_nonlazy_mode_queueing(self): assert len(q) == 1 assert q.queue[0] is prod2 + def test_correct_queued_operators(self): + """Test that args and kwargs do not add operators to the queue.""" + + dev = qml.device("default.qubit") + + @qml.qnode(dev) + def circuit(): + qml.prod(qml.QSVT)(qml.X(1), [qml.Z(1)]) + qml.prod(qml.QSVT(qml.X(1), [qml.Z(1)])) + return qml.state() + + circuit() + for op in circuit.tape.operations: + assert op.name == "QSVT" + class TestIntegration: """Integration tests for the Prod class.""" From e92154616945db326a5a15b6eb51840241583eac Mon Sep 17 00:00:00 2001 From: Guillermo Alonso-Linaje <65235481+KetpuntoG@users.noreply.github.com> Date: Thu, 19 Sep 2024 16:56:43 -0400 Subject: [PATCH 05/11] Update prod.py --- pennylane/ops/op_math/prod.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane/ops/op_math/prod.py b/pennylane/ops/op_math/prod.py index ddff2afd84c..ae0fa062434 100644 --- a/pennylane/ops/op_math/prod.py +++ b/pennylane/ops/op_math/prod.py @@ -27,7 +27,6 @@ import pennylane as qml from pennylane import math from pennylane.operation import Operator, convert_to_opmath -from pennylane.ops.op_math.controlled import remove_from_queue_args_and_kwargs from pennylane.ops.op_math.pow import Pow from pennylane.ops.op_math.sprod import SProd from pennylane.ops.op_math.sum import Sum From bfee7c083ea51d1805f6910ddf8643ca02b6bbcf Mon Sep 17 00:00:00 2001 From: Guillermo Alonso-Linaje <65235481+KetpuntoG@users.noreply.github.com> Date: Fri, 20 Sep 2024 09:10:21 -0400 Subject: [PATCH 06/11] add test and changelog --- doc/releases/changelog-dev.md | 3 +++ tests/ops/op_math/test_controlled.py | 10 ++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index d48f921304d..8aa85bcb37b 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -171,6 +171,9 @@ * Fixes a bug where a simple circuit with no parameters or only builtin/numpy arrays as parameters returns autograd tensors. [(#6225)](https://github.com/PennyLaneAI/pennylane/pull/6225) +* Fixes a bug where `qml.ctrl` and `qml.adjoint` queued extra operators if they were defined as the arguments. + [(#6284)](https://github.com/PennyLaneAI/pennylane/pull/6284) +

Contributors ✍️

This release contains contributions from (in alphabetical order): diff --git a/tests/ops/op_math/test_controlled.py b/tests/ops/op_math/test_controlled.py index 77428d03412..12020207ba4 100644 --- a/tests/ops/op_math/test_controlled.py +++ b/tests/ops/op_math/test_controlled.py @@ -1902,17 +1902,19 @@ def test_nested_pauli_x_based_ctrl_ops(self): def test_correct_queued_operators(self): """Test that args and kwargs do not add operators to the queue.""" + def func(dic): + for gate in dic.values(): + qml.apply(gate) + dev = qml.device("default.qubit") @qml.qnode(dev) def circuit(): - qml.ctrl(qml.QSVT, control=0)(qml.X(1), [qml.Z(1)]) - qml.ctrl(qml.QSVT(qml.X(1), [qml.Z(1)]), control=0) + qml.ctrl(func, control=0)({1: qml.X(1), 2: qml.Z(1)}) return qml.state() circuit() - for op in circuit.tape.operations: - assert op.name == "C(QSVT)" + assert len(circuit.tape.operations) == 2 class _Rot(Operation): From 342a92c9c3525f9e6c445b64e8b7ba5783b24393 Mon Sep 17 00:00:00 2001 From: Guillermo Alonso-Linaje <65235481+KetpuntoG@users.noreply.github.com> Date: Fri, 20 Sep 2024 09:13:24 -0400 Subject: [PATCH 07/11] Update pennylane/ops/op_math/prod.py --- pennylane/ops/op_math/prod.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane/ops/op_math/prod.py b/pennylane/ops/op_math/prod.py index 68398b4d7ad..46ab54a531d 100644 --- a/pennylane/ops/op_math/prod.py +++ b/pennylane/ops/op_math/prod.py @@ -111,7 +111,6 @@ def prod(*ops, id=None, lazy=True): @wraps(fn) def wrapper(*args, **kwargs): qs = qml.tape.make_qscript(fn)(*args, **kwargs) - if len(qs.operations) == 1: if qml.QueuingManager.recording(): qml.apply(qs[0]) From 661dd52d90cd8df0bb82408272365aaae9b36ec2 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Mon, 28 Oct 2024 11:22:31 -0400 Subject: [PATCH 08/11] Changes to setup development for 0.40.0 (#6456) --- doc/development/release_notes.md | 2 ++ doc/releases/changelog-dev.md | 19 +++++++++++++++++++ pennylane/_version.py | 2 +- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 doc/releases/changelog-dev.md diff --git a/doc/development/release_notes.md b/doc/development/release_notes.md index 8156f3cf4c3..8941bde392a 100644 --- a/doc/development/release_notes.md +++ b/doc/development/release_notes.md @@ -3,6 +3,8 @@ Release notes This page contains the release notes for PennyLane. +.. mdinclude:: ../releases/changelog-dev.md + .. mdinclude:: ../releases/changelog-0.39.0.md .. mdinclude:: ../releases/changelog-0.38.0.md diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md new file mode 100644 index 00000000000..df4ec5d605a --- /dev/null +++ b/doc/releases/changelog-dev.md @@ -0,0 +1,19 @@ +:orphan: + +# Release 0.40.0-dev (development release) + +

New features since last release

+ +

Improvements 🛠

+ +

Breaking changes 💔

+ +

Deprecations 👋

+ +

Documentation 📝

+ +

Bug fixes 🐛

+ +

Contributors ✍️

+ +This release contains contributions from (in alphabetical order): diff --git a/pennylane/_version.py b/pennylane/_version.py index 58138c79f8b..b797a93e02d 100644 --- a/pennylane/_version.py +++ b/pennylane/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.39.0-rc0" +__version__ = "0.40.0-dev0" From 8dde8f868512cea1a162dee86d015df6e006cbd8 Mon Sep 17 00:00:00 2001 From: Astral Cai Date: Mon, 28 Oct 2024 12:10:42 -0400 Subject: [PATCH 09/11] Use `pytest-rng` generated seeds for stochastic tests (#6435) **Context:** The PennyLane CI suffers from sporadic failures due to stochasticity of test cases. In order to save CI runtime, we have seeded many tests. However, this approach introduces the risk of a test case passing due to a special seed, potentially hiding bugs that could have been discovered. Therefore, it is beneficial to periodically update the seeds to review such tests. **Description of the Change:** - The test suite uses `pytest-rng` to handle seed generation. A `seed` fixture is now available to use for every test case. If seeding is required, please use the seed provided by `pytest-rng` ```python def test_some_function(..., seed): dev = qml.device("default.qubit", seed=seed) ``` - A `local_salt` test marker has been added that allows you to modify the seed locally for a particular test case if the generated seed happens to make your test fail. ```python @pytest.mark.local_salt(42) def test_some_function(..., seed): dev = qml.device("default.qubit", seed=seed) ``` - All tests that uses local seeds has been updated to use the new seeding approach. - An `rng_salt` has been added to `pytest.ini`. This controls the seed generation for the entire test suite. We want to periodically change this `rng_salt` which updates all the seeds across the test suite. - The fixture that sets the global seed for every test case has been removed, and another fixture is added that restores the global seed after each test in case the test modifies the global seed. **Benefits:** We will be able to catch bugs hidden in tests that are passing only because of a magic seed that was set locally. **Possible Drawbacks:** This is a chore to maintain this seed an ensure that all tests pass with the new salt when it is updated. **Related Shortcut Story:** [sc-74294] --------- Co-authored-by: Mudit Pandey --- pennylane/gradients/pulse_gradient.py | 7 - requirements-dev.txt | 1 + tests/capture/test_capture_cond.py | 11 +- tests/capture/test_capture_mid_measure.py | 26 +- tests/capture/test_measurements_capture.py | 12 +- tests/conftest.py | 50 +- .../default_qubit/test_default_qubit.py | 57 +- .../test_default_qubit_native_mcm.py | 41 +- tests/devices/qubit/test_apply_operation.py | 11 +- tests/devices/qubit/test_sampling.py | 129 ++-- tests/devices/qubit/test_simulate.py | 40 +- .../test_qutrit_mixed_sampling.py | 103 ++- .../test_qutrit_mixed_simulate.py | 6 +- tests/devices/test_default_clifford.py | 26 +- tests/devices/test_default_qutrit_mixed.py | 25 +- tests/devices/test_null_qubit.py | 8 +- tests/gradients/core/test_fisher.py | 4 +- .../gradients/core/test_gradient_transform.py | 11 +- tests/gradients/core/test_pulse_gradient.py | 115 +-- tests/gradients/core/test_pulse_odegen.py | 16 +- tests/gradients/core/test_vjp.py | 6 +- .../test_finite_difference_shot_vec.py | 8 +- .../finite_diff/test_spsa_gradient.py | 36 +- .../test_spsa_gradient_shot_vec.py | 70 +- .../parameter_shift/test_parameter_shift.py | 4 +- .../test_parameter_shift_shot_vec.py | 9 +- tests/interfaces/test_autograd.py | 86 ++- tests/interfaces/test_autograd_qnode.py | 45 +- tests/interfaces/test_jax.py | 81 ++- tests/interfaces/test_jax_jit_qnode.py | 685 +++++++++++------- tests/interfaces/test_jax_qnode.py | 370 +++++----- tests/interfaces/test_tensorflow.py | 76 +- ..._tensorflow_autograph_qnode_shot_vector.py | 144 +++- tests/interfaces/test_tensorflow_qnode.py | 29 +- .../test_tensorflow_qnode_shot_vector.py | 40 +- tests/interfaces/test_torch.py | 90 ++- tests/interfaces/test_torch_qnode.py | 29 +- tests/measurements/test_counts.py | 32 +- tests/measurements/test_expval.py | 17 +- tests/measurements/test_probs.py | 39 +- tests/measurements/test_var.py | 10 +- tests/ops/op_math/test_adjoint.py | 8 +- .../op_math/test_controlled_decompositions.py | 4 +- tests/ops/op_math/test_pow_op.py | 4 +- tests/ops/qubit/test_matrix_ops.py | 16 +- tests/ops/qubit/test_special_unitary.py | 7 +- tests/optimize/test_optimize_shot_adaptive.py | 5 +- tests/optimize/test_qnspsa.py | 5 +- tests/pytest.ini | 2 + tests/resource/test_specs.py | 4 +- tests/shadow/test_shadow_transforms.py | 4 +- .../test_embeddings/test_amplitude.py | 4 +- .../test_mottonen_state_prep.py | 4 +- .../test_amplitude_amplification.py | 12 +- .../test_controlled_sequence.py | 12 +- .../templates/test_subroutines/test_qdrift.py | 20 +- .../test_subroutines/test_qubitization.py | 12 +- .../test_subroutines/test_reflection.py | 12 +- tests/test_compiler.py | 4 +- tests/test_debugging.py | 12 + tests/test_hermitian_edge_cases.py | 6 +- tests/test_operation.py | 4 +- tests/test_qnode.py | 11 +- tests/test_tensor_measurements.py | 66 +- tests/test_vqe.py | 37 +- tests/transforms/test_broadcast_expand.py | 47 +- tests/transforms/test_defer_measurements.py | 18 +- tests/transforms/test_dynamic_one_shot.py | 10 +- tests/transforms/test_mitigate.py | 4 +- tests/transforms/test_qcut.py | 16 +- 70 files changed, 1647 insertions(+), 1328 deletions(-) diff --git a/pennylane/gradients/pulse_gradient.py b/pennylane/gradients/pulse_gradient.py index f862c676d73..74eb41d6cbf 100644 --- a/pennylane/gradients/pulse_gradient.py +++ b/pennylane/gradients/pulse_gradient.py @@ -272,13 +272,6 @@ def _psr_and_contract(res_list, cjacs, int_prefactor): # Single measurement without shot vector return _psr_and_contract(results, cjacs, int_prefactor) - # Multiple measurements with shot vector. Not supported with broadcasting yet. - if use_broadcasting: - # TODO: Remove once #2690 is resolved - raise NotImplementedError( - "Broadcasting, multiple measurements and shot vectors are currently not " - "supported all simultaneously by stoch_pulse_grad." - ) return tuple( tuple(_psr_and_contract(_r, cjacs, int_prefactor) for _r in zip(*r)) for r in zip(*results) ) diff --git a/requirements-dev.txt b/requirements-dev.txt index 65652b401ad..cfff525c3c7 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -3,6 +3,7 @@ pytest>=7.1.2 pytest-cov>=3.0.0 pytest-mock>=3.7.0 pytest-xdist>=2.5.0 +pytest-rng flaky>=3.7.0 pytest-forked>=1.4.0 pytest-benchmark diff --git a/tests/capture/test_capture_cond.py b/tests/capture/test_capture_cond.py index 32ba9e46355..56ece6b7619 100644 --- a/tests/capture/test_capture_cond.py +++ b/tests/capture/test_capture_cond.py @@ -653,13 +653,14 @@ def test_circuit_consts(self, pred, arg, expected): res_ev_jxpr = jax.core.eval_jaxpr(jaxpr.jaxpr, jaxpr.consts, *args) assert np.allclose(res_ev_jxpr, expected), f"Expected {expected}, but got {res_ev_jxpr}" + @pytest.mark.local_salt(1) @pytest.mark.parametrize("reset", [True, False]) @pytest.mark.parametrize("postselect", [None, 0, 1]) @pytest.mark.parametrize("shots", [None, 20]) - def test_mcm_predicate_execution(self, reset, postselect, shots): + def test_mcm_predicate_execution(self, reset, postselect, shots, seed): """Test that QNodes executed with mid-circuit measurement predicates for qml.cond give correct results.""" - device = qml.device("default.qubit", wires=3, shots=shots, seed=jax.random.PRNGKey(1234)) + device = qml.device("default.qubit", wires=3, shots=shots, seed=jax.random.PRNGKey(seed)) def true_fn(arg): qml.RX(arg, 0) @@ -682,7 +683,7 @@ def f(x, y): assert np.allclose(res, expected), f"Expected {expected}, but got {res}" - @pytest.mark.parametrize("shots", [None, 100]) + @pytest.mark.parametrize("shots", [None, 300]) @pytest.mark.parametrize( "params, expected", # The parameters used here will essentially apply a PauliX just before mid-circuit @@ -696,11 +697,11 @@ def f(x, y): ([0, 0, 0, 0], (1 / np.sqrt(2), 0, 0, 1)), # false_fn, PauliZ basis ], ) - def test_mcm_predicate_execution_with_elifs(self, params, expected, shots, tol): + def test_mcm_predicate_execution_with_elifs(self, params, expected, shots, tol, seed): """Test that QNodes executed with mid-circuit measurement predicates for qml.cond give correct results when there are also elifs present.""" # pylint: disable=expression-not-assigned - device = qml.device("default.qubit", wires=5, shots=shots, seed=jax.random.PRNGKey(10)) + device = qml.device("default.qubit", wires=5, shots=shots, seed=jax.random.PRNGKey(seed)) def true_fn(): # Adjoint Hadamard diagonalizing gates to get Hadamard basis state diff --git a/tests/capture/test_capture_mid_measure.py b/tests/capture/test_capture_mid_measure.py index 1f246d798fb..a44fd9e342c 100644 --- a/tests/capture/test_capture_mid_measure.py +++ b/tests/capture/test_capture_mid_measure.py @@ -322,12 +322,12 @@ class TestMidMeasureExecute: @pytest.mark.parametrize("reset", [True, False]) @pytest.mark.parametrize("postselect", [None, 0, 1]) @pytest.mark.parametrize("phi", jnp.arange(1.0, 2 * jnp.pi, 1.5)) - def test_simple_circuit_execution(self, phi, reset, postselect, get_device, shots, mp_fn): + def test_simple_circuit_execution(self, phi, reset, postselect, get_device, shots, mp_fn, seed): """Test that circuits with mid-circuit measurements can be executed in a QNode.""" if shots is None and mp_fn is qml.sample: pytest.skip("Cannot measure samples in analytic mode") - dev = get_device(wires=2, shots=shots, seed=jax.random.PRNGKey(12345)) + dev = get_device(wires=2, shots=shots, seed=jax.random.PRNGKey(seed)) @qml.qnode(dev) def f(x): @@ -340,7 +340,7 @@ def f(x): @pytest.mark.parametrize("phi", jnp.arange(1.0, 2 * jnp.pi, 1.5)) @pytest.mark.parametrize("multi_mcm", [True, False]) def test_circuit_with_terminal_measurement_execution( - self, phi, get_device, shots, mp_fn, multi_mcm + self, phi, get_device, shots, mp_fn, multi_mcm, seed ): """Test that circuits with mid-circuit measurements that also collect statistics on the mid-circuit measurements can be executed in a QNode.""" @@ -350,7 +350,7 @@ def test_circuit_with_terminal_measurement_execution( if multi_mcm and mp_fn in (qml.expval, qml.var): pytest.skip("Cannot measure sequences of MCMs with expval or var") - dev = get_device(wires=2, shots=shots, seed=jax.random.PRNGKey(12345)) + dev = get_device(wires=2, shots=shots, seed=jax.random.PRNGKey(seed)) @qml.qnode(dev) def f(x, y): @@ -364,13 +364,13 @@ def f(x, y): @pytest.mark.xfail @pytest.mark.parametrize("phi", jnp.arange(1.0, 2 * jnp.pi, 1.5)) - def test_circuit_with_boolean_arithmetic_execution(self, phi, get_device, shots, mp_fn): + def test_circuit_with_boolean_arithmetic_execution(self, phi, get_device, shots, mp_fn, seed): """Test that circuits that apply boolean logic to mid-circuit measurement values can be executed.""" if shots is None and mp_fn is qml.sample: pytest.skip("Cannot measure samples in analytic mode") - dev = get_device(wires=2, shots=shots, seed=jax.random.PRNGKey(12345)) + dev = get_device(wires=2, shots=shots, seed=jax.random.PRNGKey(seed)) @qml.qnode(dev) def f(x, y): @@ -386,13 +386,13 @@ def f(x, y): @pytest.mark.xfail @pytest.mark.parametrize("phi", jnp.arange(1.0, 2 * jnp.pi, 1.5)) - def test_circuit_with_classical_processing_execution(self, phi, get_device, shots, mp_fn): + def test_circuit_with_classical_processing_execution(self, phi, get_device, shots, mp_fn, seed): """Test that circuits that apply non-boolean operations to mid-circuit measurement values can be executed.""" if shots is None and mp_fn is qml.sample: pytest.skip("Cannot measure samples in analytic mode") - dev = get_device(wires=2, shots=shots, seed=jax.random.PRNGKey(12345)) + dev = get_device(wires=2, shots=shots, seed=jax.random.PRNGKey(seed)) @qml.qnode(dev) def f(x, y): @@ -409,13 +409,15 @@ def f(x, y): @pytest.mark.xfail @pytest.mark.parametrize("phi", jnp.arange(1.0, 2 * jnp.pi, 1.5)) @pytest.mark.parametrize("fn", [jnp.sin, jnp.sqrt, jnp.log, jnp.exp]) - def mid_measure_processed_with_jax_numpy_execution(self, phi, fn, get_device, shots, mp_fn): + def mid_measure_processed_with_jax_numpy_execution( + self, phi, fn, get_device, shots, mp_fn, seed + ): """Test that a circuit containing mid-circuit measurements processed using jax.numpy can be executed.""" if shots is None and mp_fn is qml.sample: pytest.skip("Cannot measure samples in analytic mode") - dev = get_device(wires=2, shots=shots, seed=jax.random.PRNGKey(12345)) + dev = get_device(wires=2, shots=shots, seed=jax.random.PRNGKey(seed)) @qml.qnode(dev) def f(x): @@ -428,13 +430,13 @@ def f(x): @pytest.mark.xfail @pytest.mark.parametrize("phi", jnp.arange(1.0, 2 * jnp.pi, 1.5)) - def test_mid_measure_as_gate_parameter_execution(self, phi, get_device, shots, mp_fn): + def test_mid_measure_as_gate_parameter_execution(self, phi, get_device, shots, mp_fn, seed): """Test that mid-circuit measurements (simple or classical processed) used as gate parameters can be executed.""" if shots is None and mp_fn is qml.sample: pytest.skip("Cannot measure samples in analytic mode") - dev = get_device(wires=2, shots=shots, seed=jax.random.PRNGKey(12345)) + dev = get_device(wires=2, shots=shots, seed=jax.random.PRNGKey(seed)) @qml.qnode(dev) def f(x): diff --git a/tests/capture/test_measurements_capture.py b/tests/capture/test_measurements_capture.py index e43ed204526..21c186fb98c 100644 --- a/tests/capture/test_measurements_capture.py +++ b/tests/capture/test_measurements_capture.py @@ -545,14 +545,14 @@ def f(): @pytest.mark.parametrize("x64_mode", (True, False)) -def test_shadow_expval(x64_mode): +def test_shadow_expval(x64_mode, seed): """Test that the shadow expval of an observable can be captured.""" initial_mode = jax.config.jax_enable_x64 jax.config.update("jax_enable_x64", x64_mode) def f(): - return qml.shadow_expval(qml.X(0), seed=887, k=4) + return qml.shadow_expval(qml.X(0), seed=seed, k=4) jaxpr = jax.make_jaxpr(f)() @@ -561,7 +561,7 @@ def f(): assert jaxpr.eqns[1].primitive == ShadowExpvalMP._obs_primitive assert jaxpr.eqns[0].outvars == jaxpr.eqns[1].invars - assert jaxpr.eqns[1].params == {"seed": 887, "k": 4} + assert jaxpr.eqns[1].params == {"seed": seed, "k": 4} am = jaxpr.eqns[1].outvars[0].aval assert isinstance(am, AbstractMeasurement) @@ -638,11 +638,11 @@ def f(w1, w2): jax.config.update("jax_enable_x64", initial_mode) -def test_ClassicalShadow(): +def test_ClassicalShadow(seed): """Test that the classical shadow measurement can be captured.""" def f(): - return qml.classical_shadow(wires=(0, 1, 2), seed=95) + return qml.classical_shadow(wires=(0, 1, 2), seed=seed) jaxpr = jax.make_jaxpr(f)() @@ -650,7 +650,7 @@ def f(): assert len(jaxpr.eqns) == 1 assert jaxpr.eqns[0].primitive == ClassicalShadowMP._wires_primitive - assert jaxpr.eqns[0].params == {"seed": 95} + assert jaxpr.eqns[0].params == {"seed": seed} assert len(jaxpr.eqns[0].invars) == 3 mp = jaxpr.eqns[0].outvars[0].aval assert isinstance(mp, AbstractMeasurement) diff --git a/tests/conftest.py b/tests/conftest.py index d885f74338f..82e1f843169 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -43,12 +43,6 @@ class DummyDevice(DefaultGaussian): _operation_map["Kerr"] = lambda *x, **y: np.identity(2) -@pytest.fixture(autouse=True) -def set_numpy_seed(): - np.random.seed(9872653) - yield - - @pytest.fixture(scope="session") def tol(): """Numerical tolerance for equality tests.""" @@ -106,12 +100,6 @@ def qutrit_device_3_wires(request): ####################################################################### -@pytest.fixture(scope="module", params=[1, 2, 3]) -def seed(request): - """Different seeds.""" - return request.param - - @pytest.fixture(scope="function") def mock_device(monkeypatch): """A mock instance of the abstract Device class""" @@ -190,6 +178,44 @@ def legacy_opmath_only(): pytest.skip("This test exclusively tests legacy opmath") +####################################################################### + + +@pytest.fixture(autouse=True) +def restore_global_seed(): + original_state = np.random.get_state() + yield + np.random.set_state(original_state) + + +@pytest.fixture +def seed(request): + """An integer random number generator seed + + This fixture overrides the ``seed`` fixture provided by pytest-rng, adding the flexibility + of locally getting a new seed for a test case by applying the ``local_salt`` marker. This is + useful when the seed from pytest-rng happens to be a bad seed that causes your test to fail. + + .. code_block:: python + + @pytest.mark.local_salt(42) + def test_something(seed): + ... + + The value passed to ``local_salt`` needs to be an integer. + + """ + + fixture_manager = request._fixturemanager # pylint:disable=protected-access + fixture_defs = fixture_manager.getfixturedefs("seed", request.node) + original_fixture_def = fixture_defs[0] # the original seed fixture provided by pytest-rng + original_seed = original_fixture_def.func(request) + marker = request.node.get_closest_marker("local_salt") + if marker and marker.args: + return original_seed + marker.args[0] + return original_seed + + ####################################################################### try: diff --git a/tests/devices/default_qubit/test_default_qubit.py b/tests/devices/default_qubit/test_default_qubit.py index ff17a6600d7..0ac9bc2cffd 100644 --- a/tests/devices/default_qubit/test_default_qubit.py +++ b/tests/devices/default_qubit/test_default_qubit.py @@ -598,12 +598,12 @@ def test_batch_tapes(self, max_workers): assert results[1].shape == (50,) @pytest.mark.parametrize("max_workers", max_workers_list) - def test_counts_wires(self, max_workers): + def test_counts_wires(self, max_workers, seed): """Test that a Counts measurement with wires works as expected""" x = np.array(np.pi / 2) qs = qml.tape.QuantumScript([qml.RY(x, wires=0)], [qml.counts(wires=[0, 1])], shots=10000) - dev = DefaultQubit(seed=123, max_workers=max_workers) + dev = DefaultQubit(seed=seed, max_workers=max_workers) result = dev.execute(qs) assert isinstance(result, dict) @@ -611,11 +611,11 @@ def test_counts_wires(self, max_workers): # check that the count values match the expected values = list(result.values()) - assert np.allclose(values[0] / (values[0] + values[1]), 0.5, atol=0.01) + assert np.allclose(values[0] / (values[0] + values[1]), 0.5, atol=0.02) @pytest.mark.parametrize("max_workers", max_workers_list) @pytest.mark.parametrize("all_outcomes", [False, True]) - def test_counts_obs(self, all_outcomes, max_workers): + def test_counts_obs(self, all_outcomes, max_workers, seed): """Test that a Counts measurement with an observable works as expected""" x = np.array(np.pi / 2) qs = qml.tape.QuantumScript( @@ -624,7 +624,7 @@ def test_counts_obs(self, all_outcomes, max_workers): shots=10000, ) - dev = DefaultQubit(seed=123, max_workers=max_workers) + dev = DefaultQubit(seed=seed, max_workers=max_workers) result = dev.execute(qs) assert isinstance(result, dict) @@ -632,7 +632,7 @@ def test_counts_obs(self, all_outcomes, max_workers): # check that the count values match the expected values = list(result.values()) - assert np.allclose(values[0] / (values[0] + values[1]), 0.5, atol=0.01) + assert np.allclose(values[0] / (values[0] + values[1]), 0.5, rtol=0.05) class TestExecutingBatches: @@ -1499,13 +1499,13 @@ class TestHamiltonianSamples: This is a copy of the tests in test_sampling.py, but using the device instead""" @pytest.mark.parametrize("max_workers", max_workers_list) - def test_hamiltonian_expval(self, max_workers): + def test_hamiltonian_expval(self, max_workers, seed): """Test that sampling works well for Hamiltonian observables""" x, y = np.array(0.67), np.array(0.95) ops = [qml.RY(x, wires=0), qml.RZ(y, wires=0)] meas = [qml.expval(qml.Hamiltonian([0.8, 0.5], [qml.PauliZ(0), qml.PauliX(0)]))] - dev = DefaultQubit(seed=100, max_workers=max_workers) + dev = DefaultQubit(seed=seed, max_workers=max_workers) qs = qml.tape.QuantumScript(ops, meas, shots=10000) res = dev.execute(qs) @@ -1513,13 +1513,13 @@ def test_hamiltonian_expval(self, max_workers): assert np.allclose(res, expected, atol=0.01) @pytest.mark.parametrize("max_workers", max_workers_list) - def test_sum_expval(self, max_workers): + def test_sum_expval(self, max_workers, seed): """Test that sampling works well for Sum observables""" x, y = np.array(0.67), np.array(0.95) ops = [qml.RY(x, wires=0), qml.RZ(y, wires=0)] meas = [qml.expval(qml.s_prod(0.8, qml.PauliZ(0)) + qml.s_prod(0.5, qml.PauliX(0)))] - dev = DefaultQubit(seed=100, max_workers=max_workers) + dev = DefaultQubit(seed=seed, max_workers=max_workers) qs = qml.tape.QuantumScript(ops, meas, shots=10000) res = dev.execute(qs) @@ -1527,7 +1527,7 @@ def test_sum_expval(self, max_workers): assert np.allclose(res, expected, atol=0.01) @pytest.mark.parametrize("max_workers", max_workers_list) - def test_multi_wires(self, max_workers): + def test_multi_wires(self, max_workers, seed): """Test that sampling works for Sums with large numbers of wires""" n_wires = 10 scale = 0.05 @@ -1539,8 +1539,8 @@ def test_multi_wires(self, max_workers): t2 = 6.2 * qml.prod(*(qml.PauliY(i) for i in range(n_wires))) H = t1 + t2 - dev = DefaultQubit(seed=100, max_workers=max_workers) - qs = qml.tape.QuantumScript(ops, [qml.expval(H)], shots=100000) + dev = DefaultQubit(seed=seed, max_workers=max_workers) + qs = qml.tape.QuantumScript(ops, [qml.expval(H)], shots=30000) res = dev.execute(qs) phase = offset + scale * np.array(range(n_wires)) @@ -1548,10 +1548,10 @@ def test_multi_wires(self, max_workers): sines = qml.math.sin(phase) expected = 2.5 * qml.math.prod(cosines) + 6.2 * qml.math.prod(sines) - assert np.allclose(res, expected, atol=0.05) + assert np.allclose(res, expected, rtol=0.05) @pytest.mark.parametrize("max_workers", max_workers_list) - def test_complex_hamiltonian(self, max_workers): + def test_complex_hamiltonian(self, max_workers, seed): """Test that sampling works for complex Hamiltonians""" scale = 0.05 offset = 0.4 @@ -1608,8 +1608,8 @@ def test_complex_hamiltonian(self, max_workers): ], ) - dev = DefaultQubit(seed=100, max_workers=max_workers) - qs = qml.tape.QuantumScript(ops, [qml.expval(H)], shots=100000) + dev = DefaultQubit(seed=seed, max_workers=max_workers) + qs = qml.tape.QuantumScript(ops, [qml.expval(H)], shots=50000) res = dev.execute(qs) qs_exp = qml.tape.QuantumScript(ops, [qml.expval(H)]) @@ -1641,17 +1641,18 @@ def test_shape_and_dtype(self, max_workers, n_qubits): assert np.all(np.logical_or(np.logical_or(res[1] == 0, res[1] == 1), res[1] == 2)) @pytest.mark.parametrize("max_workers", max_workers_list) - def test_expval(self, max_workers): + def test_expval(self, max_workers, seed): """Test that shadow expval measurements work as expected""" - dev = DefaultQubit(seed=100, max_workers=max_workers) + + dev = DefaultQubit(seed=seed, max_workers=max_workers) ops = [qml.Hadamard(0), qml.Hadamard(1)] - meas = [qml.shadow_expval(qml.PauliX(0) @ qml.PauliX(1), seed=200)] - qs = qml.tape.QuantumScript(ops, meas, shots=1000) + meas = [qml.shadow_expval(qml.PauliX(0) @ qml.PauliX(1), seed=seed)] + qs = qml.tape.QuantumScript(ops, meas, shots=10000) res = dev.execute(qs) assert res.shape == () - assert np.allclose(res, 1.0, atol=0.05) + assert np.allclose(res, 1.0, rtol=0.05) @pytest.mark.parametrize("n_qubits", [1, 2, 3]) @pytest.mark.parametrize("max_workers", max_workers_list) @@ -1681,12 +1682,12 @@ def test_multiple_shadow_measurements(self, n_qubits, max_workers): assert not np.all(res[0] == res[1]) @pytest.mark.parametrize("max_workers", max_workers_list) - def test_reconstruct_bell_state(self, max_workers): + def test_reconstruct_bell_state(self, max_workers, seed): """Test that a bell state can be faithfully reconstructed""" - dev = DefaultQubit(seed=100, max_workers=max_workers) + dev = DefaultQubit(seed=seed, max_workers=max_workers) ops = [qml.Hadamard(0), qml.CNOT([0, 1])] - meas = [qml.classical_shadow(wires=[0, 1], seed=200)] + meas = [qml.classical_shadow(wires=[0, 1], seed=seed)] qs = qml.tape.QuantumScript(ops, meas, shots=10000) # should prepare the bell state @@ -1704,7 +1705,7 @@ def test_reconstruct_bell_state(self, max_workers): # alternative computation ops = [qml.Hadamard(0), qml.CNOT([0, 1])] - meas = [qml.classical_shadow(wires=[0], seed=200)] + meas = [qml.classical_shadow(wires=[0], seed=seed)] qs = qml.tape.QuantumScript(ops, meas, shots=10000) bits, recipes = dev.execute(qs) @@ -1837,13 +1838,13 @@ def circ_expected(): ) @pytest.mark.parametrize("param", np.linspace(np.pi / 4, 3 * np.pi / 4, 3)) @pytest.mark.parametrize("shots", [50000, (50000, 50000)]) - def test_postselection_valid_finite_shots(self, param, mp, shots, interface, use_jit): + def test_postselection_valid_finite_shots(self, param, mp, shots, interface, use_jit, seed): """Test that the results of a circuit with postselection is expected with finite shots.""" if use_jit and (interface != "jax" or isinstance(shots, tuple)): pytest.skip("Cannot JIT in non-JAX interfaces, or with shot vectors.") - dev = qml.device("default.qubit", seed=1971) + dev = qml.device("default.qubit", seed=seed) param = qml.math.asarray(param, like=interface) @qml.defer_measurements diff --git a/tests/devices/default_qubit/test_default_qubit_native_mcm.py b/tests/devices/default_qubit/test_default_qubit_native_mcm.py index 80467c66d3b..eed50f29e0f 100644 --- a/tests/devices/default_qubit/test_default_qubit_native_mcm.py +++ b/tests/devices/default_qubit/test_default_qubit_native_mcm.py @@ -27,10 +27,7 @@ pytestmark = pytest.mark.slow -def get_device(**kwargs): - kwargs.setdefault("shots", None) - kwargs.setdefault("seed", 8237945) - return qml.device("default.qubit", **kwargs) +# pylint: disable=too-many-arguments def test_combine_measurements_core(): @@ -57,8 +54,9 @@ def test_measurement_with_no_shots(): @pytest.mark.parametrize("mcm_method", ["one-shot", "tree-traversal"]) def test_all_invalid_shots_circuit(obs, mcm_method): """Test that circuits in which all shots mismatch with post-selection conditions return the same answer as ``defer_measurements``.""" - dev = get_device() - dev_shots = get_device(shots=10) + + dev = qml.device("default.qubit") + dev_shots = qml.device("default.qubit", shots=10) def circuit_op(): m = qml.measure(0, postselect=1) @@ -85,7 +83,8 @@ def circuit_op(): @pytest.mark.parametrize("mcm_method", ["one-shot", "tree-traversal"]) def test_unsupported_measurement(mcm_method): """Test that circuits with unsupported measurements raise the correct error.""" - dev = get_device(shots=1000) + + dev = qml.device("default.qubit", shots=1000) params = np.pi / 4 * np.ones(2) @qml.qnode(dev, mcm_method=mcm_method) @@ -132,7 +131,7 @@ def obs_tape(x, y, z, reset=False, postselect=None): ) @pytest.mark.parametrize("postselect", [None, 0, 1]) @pytest.mark.parametrize("reset", [False, True]) -def test_multiple_measurements_and_reset(mcm_method, shots, params, postselect, reset): +def test_multiple_measurements_and_reset(mcm_method, shots, params, postselect, reset, seed): """Tests that DefaultQubit handles a circuit with a single mid-circuit measurement with reset and a conditional gate. Multiple measurements of the mid-circuit measurement value are performed. This function also tests `reset` parametrizing over the parameter.""" @@ -150,7 +149,7 @@ def test_multiple_measurements_and_reset(mcm_method, shots, params, postselect, if batch_size is not None and shots is not None and postselect is not None: pytest.skip("Postselection with samples doesn't work with broadcasting") - dev = get_device(shots=shots) + dev = qml.device("default.qubit", shots=shots, seed=seed) obs = qml.PauliY(1) state = qml.math.zeros((4,)) state[0] = 1.0 @@ -212,7 +211,7 @@ def func(x, y, z): ], ) @pytest.mark.parametrize("measure_f", [qml.counts, qml.expval, qml.probs, qml.sample, qml.var]) -def test_composite_mcms(mcm_method, shots, mcm_name, mcm_func, measure_f): +def test_composite_mcms(mcm_method, shots, mcm_name, mcm_func, measure_f, seed): """Tests that DefaultQubit handles a circuit with a composite mid-circuit measurement and a conditional gate. A single measurement of a composite mid-circuit measurement is performed at the end.""" @@ -233,7 +232,7 @@ def test_composite_mcms(mcm_method, shots, mcm_name, mcm_func, measure_f): "Cannot use qml.probs() when measuring multiple mid-circuit measurements collected using arithmetic operators." ) - dev = get_device(shots=shots) + dev = qml.device("default.qubit", shots=shots, seed=seed) param = qml.numpy.array([np.pi / 3, np.pi / 6]) def func(x, y): @@ -260,12 +259,12 @@ def func(x, y): @pytest.mark.parametrize("postselect", [None, 0, 1]) @pytest.mark.parametrize("reset", [False, True]) @pytest.mark.parametrize("measure_f", [qml.expval]) -def composite_mcm_gradient_measure_obs(shots, postselect, reset, measure_f): +def composite_mcm_gradient_measure_obs(shots, postselect, reset, measure_f, seed): """Tests that DefaultQubit can differentiate a circuit with a composite mid-circuit measurement and a conditional gate. A single measurement of a common observable is performed at the end.""" - dev = get_device(shots=shots) + dev = qml.device("default.qubit", shots=shots, seed=seed) param = qml.numpy.array([np.pi / 3, np.pi / 6]) obs = qml.PauliZ(0) @ qml.PauliZ(1) @@ -294,7 +293,7 @@ def func(x, y): @pytest.mark.parametrize("mcm_method", ["one-shot", "tree-traversal"]) -def test_sample_with_broadcasting_and_postselection_error(mcm_method): +def test_sample_with_broadcasting_and_postselection_error(mcm_method, seed): """Test that an error is raised if returning qml.sample if postselecting with broadcasting""" tape = qml.tape.QuantumScript( [qml.RX([0.1, 0.2], 0), MidMeasureMP(0, postselect=1)], [qml.sample(wires=0)], shots=10 @@ -302,7 +301,7 @@ def test_sample_with_broadcasting_and_postselection_error(mcm_method): with pytest.raises(ValueError, match="Returning qml.sample is not supported when"): qml.transforms.dynamic_one_shot(tape) - dev = get_device(shots=10) + dev = qml.device("default.qubit", shots=10, seed=seed) @qml.qnode(dev, mcm_method=mcm_method) def circuit(x): @@ -317,11 +316,11 @@ def circuit(x): @pytest.mark.all_interfaces @pytest.mark.parametrize("interface", ["torch", "tensorflow", "jax", "autograd"]) @pytest.mark.parametrize("mcm_method", ["one-shot", "tree-traversal"]) -def test_finite_diff_in_transform_program(interface, mcm_method): +def test_finite_diff_in_transform_program(interface, mcm_method, seed): """Test that finite diff is in the transform program of a qnode containing mid-circuit measurements""" - dev = get_device(shots=10) + dev = qml.device("default.qubit", shots=10, seed=seed) @qml.qnode(dev, mcm_method=mcm_method, diff_method="finite-diff") def circuit(x): @@ -348,13 +347,13 @@ class TestJaxIntegration: @pytest.mark.parametrize("mcm_method", ["one-shot", "tree-traversal"]) @pytest.mark.parametrize("shots", [100, [100, 101], [100, 100, 101]]) @pytest.mark.parametrize("postselect", [None, 0, 1]) - def test_sample_with_prng_key(self, mcm_method, shots, postselect): + def test_sample_with_prng_key(self, mcm_method, shots, postselect, seed): """Test that setting a PRNGKey gives the expected behaviour. With separate calls to DefaultQubit.execute, the same results are expected when using a PRNGKey""" # pylint: disable=import-outside-toplevel from jax.random import PRNGKey - dev = get_device(shots=shots, seed=PRNGKey(678)) + dev = qml.device("default.qubit", shots=shots, seed=PRNGKey(seed)) params = [np.pi / 4, np.pi / 3] obs = qml.PauliZ(0) @ qml.PauliZ(1) @@ -389,14 +388,14 @@ def func(x, y): @pytest.mark.parametrize("diff_method", [None, "best"]) @pytest.mark.parametrize("postselect", [None, 1]) @pytest.mark.parametrize("reset", [False, True]) - def test_jax_jit(self, diff_method, postselect, reset): + def test_jax_jit(self, diff_method, postselect, reset, seed): """Tests that DefaultQubit handles a circuit with a single mid-circuit measurement and a conditional gate. A single measurement of a common observable is performed at the end.""" import jax shots = 10 - dev = get_device(shots=shots, seed=jax.random.PRNGKey(678)) + dev = qml.device("default.qubit", shots=shots, seed=jax.random.PRNGKey(seed)) params = [np.pi / 2.5, np.pi / 3, -np.pi / 3.5] obs = qml.PauliY(0) diff --git a/tests/devices/qubit/test_apply_operation.py b/tests/devices/qubit/test_apply_operation.py index ec4a31804bb..08235091ee6 100644 --- a/tests/devices/qubit/test_apply_operation.py +++ b/tests/devices/qubit/test_apply_operation.py @@ -1329,8 +1329,8 @@ def test_conditional(self, wires, unitary, batched, ml_framework): qml.math.squeeze(initial_state), qml.math.reshape(new_state, (n_states, 4)) ) - @pytest.mark.parametrize("rng_seed, m_res", ((12, (0, 0)), (42, (1, 1)))) - def test_mid_measure(self, rng_seed, m_res): + @pytest.mark.parametrize("m_res", [(0, 0), (1, 1)]) + def test_mid_measure(self, m_res, monkeypatch): """Test the application of a MidMeasureMP on an arbitrary state to give a basis state.""" initial_state = np.array( @@ -1344,14 +1344,15 @@ def test_mid_measure(self, rng_seed, m_res): mid_state[m_res[0]] = initial_state[m_res[0]] / np.linalg.norm(initial_state[m_res[0]]) end_state[m_res] = mid_state[m_res] / np.abs(mid_state[m_res]) - rng = np.random.default_rng(rng_seed) m0, m1 = qml.measure(0).measurements[0], qml.measure(1).measurements[0] mid_meas = {} - res_state = apply_operation(m0, initial_state, mid_measurements=mid_meas, rng=rng) + monkeypatch.setattr(np.random, "binomial", lambda *args: m_res[0]) + + res_state = apply_operation(m0, initial_state, mid_measurements=mid_meas) assert qml.math.allclose(mid_state, res_state) - res_state = apply_operation(m1, res_state, mid_measurements=mid_meas, rng=rng) + res_state = apply_operation(m1, res_state, mid_measurements=mid_meas) assert qml.math.allclose(end_state, res_state) assert mid_meas == {m0: m_res[0], m1: m_res[1]} diff --git a/tests/devices/qubit/test_sampling.py b/tests/devices/qubit/test_sampling.py index f0ebc8aa3ac..1675508b98b 100644 --- a/tests/devices/qubit/test_sampling.py +++ b/tests/devices/qubit/test_sampling.py @@ -32,9 +32,9 @@ def fixture_init_state(): """Generates a random initial state""" - def _init_state(n): + def _init_state(n, seed): """random initial state""" - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) state = rng.random([1 << n]) + rng.random([1 << n]) * 1j state /= np.linalg.norm(state) return state.reshape((2,) * n) @@ -98,13 +98,13 @@ def test_prng_key_as_seed_uses_sample_state_jax(self, mocker): spy.assert_called_once() @pytest.mark.jax - def test_sample_state_jax(self): + def test_sample_state_jax(self, seed): """Tests that the returned samples are as expected when explicitly calling sample_state.""" import jax state = qml.math.array(two_qubit_state, like="jax") - samples = sample_state(state, 10, prng_key=jax.random.PRNGKey(84)) + samples = sample_state(state, 10, prng_key=jax.random.PRNGKey(seed)) assert samples.shape == (10, 2) assert samples.dtype == np.int64 @@ -141,11 +141,11 @@ def test_sample_state_custom_rng(self): expected = [[0, 1], [0, 1], [1, 0], [1, 0]] assert qml.math.allequal(samples, expected) - def test_approximate_probs_from_samples(self, init_state): + def test_approximate_probs_from_samples(self, init_state, seed): """Tests that the generated samples are approximately as expected.""" n = 4 shots = 20000 - state = init_state(n) + state = init_state(n, seed) flat_state = state.flatten() expected_probs = np.real(flat_state) ** 2 + np.imag(flat_state) ** 2 @@ -310,47 +310,47 @@ def test_var_measure_single_wire(self): assert result0 == 0 assert result1 == 0 - def test_approximate_sample_measure(self): + def test_approximate_sample_measure(self, seed): """Test that a sample measurement returns approximately the correct distribution""" state = qml.math.array(two_qubit_state) shots = qml.measurements.Shots(10000) mp = qml.sample(wires=range(2)) - result = measure_with_samples([mp], state, shots=shots, rng=123)[0] + result = measure_with_samples([mp], state, shots=shots, rng=seed)[0] one_prob = np.count_nonzero(result[:, 0]) / result.shape[0] assert np.allclose(one_prob, 0.5, atol=0.05) - def test_approximate_prob_measure(self): + def test_approximate_prob_measure(self, seed): """Test that a probability measurement works as expected""" state = qml.math.array(two_qubit_state) shots = qml.measurements.Shots(10000) mp = qml.probs(wires=range(2)) - result = measure_with_samples([mp], state, shots=shots, rng=123)[0] + result = measure_with_samples([mp], state, shots=shots, rng=seed)[0] assert np.allclose(result[1], 0.5, atol=0.05) assert np.allclose(result[2], 0.5, atol=0.05) assert result[1] + result[2] == 1 - def test_approximate_expval_measure(self): + def test_approximate_expval_measure(self, seed): """Test that an expval measurement works as expected""" state = qml.math.array(two_qubit_state) shots = qml.measurements.Shots(10000) mp = qml.expval(qml.prod(qml.PauliX(0), qml.PauliX(1))) - result = measure_with_samples([mp], state, shots=shots, rng=123)[0] + result = measure_with_samples([mp], state, shots=shots, rng=seed)[0] assert result != 0 assert np.allclose(result, 0, atol=0.05) - def test_approximate_var_measure(self): + def test_approximate_var_measure(self, seed): """Test that a variance measurement works as expected""" state = qml.math.array(two_qubit_state) shots = qml.measurements.Shots(10000) mp = qml.var(qml.prod(qml.PauliX(0), qml.PauliX(1))) - result = measure_with_samples([mp], state, shots=shots, rng=123)[0] + result = measure_with_samples([mp], state, shots=shots, rng=seed)[0] assert result != 1 assert np.allclose(result, 1, atol=0.05) @@ -782,10 +782,10 @@ def test_sample_batched_state_renorm_error(self, interface): class TestBroadcasting: """Test that measurements work when the state has a batch dim""" - def test_sample_measure(self): + def test_sample_measure(self, seed): """Test that broadcasting works for qml.sample and single shots""" - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) shots = qml.measurements.Shots(100) state = [ @@ -821,10 +821,9 @@ def test_sample_measure(self): (qml.var(qml.PauliZ(1)), np.array([0, 0, 1])), ], ) - def test_nonsample_measure(self, measurement, expected): + def test_nonsample_measure(self, measurement, expected, seed): """Test that broadcasting works for the other sample measurements and single shots""" - rng = np.random.default_rng(123) shots = qml.measurements.Shots(10000) state = [ @@ -834,8 +833,8 @@ def test_nonsample_measure(self, measurement, expected): ] state = np.stack(state) - res = measure_with_samples([measurement], state, shots, is_state_batched=True, rng=rng) - assert np.allclose(res, expected, atol=0.01) + res = measure_with_samples([measurement], state, shots, is_state_batched=True, rng=seed) + assert np.allclose(res, expected, atol=0.03) @pytest.mark.parametrize( "shots", @@ -847,10 +846,10 @@ def test_nonsample_measure(self, measurement, expected): (200, (100, 2)), ], ) - def test_sample_measure_shot_vector(self, shots): + def test_sample_measure_shot_vector(self, shots, seed): """Test that broadcasting works for qml.sample and shot vectors""" - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) shots = qml.measurements.Shots(shots) state = [ @@ -905,10 +904,10 @@ def test_sample_measure_shot_vector(self, shots): (qml.var(qml.PauliZ(1)), np.array([0, 0, 1])), ], ) - def test_nonsample_measure_shot_vector(self, shots, measurement, expected): + def test_nonsample_measure_shot_vector(self, shots, measurement, expected, seed): """Test that broadcasting works for the other sample measurements and shot vectors""" - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) shots = qml.measurements.Shots(shots) state = [ @@ -937,7 +936,7 @@ class TestBroadcastingPRNG: """Test that measurements work and use sample_state when the state has a batch dim and a PRNG key is provided""" - def test_sample_measure(self, mocker): + def test_sample_measure(self, mocker, seed): """Test that broadcasting works for qml.sample and single shots""" import jax @@ -945,7 +944,7 @@ def test_sample_measure(self, mocker): spy = mocker.spy(qml.devices.qubit.sampling, "_sample_probs_jax") - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) shots = qml.measurements.Shots(100) state = [ @@ -962,7 +961,7 @@ def test_sample_measure(self, mocker): shots, is_state_batched=True, rng=rng, - prng_key=jax.random.PRNGKey(184), + prng_key=jax.random.PRNGKey(seed), )[0] spy.assert_called() @@ -993,13 +992,13 @@ def test_sample_measure(self, mocker): (qml.var(qml.PauliZ(1)), np.array([0, 0, 1])), ], ) - def test_nonsample_measure(self, mocker, measurement, expected): + def test_nonsample_measure(self, mocker, measurement, expected, seed): """Test that broadcasting works for the other sample measurements and single shots""" import jax spy = mocker.spy(qml.devices.qubit.sampling, "_sample_probs_jax") - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) shots = qml.measurements.Shots(10000) state = [ @@ -1015,7 +1014,7 @@ def test_nonsample_measure(self, mocker, measurement, expected): shots, is_state_batched=True, rng=rng, - prng_key=jax.random.PRNGKey(184), + prng_key=jax.random.PRNGKey(seed), ) spy.assert_called() @@ -1031,14 +1030,14 @@ def test_nonsample_measure(self, mocker, measurement, expected): (200, (100, 2)), ], ) - def test_sample_measure_shot_vector(self, mocker, shots): + def test_sample_measure_shot_vector(self, mocker, shots, seed): """Test that broadcasting works for qml.sample and shot vectors""" import jax spy = mocker.spy(qml.devices.qubit.sampling, "_sample_probs_jax") - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) shots = qml.measurements.Shots(shots) state = [ @@ -1055,7 +1054,7 @@ def test_sample_measure_shot_vector(self, mocker, shots): shots, is_state_batched=True, rng=rng, - prng_key=jax.random.PRNGKey(184), + prng_key=jax.random.PRNGKey(seed), ) spy.assert_called() @@ -1107,14 +1106,14 @@ def test_sample_measure_shot_vector(self, mocker, shots): (qml.var(qml.PauliZ(1)), np.array([0, 0, 1])), ], ) - def test_nonsample_measure_shot_vector(self, mocker, shots, measurement, expected): + def test_nonsample_measure_shot_vector(self, mocker, shots, measurement, expected, seed): """Test that broadcasting works for the other sample measurements and shot vectors""" import jax spy = mocker.spy(qml.devices.qubit.sampling, "_sample_probs_jax") - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) shots = qml.measurements.Shots(shots) state = [ @@ -1130,7 +1129,7 @@ def test_nonsample_measure_shot_vector(self, mocker, shots, measurement, expecte shots, is_state_batched=True, rng=rng, - prng_key=jax.random.PRNGKey(0), + prng_key=jax.random.PRNGKey(seed), ) spy.assert_called() @@ -1152,26 +1151,26 @@ class TestHamiltonianSamples: Hamiltonian and Sum observables""" @pytest.mark.usefixtures("use_legacy_and_new_opmath") - def test_hamiltonian_expval(self): + def test_hamiltonian_expval(self, seed): """Test that sampling works well for Hamiltonian observables""" x, y = np.array(0.67), np.array(0.95) ops = [qml.RY(x, wires=0), qml.RZ(y, wires=0)] meas = [qml.expval(qml.Hamiltonian([0.8, 0.5], [qml.PauliZ(0), qml.PauliX(0)]))] qs = qml.tape.QuantumScript(ops, meas, shots=10000) - res = simulate(qs, rng=200) + res = simulate(qs, rng=seed) expected = 0.8 * np.cos(x) + 0.5 * np.real(np.exp(y * 1j)) * np.sin(x) - assert np.allclose(res, expected, atol=0.01) + assert np.allclose(res, expected, atol=0.02) - def test_hamiltonian_expval_shot_vector(self): + def test_hamiltonian_expval_shot_vector(self, seed): """Test that sampling works well for Hamiltonian observables with a shot vector""" x, y = np.array(0.67), np.array(0.95) ops = [qml.RY(x, wires=0), qml.RZ(y, wires=0)] meas = [qml.expval(qml.Hamiltonian([0.8, 0.5], [qml.PauliZ(0), qml.PauliX(0)]))] qs = qml.tape.QuantumScript(ops, meas, shots=(10000, 10000)) - res = simulate(qs, rng=200) + res = simulate(qs, rng=seed) expected = 0.8 * np.cos(x) + 0.5 * np.real(np.exp(y * 1j)) * np.sin(x) @@ -1180,7 +1179,7 @@ def test_hamiltonian_expval_shot_vector(self): assert np.allclose(res[0], expected, atol=0.01) assert np.allclose(res[1], expected, atol=0.01) - def test_sum_expval(self): + def test_sum_expval(self, seed): """Test that sampling works well for Sum observables""" x, y = np.array(0.67), np.array(0.95) @@ -1188,19 +1187,19 @@ def test_sum_expval(self): meas = [qml.expval(qml.s_prod(0.8, qml.PauliZ(0)) + qml.s_prod(0.5, qml.PauliX(0)))] qs = qml.tape.QuantumScript(ops, meas, shots=10000) - res = simulate(qs, rng=200) + res = simulate(qs, rng=seed) expected = 0.8 * np.cos(x) + 0.5 * np.real(np.exp(y * 1j)) * np.sin(x) assert np.allclose(res, expected, atol=0.01) - def test_sum_expval_shot_vector(self): + def test_sum_expval_shot_vector(self, seed): """Test that sampling works well for Sum observables with a shot vector.""" x, y = np.array(0.67), np.array(0.95) ops = [qml.RY(x, wires=0), qml.RZ(y, wires=0)] meas = [qml.expval(qml.s_prod(0.8, qml.PauliZ(0)) + qml.s_prod(0.5, qml.PauliX(0)))] qs = qml.tape.QuantumScript(ops, meas, shots=(10000, 10000)) - res = simulate(qs, rng=200) + res = simulate(qs, rng=seed) expected = 0.8 * np.cos(x) + 0.5 * np.real(np.exp(y * 1j)) * np.sin(x) @@ -1209,7 +1208,7 @@ def test_sum_expval_shot_vector(self): assert np.allclose(res[0], expected, atol=0.01) assert np.allclose(res[1], expected, atol=0.01) - def test_prod_expval(self): + def test_prod_expval(self, seed): """Tests that sampling works for Prod observables""" x, y = np.array(0.67), np.array(0.95) @@ -1218,11 +1217,11 @@ def test_prod_expval(self): tape = qml.tape.QuantumScript( ops, measurements=[qml.expval(qml.PauliX(0)), qml.expval(H)], shots=10000 ) - res = simulate(tape, rng=200) + res = simulate(tape, rng=seed) expected = [np.sin(y), -np.sin(y) * np.sin(x)] assert np.allclose(res, expected, atol=0.05) - def test_sprod_expval(self): + def test_sprod_expval(self, seed): """Tests that sampling works for SProd observables""" y = np.array(0.95) @@ -1231,11 +1230,11 @@ def test_sprod_expval(self): tape = qml.tape.QuantumScript( ops, measurements=[qml.expval(qml.PauliX(0)), qml.expval(H)], shots=10000 ) - res = simulate(tape, rng=200) + res = simulate(tape, rng=seed) expected = [np.sin(y), 1.5 * np.sin(y)] assert np.allclose(res, expected, atol=0.05) - def test_multi_wires(self): + def test_multi_wires(self, seed): """Test that sampling works for Sums with large numbers of wires""" n_wires = 10 scale = 0.05 @@ -1248,7 +1247,7 @@ def test_multi_wires(self): H = t1 + t2 qs = qml.tape.QuantumScript(ops, [qml.expval(H)], shots=100000) - res = simulate(qs, rng=100) + res = simulate(qs, rng=seed) phase = offset + scale * np.array(range(n_wires)) cosines = qml.math.cos(phase) @@ -1257,7 +1256,7 @@ def test_multi_wires(self): assert np.allclose(res, expected, atol=0.05) - def test_complex_hamiltonian(self): + def test_complex_hamiltonian(self, seed): """Test that sampling works for complex Hamiltonians""" scale = 0.05 offset = 0.4 @@ -1315,7 +1314,7 @@ def test_complex_hamiltonian(self): ) qs = qml.tape.QuantumScript(ops, [qml.expval(H)], shots=100000) - res = simulate(qs, rng=100) + res = simulate(qs, rng=seed) qs_exp = qml.tape.QuantumScript(ops, [qml.expval(H)]) expected = simulate(qs_exp) @@ -1324,43 +1323,39 @@ def test_complex_hamiltonian(self): class TestSampleProbs: - # pylint: disable=attribute-defined-outside-init - @pytest.fixture(autouse=True) - def setup(self): - self.rng = np.random.default_rng(42) # Fixed seed for reproducibility - def test_basic_sampling(self): + def test_basic_sampling(self, seed): """One Qubit, two outcomes""" probs = np.array([0.3, 0.7]) - samples = sample_probs(probs, shots=1000, num_wires=1, is_state_batched=False, rng=self.rng) + samples = sample_probs(probs, shots=1000, num_wires=1, is_state_batched=False, rng=seed) assert samples.shape == (1000, 1) # Check if the distribution is roughly correct (allowing for some variance) zeros = np.sum(samples == 0) assert 250 <= zeros <= 350 # Approx 30% of 1000, with some leeway - def test_multi_qubit_sampling(self): + def test_multi_qubit_sampling(self, seed): """Two Qubit, four outcomes""" probs = np.array([0.1, 0.2, 0.3, 0.4]) - samples = sample_probs(probs, shots=1000, num_wires=2, is_state_batched=False, rng=self.rng) + samples = sample_probs(probs, shots=1000, num_wires=2, is_state_batched=False, rng=seed) assert samples.shape == (1000, 2) # Check if all possible states are present unique_samples = set(map(tuple, samples)) assert len(unique_samples) == 4 - def test_batched_sampling(self): + def test_batched_sampling(self, seed): """A batch of two circuits, each with two outcomes""" probs = np.array([[0.5, 0.5], [0.3, 0.7]]) - samples = sample_probs(probs, shots=1000, num_wires=1, is_state_batched=True, rng=self.rng) + samples = sample_probs(probs, shots=1000, num_wires=1, is_state_batched=True, rng=seed) assert samples.shape == (2, 1000, 1) - def test_cutoff_edge_case_failure(self): + def test_cutoff_edge_case_failure(self, seed): """Test sampling with probabilities just outside the cutoff.""" cutoff = 1e-7 # Assuming this is the cutoff used in sample_probs probs = np.array([0.5, 0.5 - 2 * cutoff]) with pytest.raises(ValueError, match=r"(?i)probabilities do not sum to 1"): - sample_probs(probs, shots=1000, num_wires=1, is_state_batched=False, rng=self.rng) + sample_probs(probs, shots=1000, num_wires=1, is_state_batched=False, rng=seed) - def test_batched_cutoff_edge_case_failure(self): + def test_batched_cutoff_edge_case_failure(self, seed): """Test sampling with probabilities just outside the cutoff.""" cutoff = 1e-7 # Assuming this is the cutoff used in sample_probs probs = np.array( @@ -1370,4 +1365,4 @@ def test_batched_cutoff_edge_case_failure(self): ] ) with pytest.raises(ValueError, match=r"(?i)probabilities do not sum to 1"): - sample_probs(probs, shots=1000, num_wires=1, is_state_batched=True, rng=self.rng) + sample_probs(probs, shots=1000, num_wires=1, is_state_batched=True, rng=seed) diff --git a/tests/devices/qubit/test_simulate.py b/tests/devices/qubit/test_simulate.py index 5de0c6c14a5..c926a4d655c 100644 --- a/tests/devices/qubit/test_simulate.py +++ b/tests/devices/qubit/test_simulate.py @@ -18,7 +18,6 @@ import pytest import scipy as sp from dummy_debugger import Debugger -from flaky import flaky from stat_utils import fisher_exact_test import pennylane as qml @@ -304,7 +303,7 @@ def test_broadcasted_op_state(self): assert np.allclose(res[0], np.cos(x)) assert np.allclose(res[1], -np.cos(x)) - def test_broadcasted_prep_sample(self): + def test_broadcasted_prep_sample(self, seed): """Test that simulate works for sample measurements when the state prep has broadcasted parameters""" x = np.array(1.2) @@ -313,8 +312,8 @@ def test_broadcasted_prep_sample(self): measurements = [qml.expval(qml.PauliZ(i)) for i in range(2)] prep = [qml.StatePrep(np.eye(4), wires=[0, 1])] - qs = qml.tape.QuantumScript(prep + ops, measurements, shots=qml.measurements.Shots(10000)) - res = simulate(qs, rng=123) + qs = qml.tape.QuantumScript(prep + ops, measurements, shots=qml.measurements.Shots(5000)) + res = simulate(qs, rng=seed) assert isinstance(res, tuple) assert len(res) == 2 @@ -326,7 +325,7 @@ def test_broadcasted_prep_sample(self): ) state, is_state_batched = get_final_state(qs) - res = measure_final_state(qs, state, is_state_batched, rng=123) + res = measure_final_state(qs, state, is_state_batched, rng=seed) expected_state = np.array( [ [np.cos(x / 2), 0, 0, np.sin(x / 2)], @@ -347,7 +346,7 @@ def test_broadcasted_prep_sample(self): res[1], np.array([np.cos(x), -np.cos(x), -np.cos(x), np.cos(x)]), atol=0.05 ) - def test_broadcasted_op_sample(self): + def test_broadcasted_op_sample(self, seed): """Test that simulate works for sample measurements when an operation has broadcasted parameters""" x = np.array([0.8, 1.0, 1.2, 1.4]) @@ -355,8 +354,8 @@ def test_broadcasted_op_sample(self): ops = [qml.PauliX(wires=1), qml.RY(x, wires=0), qml.CNOT(wires=[0, 1])] measurements = [qml.expval(qml.PauliZ(i)) for i in range(2)] - qs = qml.tape.QuantumScript(ops, measurements, shots=qml.measurements.Shots(10000)) - res = simulate(qs, rng=123) + qs = qml.tape.QuantumScript(ops, measurements, shots=qml.measurements.Shots(5000)) + res = simulate(qs, rng=seed) assert isinstance(res, tuple) assert len(res) == 2 @@ -364,7 +363,7 @@ def test_broadcasted_op_sample(self): assert np.allclose(res[1], -np.cos(x), atol=0.05) state, is_state_batched = get_final_state(qs) - res = measure_final_state(qs, state, is_state_batched, rng=123) + res = measure_final_state(qs, state, is_state_batched, rng=seed) expected_state = np.zeros((4, 2, 2)) expected_state[:, 0, 1] = np.cos(x / 2) @@ -1295,7 +1294,7 @@ def test_basic_mid_meas_circuit_with_reset(self): @pytest.mark.parametrize( "meas_obj", [qml.Y(0), [1], [1, 0], "mcm", "composite_mcm", "mcm_list"] ) - def test_simple_dynamic_circuit(self, shots, measure_f, postselect, reset, meas_obj): + def test_simple_dynamic_circuit(self, shots, measure_f, postselect, reset, meas_obj, seed): """Tests that `simulate` can handles a simple dynamic circuit with the following measurements: * qml.counts with obs (comp basis or not), single wire, multiple wires (ordered/unordered), MCM, f(MCM), MCM list @@ -1361,7 +1360,7 @@ def test_simple_dynamic_circuit(self, shots, measure_f, postselect, reset, meas_ shots=shots, ) - rng = np.random.default_rng(1234) + rng = np.random.default_rng(seed) results0 = simulate(qscript, mcm_method="tree-traversal", rng=rng) deferred_tapes, deferred_func = qml.defer_measurements(qscript) @@ -1378,10 +1377,9 @@ def test_simple_dynamic_circuit(self, shots, measure_f, postselect, reset, meas_ mcm_utils.validate_measurements(measure_f, shots, results2, results0) @pytest.mark.parametrize("shots", [None, 5500, [5500, 5500]]) - @pytest.mark.parametrize("rng", [None, 42, np.array([37])]) @pytest.mark.parametrize("angles", [(0.123, 0.015), (0.543, 0.057)]) @pytest.mark.parametrize("measure_f", [qml.probs, qml.sample]) - def test_approx_dynamic_mid_meas_circuit(self, shots, rng, angles, measure_f): + def test_approx_dynamic_mid_meas_circuit(self, shots, angles, measure_f, seed): """Test execution of a dynamic circuit with an equivalent static one.""" if measure_f in (qml.sample,) and shots is None: @@ -1422,8 +1420,8 @@ def test_approx_dynamic_mid_meas_circuit(self, shots, rng, angles, measure_f): [measure_f(wires=[0, 1, 2, 3])], shots=shots, ) # approximate compiled circuit of the above - res1 = simulate_tree_mcm(qs_with_mid_meas, rng=rng) - res2 = simulate(qs_without_mid_meas, rng=rng) + res1 = simulate_tree_mcm(qs_with_mid_meas, rng=seed) + res2 = simulate(qs_without_mid_meas, rng=seed) if not isinstance(shots, list): res1, res2 = (res1,), (res2,) @@ -1447,7 +1445,7 @@ def test_approx_dynamic_mid_meas_circuit(self, shots, rng, angles, measure_f): @pytest.mark.parametrize( "postselect_mode", [None, "hw-like", "pad-invalid-samples", "fill-shots"] ) - def test_tree_traversal_interface_mcm(self, ml_framework, postselect_mode): + def test_tree_traversal_interface_mcm(self, ml_framework, postselect_mode, seed): """Test that tree traversal works numerically with different interfaces""" # pylint:disable = singleton-comparison, import-outside-toplevel @@ -1461,7 +1459,7 @@ def test_tree_traversal_interface_mcm(self, ml_framework, postselect_mode): shots=5500, ) - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) res1, res2 = simulate_tree_mcm(qscript, interface=ml_framework, rng=rng) p1 = [qml.math.mean(res1 == -1), qml.math.mean(res1 == 1)] @@ -1561,12 +1559,12 @@ def test_tree_traversal_combine_measurements(self, measurements, expected): else: assert qml.math.allclose(combined_measurement, expected) - @flaky(max_runs=3, min_passes=1) + @pytest.mark.local_salt(2) @pytest.mark.parametrize("ml_framework", ml_frameworks_list) @pytest.mark.parametrize( "postselect_mode", [None, "hw-like", "pad-invalid-samples", "fill-shots"] ) - def test_simulate_one_shot_native_mcm(self, ml_framework, postselect_mode): + def test_simulate_one_shot_native_mcm(self, ml_framework, postselect_mode, seed): """Unit tests for simulate_one_shot_native_mcm""" with qml.queuing.AnnotatedQueue() as q: @@ -1576,8 +1574,8 @@ def test_simulate_one_shot_native_mcm(self, ml_framework, postselect_mode): circuit = qml.tape.QuantumScript(q.queue, [qml.expval(qml.Z(0)), qml.sample(m)], shots=[1]) - rng = np.random.default_rng(1234) - n_shots = 500 + rng = np.random.default_rng(seed) + n_shots = 1000 results = [ simulate_one_shot_native_mcm( circuit, diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_sampling.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_sampling.py index 8471ceb48fb..176d9c92b31 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_sampling.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_sampling.py @@ -13,6 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. """Unit tests for sampling states in devices/qutrit_mixed.""" + +# pylint: disable=unused-argument,too-many-arguments + import numpy as np import pytest from flaky import flaky @@ -148,13 +151,13 @@ def test_prng_key_as_seed_uses_sample_state_jax(self, mocker, two_qutrit_state): spy.assert_called_once() @pytest.mark.jax - def test_sample_state_jax(self, two_qutrit_pure_state): + def test_sample_state_jax(self, two_qutrit_pure_state, seed): """Tests that the returned samples are as expected when explicitly calling _sample_state_jax.""" import jax state = qml.math.array(two_qutrit_pure_state, like="jax") - samples = _sample_state_jax(state, 10, prng_key=jax.random.PRNGKey(84)) + samples = _sample_state_jax(state, 10, prng_key=jax.random.PRNGKey(seed)) assert samples.shape == (10, 2) assert samples.dtype == np.int64 @@ -181,15 +184,14 @@ def test_sample_state_custom_rng(self, two_qutrit_state): expected = [[0, 2], [1, 0], [2, 1], [1, 2]] assert qml.math.allequal(samples, expected) - @flaky - def test_entangled_qutrit_samples_always_match(self): + def test_entangled_qutrit_samples_always_match(self, seed): """Tests that entangled qutrits are always in the same state.""" num_samples = 10000 bell_state_vector = np.array([[1, 0, 0, 0, 1, 0, 0, 0, 1]]) bell_state = get_dm_of_state(bell_state_vector, 2, 3) - samples = sample_state(bell_state, num_samples) + samples = sample_state(bell_state, num_samples, rng=seed) assert samples.shape == (num_samples, 2) assert not any(samples[:, 0] ^ samples[:, 1]) # all samples are entangled @@ -272,25 +274,25 @@ def test_sample_measure_single_wire(self): assert result1.dtype == np.int64 assert len(np.unique(result1)) == 3 - def test_approximate_sample_measure(self, two_qutrit_pure_state): + def test_approximate_sample_measure(self, two_qutrit_pure_state, seed): """Test that a sample measurement returns approximately the correct distribution""" shots = qml.measurements.Shots(10000) mp = qml.sample(wires=range(2)) - result = measure_with_samples(mp, two_qutrit_pure_state, shots=shots, rng=1234) + result = measure_with_samples(mp, two_qutrit_pure_state, shots=shots, rng=seed) one_or_two_prob = np.count_nonzero(result[:, 0]) / result.shape[0] one_prob = np.count_nonzero(result[:, 0] == 1) / result.shape[0] assert np.allclose(one_or_two_prob, 2 / 3, atol=APPROX_ATOL) assert np.allclose(one_prob, 1 / 3, atol=APPROX_ATOL) - def test_approximate_expval_measure(self, two_qutrit_state): + def test_approximate_expval_measure(self, two_qutrit_state, seed): """Test that an expval measurement works as expected""" state = qml.math.array(two_qutrit_state) shots = qml.measurements.Shots(10000) mp = qml.expval(qml.GellMann(0, 1) @ qml.GellMann(1, 1)) - result = measure_with_samples(mp, state, shots=shots, rng=1234) + result = measure_with_samples(mp, state, shots=shots, rng=seed) gellmann_1_matrix = qml.GellMann.compute_matrix(1) observable_matrix = np.kron(gellmann_1_matrix, gellmann_1_matrix) @@ -299,13 +301,13 @@ def test_approximate_expval_measure(self, two_qutrit_state): assert isinstance(result, np.float64) assert np.allclose(result, expected, atol=APPROX_ATOL) - def test_approximate_var_measure(self, two_qutrit_state): + def test_approximate_var_measure(self, two_qutrit_state, seed): """Test that a variance measurement works as expected""" state = qml.math.array(two_qutrit_state) shots = qml.measurements.Shots(10000) mp = qml.var(qml.GellMann(0, 1) @ qml.GellMann(1, 1)) - result = measure_with_samples(mp, state, shots=shots, rng=123) + result = measure_with_samples(mp, state, shots=shots, rng=seed) gellmann_1_matrix = qml.GellMann.compute_matrix(1) obs_mat = np.kron(gellmann_1_matrix, gellmann_1_matrix) @@ -441,9 +443,9 @@ def test_currently_unsupported_observable(self, mp, two_qutrit_state): class TestBroadcasting: """Test that measurements work when the state has a batch dim""" - def test_sample_measure(self, batched_two_qutrit_pure_state): + def test_sample_measure(self, batched_two_qutrit_pure_state, seed): """Test that broadcasting works for qml.sample and single shots""" - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) shots = qml.measurements.Shots(100) state = batched_two_qutrit_pure_state @@ -453,9 +455,9 @@ def test_sample_measure(self, batched_two_qutrit_pure_state): assert res.shape == (3, shots.total_shots, 2) assert res.dtype == np.int64 - def test_counts_measure(self, batched_two_qutrit_pure_state): + def test_counts_measure(self, batched_two_qutrit_pure_state, seed): """Test that broadcasting works for qml.sample and single shots""" - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) shots = qml.measurements.Shots(100) state = batched_two_qutrit_pure_state @@ -468,9 +470,9 @@ def test_counts_measure(self, batched_two_qutrit_pure_state): assert list(res[2].keys()) == ["01", "10", "21", "22"] @pytest.mark.parametrize("shots", shots_to_test_samples) - def test_sample_measure_shot_vector(self, shots, batched_two_qutrit_pure_state): + def test_sample_measure_shot_vector(self, shots, batched_two_qutrit_pure_state, seed): """Test that broadcasting works for qml.sample and shot vectors""" - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) shots = qml.measurements.Shots(shots) measurement = qml.sample(wires=[0, 1]) @@ -503,15 +505,10 @@ def test_sample_measure_shot_vector(self, shots, batched_two_qutrit_pure_state): (qml.var(qml.GellMann(1, 3)), np.array([1 / 4, 0, 1])), ], ) - def test_nonsample_measure_shot_vector( - self, - shots, - measurement, - expected, - ): + def test_nonsample_measure_shot_vector(self, shots, measurement, expected, seed): """Test that broadcasting works for the other sample measurements and shot vectors""" - rng = np.random.default_rng(123456) + rng = np.random.default_rng(seed) shots = qml.measurements.Shots(shots) state = [ @@ -537,13 +534,13 @@ class TestBroadcastingPRNG: """Test that measurements work and use _sample_state_jax when the state has a batch dim and a PRNG key is provided""" - def test_sample_measure(self, mocker, batched_two_qutrit_pure_state): + def test_sample_measure(self, mocker, batched_two_qutrit_pure_state, seed): """Test that broadcasting works for qml.sample and single shots""" import jax spy = mocker.spy(qml.devices.qutrit_mixed.sampling, "_sample_state_jax") - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) shots = qml.measurements.Shots(100) measurement = qml.sample(wires=[0, 1]) @@ -553,7 +550,7 @@ def test_sample_measure(self, mocker, batched_two_qutrit_pure_state): shots, is_state_batched=True, rng=rng, - prng_key=jax.random.PRNGKey(184), + prng_key=jax.random.PRNGKey(seed), ) spy.assert_called() @@ -567,7 +564,7 @@ def test_sample_measure(self, mocker, batched_two_qutrit_pure_state): assert_correct_sampled_batched_two_qutrit_pure_state(res) @pytest.mark.parametrize("shots", shots_to_test_samples) - def test_sample_measure_shot_vector(self, mocker, shots, batched_two_qutrit_pure_state): + def test_sample_measure_shot_vector(self, mocker, shots, batched_two_qutrit_pure_state, seed): """Test that broadcasting works for qml.sample and shot vectors""" import jax @@ -575,7 +572,7 @@ def test_sample_measure_shot_vector(self, mocker, shots, batched_two_qutrit_pure spy = mocker.spy(qml.devices.qutrit_mixed.sampling, "_sample_state_jax") - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) shots = qml.measurements.Shots(shots) measurement = qml.sample(wires=[0, 1]) @@ -585,7 +582,7 @@ def test_sample_measure_shot_vector(self, mocker, shots, batched_two_qutrit_pure shots, is_state_batched=True, rng=rng, - prng_key=jax.random.PRNGKey(184), + prng_key=jax.random.PRNGKey(seed), ) spy.assert_called() @@ -611,13 +608,13 @@ def test_sample_measure_shot_vector(self, mocker, shots, batched_two_qutrit_pure (qml.var(qml.GellMann(1, 3)), np.array([1 / 4, 0, 1])), ], ) - def test_nonsample_measure_shot_vector(self, mocker, shots, measurement, expected): + def test_nonsample_measure_shot_vector(self, mocker, shots, measurement, expected, seed): """Test that broadcasting works for the other sample measurements and shot vectors""" import jax spy = mocker.spy(qml.devices.qutrit_mixed.sampling, "_sample_state_jax") - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) shots = qml.measurements.Shots(shots) state = [ @@ -633,7 +630,7 @@ def test_nonsample_measure_shot_vector(self, mocker, shots, measurement, expecte shots, is_state_batched=True, rng=rng, - prng_key=jax.random.PRNGKey(184), + prng_key=jax.random.PRNGKey(seed), ) spy.assert_called() @@ -643,7 +640,7 @@ def test_nonsample_measure_shot_vector(self, mocker, shots, measurement, expecte for r in res: assert r.shape == expected.shape - assert np.allclose(r, expected, atol=0.01) + assert np.allclose(r, expected, atol=0.02) @pytest.mark.parametrize( @@ -658,7 +655,7 @@ class TestHamiltonianSamples: Hamiltonian and Sum observables""" @pytest.mark.usefixtures("use_legacy_and_new_opmath") - def test_hamiltonian_expval(self, obs): + def test_hamiltonian_expval(self, obs, seed): """Test that sampling works well for Hamiltonian and Sum observables""" if not qml.operation.active_new_opmath(): @@ -671,14 +668,14 @@ def test_hamiltonian_expval(self, obs): for op in ops: state = apply_operation(op, state) - res = measure_with_samples(qml.expval(obs), state, shots=shots, rng=300) + res = measure_with_samples(qml.expval(obs), state, shots=shots, rng=seed) expected = 0.8 * np.cos(x) + 0.5 * np.cos(y) * np.sin(x) assert isinstance(res, np.float64) assert np.allclose(res, expected, atol=APPROX_ATOL) @pytest.mark.usefixtures("use_legacy_and_new_opmath") - def test_hamiltonian_expval_shot_vector(self, obs): + def test_hamiltonian_expval_shot_vector(self, obs, seed): """Test that sampling works well for Hamiltonian and Sum observables with a shot vector""" if not qml.operation.active_new_opmath(): @@ -691,7 +688,7 @@ def test_hamiltonian_expval_shot_vector(self, obs): for op in ops: state = apply_operation(op, state) - res = measure_with_samples(qml.expval(obs), state, shots=shots, rng=300) + res = measure_with_samples(qml.expval(obs), state, shots=shots, rng=seed) expected = 0.8 * np.cos(x) + 0.5 * np.cos(y) * np.sin(x) @@ -704,11 +701,12 @@ def test_hamiltonian_expval_shot_vector(self, obs): class TestSampleProbs: # pylint: disable=attribute-defined-outside-init @pytest.fixture(autouse=True) - def setup(self): - self.rng = np.random.default_rng(42) + def setup(self, request): + seed = request.getfixturevalue("seed") + self.rng = np.random.default_rng(seed) self.shots = 1000 - def test_sample_probs_basic(self): + def test_sample_probs_basic(self, seed): probs = np.array([0.2, 0.3, 0.5]) num_wires = 1 is_state_batched = False @@ -722,7 +720,7 @@ def test_sample_probs_basic(self): observed_probs = counts / self.shots np.testing.assert_allclose(observed_probs, probs, atol=0.05) - def test_sample_probs_multi_wire(self): + def test_sample_probs_multi_wire(self, seed): probs = np.array( [0.1, 0.2, 0.3, 0.15, 0.1, 0.05, 0.05, 0.03, 0.02] ) # 3^2 = 9 probabilities for 2 wires @@ -734,7 +732,7 @@ def test_sample_probs_multi_wire(self): assert result.shape == (self.shots, num_wires) assert np.all(result >= 0) and np.all(result < QUDIT_DIM) - def test_sample_probs_batched(self): + def test_sample_probs_batched(self, seed): probs = np.array([[0.2, 0.3, 0.5], [0.4, 0.1, 0.5]]) num_wires = 1 is_state_batched = True @@ -752,11 +750,11 @@ def test_sample_probs_batched(self): (np.array([[0.2, 0.3, 0.5], [0.4, 0.1, 0.5]]), 1, True, (2, 1000, 1)), ], ) - def test_sample_probs_shapes(self, probs, num_wires, is_state_batched, expected_shape): + def test_sample_probs_shapes(self, probs, num_wires, is_state_batched, expected_shape, seed): result = sample_probs(probs, self.shots, num_wires, is_state_batched, self.rng) assert result.shape == expected_shape - def test_invalid_probs(self): + def test_invalid_probs(self, seed): probs = np.array( [0.1, 0.2, 0.3, 0.4] ) # 4 probabilities, which is invalid for qutrit system @@ -770,14 +768,15 @@ def test_invalid_probs(self): class TestSampleProbsJax: # pylint: disable=attribute-defined-outside-init @pytest.fixture(autouse=True) - def setup(self): + def setup(self, request): import jax - self.jax_key = jax.random.PRNGKey(42) + seed = request.getfixturevalue("seed") + self.jax_key = jax.random.PRNGKey(seed) self.shots = 1000 @pytest.mark.jax - def test_sample_probs_jax_basic(self): + def test_sample_probs_jax_basic(self, seed): probs = np.array([0.2, 0.3, 0.5]) num_wires = 1 is_state_batched = False @@ -795,7 +794,7 @@ def test_sample_probs_jax_basic(self): np.testing.assert_allclose(observed_probs, probs, atol=0.05) @pytest.mark.jax - def test_sample_probs_jax_multi_wire(self): + def test_sample_probs_jax_multi_wire(self, seed): probs = qml.math.array( [0.1, 0.2, 0.3, 0.15, 0.1, 0.05, 0.05, 0.03, 0.02] ) # 3^2 = 9 probabilities for 2 wires @@ -811,7 +810,7 @@ def test_sample_probs_jax_multi_wire(self): assert qml.math.all(result >= 0) and qml.math.all(result < QUDIT_DIM) @pytest.mark.jax - def test_sample_probs_jax_batched(self): + def test_sample_probs_jax_batched(self, seed): probs = qml.math.array([[0.2, 0.3, 0.5], [0.4, 0.1, 0.5]]) num_wires = 1 is_state_batched = True @@ -841,7 +840,7 @@ def test_sample_probs_jax_batched(self): ) @pytest.mark.jax def test_sample_probs_jax_shapes( - self, probs, num_wires, is_state_batched, expected_shape, state_len + self, probs, num_wires, is_state_batched, expected_shape, state_len, seed ): result = _sample_probs_jax( probs, self.shots, num_wires, is_state_batched, self.jax_key, state_len @@ -849,7 +848,7 @@ def test_sample_probs_jax_shapes( assert result.shape == expected_shape @pytest.mark.jax - def test_invalid_probs_jax(self): + def test_invalid_probs_jax(self, seed): probs = qml.math.array( [0.1, 0.2, 0.3, 0.4] ) # 4 probabilities, which is invalid for qutrit system diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_simulate.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_simulate.py index 90c36e89d92..cfe7231d345 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_simulate.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_simulate.py @@ -301,13 +301,13 @@ def test_broadcasted_op_state(self, subspace): assert len(res) == 2 assert np.allclose(res, expected) - def test_broadcasted_op_sample(self, subspace): + def test_broadcasted_op_sample(self, subspace, seed): """Test that simulate works for sample measurements when an operation has broadcasted parameters""" x = np.array([0.8, 1.0, 1.2, 1.4]) qs = self.get_quantum_script(x, subspace, shots=qml.measurements.Shots(10000)) - res = simulate(qs, rng=123) + res = simulate(qs, rng=seed) expected = self.get_expectation_values(x, subspace) assert isinstance(res, tuple) @@ -315,7 +315,7 @@ def test_broadcasted_op_sample(self, subspace): assert np.allclose(res, expected, atol=0.05) state, is_state_batched = get_final_state(qs) - res = measure_final_state(qs, state, is_state_batched, rng=123) + res = measure_final_state(qs, state, is_state_batched, rng=seed) assert np.allclose(state, self.get_expected_state(x, subspace)) assert is_state_batched diff --git a/tests/devices/test_default_clifford.py b/tests/devices/test_default_clifford.py index 29294dc4a2c..fac9de067cf 100644 --- a/tests/devices/test_default_clifford.py +++ b/tests/devices/test_default_clifford.py @@ -190,10 +190,10 @@ def circuit_fn(): qml.Projector([1, 0], [0, 1]), ], ) -def test_meas_expval(shots, ops): +def test_meas_expval(shots, ops, seed): """Test that expectation value measurements with `default.clifford` is possible and agrees with `default.qubit`.""" - dev_c = qml.device("default.clifford", shots=shots, seed=24) + dev_c = qml.device("default.clifford", shots=shots, seed=seed) dev_q = qml.device("default.qubit") def circuit_fn(): @@ -269,10 +269,10 @@ def circuit_fn(): qml.Projector([0, 1], wires=[0, 1]), ], ) -def test_meas_probs(tableau, shots, ops): +def test_meas_probs(tableau, shots, ops, seed): """Test if probabilities are returned in the clifford device.""" - dev_c = qml.device("default.clifford", tableau=tableau, shots=shots, seed=24) + dev_c = qml.device("default.clifford", tableau=tableau, shots=shots, seed=seed) dev_q = qml.device("default.qubit") def circuit_fn(): @@ -290,11 +290,9 @@ def circuit_fn(): assert qml.math.allclose(gotten_probs, target_probs, atol=5e-2 if shots else 1e-8) -def test_meas_probs_large(): +def test_meas_probs_large(seed): """Test if probabilities are returned in the clifford device with target basis states""" - dev_c = qml.device("default.clifford", seed=24) - def single_op(idx): return [qml.PauliX, qml.PauliY, qml.Hadamard, qml.PauliZ][idx] @@ -304,7 +302,7 @@ def circuit_fn2(meas): qml.CNOT([wire, wire + 1]) return qml.apply(meas) - dev_c = qml.device("default.clifford", seed=24) + dev_c = qml.device("default.clifford", seed=seed) qnode_clfrd = qml.QNode(circuit_fn2, dev_c) meas1 = qml.probs(op=qml.Projector([1, 1, 0], wires=[0, 6, 14])) @@ -321,11 +319,11 @@ def circuit_fn2(meas): "ops", [None, qml.PauliY(0), qml.PauliX(0) @ qml.PauliY(1)], ) -def test_meas_counts(shots, ops): +def test_meas_counts(shots, ops, seed): """Test if counts are returned with shots given in the clifford device.""" - dev_c = qml.device("default.clifford", shots=shots, seed=24) - dev_q = qml.device("default.qubit", shots=shots, seed=24) + dev_c = qml.device("default.clifford", shots=shots, seed=seed) + dev_q = qml.device("default.qubit", shots=shots, seed=seed) def circuit_fn(): qml.PauliX(0) @@ -352,7 +350,7 @@ def circuit_fn(): qml.PauliZ(0) @ qml.PauliY(1), ], ) -def test_meas_classical_shadows(shots, ops): +def test_meas_classical_shadows(shots, ops, seed): """Test if classical shadows measurements are returned with shots given in the clifford device.""" @@ -367,7 +365,7 @@ def circuit(): def circuit_shadow(): circuit() - return qml.classical_shadow(wires=[0, 1], seed=13) + return qml.classical_shadow(wires=[0, 1], seed=seed) qnode_clfrd_shadow = qml.QNode(circuit_shadow, dev_c) qnode_qubit_shadow = qml.QNode(circuit_shadow, dev_q) @@ -381,7 +379,7 @@ def circuit_shadow(): def circuit_expval(): circuit() - return qml.shadow_expval(ops, seed=13) + return qml.shadow_expval(ops, seed=seed) qnode_clfrd_expval = qml.QNode(circuit_expval, dev_c) expval = qnode_clfrd_expval() diff --git a/tests/devices/test_default_qutrit_mixed.py b/tests/devices/test_default_qutrit_mixed.py index 13f3d744bb1..8c9c635509a 100644 --- a/tests/devices/test_default_qutrit_mixed.py +++ b/tests/devices/test_default_qutrit_mixed.py @@ -573,7 +573,7 @@ def test_batch_tapes(self, subspace): assert results[1].shape == (50,) @pytest.mark.parametrize("all_outcomes", [False, True]) - def test_counts_obs(self, all_outcomes, subspace): + def test_counts_obs(self, all_outcomes, subspace, seed): """Test that a Counts measurement with an observable works as expected.""" x = np.array(np.pi / 2) qs = qml.tape.QuantumScript( @@ -582,7 +582,7 @@ def test_counts_obs(self, all_outcomes, subspace): shots=10000, ) - dev = DefaultQutritMixed(seed=123) + dev = DefaultQutritMixed(seed=seed) result = dev.execute(qs) assert isinstance(result, dict) @@ -591,7 +591,7 @@ def test_counts_obs(self, all_outcomes, subspace): # check that the count values match the expected values = list(result.values()) - assert np.allclose(values[0] / (values[0] + values[1]), 0.5, atol=0.01) + assert np.allclose(values[0] / (values[0] + values[1]), 0.5, atol=0.02) class TestExecutingBatches: @@ -1149,7 +1149,7 @@ class TestHamiltonianSamples: This is a copy of the tests in `test_qutrit_mixed_sampling.py`, but using the device instead. """ - def test_hamiltonian_expval(self, obs): + def test_hamiltonian_expval(self, obs, seed): """Tests that sampling works well for Hamiltonian and Sum observables.""" if not qml.operation.active_new_opmath(): obs = qml.operation.convert_to_legacy_H(obs) @@ -1157,14 +1157,14 @@ def test_hamiltonian_expval(self, obs): x, y = np.array(0.67), np.array(0.95) ops = [qml.TRY(x, wires=0), qml.TRZ(y, wires=0)] - dev = DefaultQutritMixed(seed=100) + dev = DefaultQutritMixed(seed=seed) qs = qml.tape.QuantumScript(ops, [qml.expval(obs)], shots=10000) res = dev.execute(qs) expected = 0.8 * np.cos(x) + 0.5 * np.cos(y) * np.sin(x) assert np.allclose(res, expected, atol=0.01) - def test_hamiltonian_expval_shot_vector(self, obs): + def test_hamiltonian_expval_shot_vector(self, obs, seed): """Test that sampling works well for Hamiltonian and Sum observables with a shot vector.""" if not qml.operation.active_new_opmath(): @@ -1173,7 +1173,7 @@ def test_hamiltonian_expval_shot_vector(self, obs): shots = qml.measurements.Shots((10000, 100000)) x, y = np.array(0.67), np.array(0.95) ops = [qml.TRY(x, wires=0), qml.TRZ(y, wires=0)] - dev = DefaultQutritMixed(seed=100) + dev = DefaultQutritMixed(seed=seed) qs = qml.tape.QuantumScript(ops, [qml.expval(obs)], shots=shots) res = dev.execute(qs) @@ -1181,8 +1181,8 @@ def test_hamiltonian_expval_shot_vector(self, obs): assert len(res) == 2 assert isinstance(res, tuple) - assert np.allclose(res[0], expected, atol=0.01) - assert np.allclose(res[1], expected, atol=0.01) + assert np.allclose(res[0], expected, atol=0.02) + assert np.allclose(res[1], expected, atol=0.02) class TestIntegration: @@ -1556,6 +1556,7 @@ def circuit(): res = circuit() assert res == expected + # pylint:disable=too-many-arguments @pytest.mark.parametrize( "relaxations, misclassifications, expected", [ @@ -1564,7 +1565,9 @@ def circuit(): [(0.2, 0.1, 0.4), (0.1, 0.2, 0.5), [11 / 24, 7 / 30, 37 / 120]], ], ) - def test_approximate_readout_counts(self, num_wires, relaxations, misclassifications, expected): + def test_approximate_readout_counts( + self, num_wires, relaxations, misclassifications, expected, seed + ): """Tests the counts output with readout error""" num_shots = 10000 dev = qml.device( @@ -1573,7 +1576,7 @@ def test_approximate_readout_counts(self, num_wires, relaxations, misclassificat wires=num_wires, readout_relaxation_probs=relaxations, readout_misclassification_probs=misclassifications, - seed=221349, + seed=seed, ) @qml.qnode(dev) diff --git a/tests/devices/test_null_qubit.py b/tests/devices/test_null_qubit.py index fd7f5b4638f..d0565b1b9e6 100644 --- a/tests/devices/test_null_qubit.py +++ b/tests/devices/test_null_qubit.py @@ -976,12 +976,12 @@ def test_shape_and_dtype(self, n_qubits): assert np.array_equal(res, np.zeros((2, 100, n_qubits))) assert res.dtype == np.int8 - def test_expval(self): + def test_expval(self, seed): """Test that shadow expval measurements work as expected""" dev = NullQubit() ops = [qml.Hadamard(0), qml.Hadamard(1)] - meas = [qml.shadow_expval(qml.PauliX(0) @ qml.PauliX(1), seed=200)] + meas = [qml.shadow_expval(qml.PauliX(0) @ qml.PauliX(1), seed=seed)] qs = qml.tape.QuantumScript(ops, meas, shots=1000) assert dev.execute(qs) == np.array(0.0) @@ -1135,12 +1135,12 @@ def circuit(): @pytest.mark.parametrize( "diff_method", ["device", "adjoint", "backprop", "finite-diff", "parameter-shift"] ) - def test_expected_shape_all_methods(self, diff_method): + def test_expected_shape_all_methods(self, diff_method, seed): """Test that the gradient shape is as expected with all diff methods.""" n_wires = 4 shape = qml.StronglyEntanglingLayers.shape(n_layers=5, n_wires=n_wires) - rng = np.random.default_rng(seed=1239594) + rng = np.random.default_rng(seed=seed) params = qml.numpy.array(rng.random(shape)) dev = qml.device("null.qubit") diff --git a/tests/gradients/core/test_fisher.py b/tests/gradients/core/test_fisher.py index 8d921701461..7f24aff648a 100644 --- a/tests/gradients/core/test_fisher.py +++ b/tests/gradients/core/test_fisher.py @@ -151,13 +151,13 @@ def circ(params): qml.device("lightning.qubit", wires=3), ), ) - def test_quantum_fisher_info(self, dev): + def test_quantum_fisher_info(self, dev, seed): """Integration test of quantum fisher information matrix CFIM. This is just calling ``qml.metric_tensor`` or ``qml.adjoint_metric_tensor`` and multiplying by a factor of 4""" n_wires = 2 - rng = pnp.random.default_rng(200) + rng = pnp.random.default_rng(seed) dev_hard = qml.device("default.qubit", wires=n_wires + 1, shots=1000, seed=rng) def qfunc(params): diff --git a/tests/gradients/core/test_gradient_transform.py b/tests/gradients/core/test_gradient_transform.py index f98bff71b2d..74d484b4bf4 100644 --- a/tests/gradients/core/test_gradient_transform.py +++ b/tests/gradients/core/test_gradient_transform.py @@ -250,11 +250,12 @@ def circuit(weights): else: assert np.allclose(res, expected, atol=atol, rtol=0) - @pytest.mark.parametrize("shots, atol", [(None, 1e-6), (1000, 1e-1), ([1000, 100], 2e-1)]) + @pytest.mark.parametrize("shots, atol", [(None, 1e-6), (1000, 2e-1), ([1000, 1500], 2e-1)]) @pytest.mark.parametrize("prefactor", [1.0, 2.0]) - def test_acting_on_qnodes_multi_param(self, shots, prefactor, atol): + def test_acting_on_qnodes_multi_param(self, shots, prefactor, atol, seed): """Test that a gradient transform acts on QNodes with multiple parameters correctly""" - dev = qml.device("default.qubit", wires=2, shots=shots) + + dev = qml.device("default.qubit", wires=2, shots=shots, seed=seed) @qml.qnode(dev) def circuit(weights): @@ -280,9 +281,9 @@ def circuit(weights): ] ) if isinstance(shots, list): - assert all(np.allclose(r, expected, atol=atol, rtol=0) for r in res) + assert all(np.allclose(r, expected, atol=atol) for r in res) else: - assert np.allclose(res, expected, atol=atol, rtol=0) + assert np.allclose(res, expected, atol=atol) @pytest.mark.xfail(reason="Gradient transforms are not compatible with shots and mixed shapes") @pytest.mark.parametrize("shots, atol", [(None, 1e-6), (1000, 1e-1), ([1000, 100], 2e-1)]) diff --git a/tests/gradients/core/test_pulse_gradient.py b/tests/gradients/core/test_pulse_gradient.py index f3154689ba0..164c047c776 100644 --- a/tests/gradients/core/test_pulse_gradient.py +++ b/tests/gradients/core/test_pulse_gradient.py @@ -64,14 +64,14 @@ class TestSplitEvolOps: # pylint: disable=too-many-arguments @pytest.mark.parametrize("ham, params, time, ob, word", split_evol_ops_test_cases_pauliword) - def test_with_pauliword(self, ham, params, time, ob, word): + def test_with_pauliword(self, ham, params, time, ob, word, seed): """Test that _split_evol_ops returns the right ops with correct relations to the input operation for a Pauli word as ``ob``.""" import jax ham = ham(None) - key = jax.random.PRNGKey(5324) + key = jax.random.PRNGKey(seed) op = qml.evolve(ham)(params, time) op_copy = copy.deepcopy(op) exp_time = [0, time] if qml.math.ndim(time) == 0 else time @@ -138,14 +138,14 @@ def test_with_pauliword(self, ham, params, time, ob, word): ] @pytest.mark.parametrize("ham, params, time, ob", split_evol_ops_test_cases_general) - def test_with_general_ob(self, ham, params, time, ob): + def test_with_general_ob(self, ham, params, time, ob, seed): """Test that _split_evol_ops returns the right ops with correct relations to the input operation for a general Hermitian as ``ob``.""" import jax ham = ham(None) - key = jax.random.PRNGKey(5324) + key = jax.random.PRNGKey(seed) op = qml.evolve(ham)(params, time) op_copy = copy.deepcopy(op) exp_time = [0, time] if qml.math.ndim(time) == 0 else time @@ -618,9 +618,6 @@ def test_multi_measure_multi_shots( expected = np.einsum(contraction, _psr_coeffs, _results, _cjacs) assert np.allclose(np.stack(res), expected * prefactor) - # TODO: Once #2690 is resolved and the corresponding error is removed, - # unskip the following test - @pytest.mark.skip("Broadcasting, shot vector and multi-measurement not supported.") @pytest.mark.parametrize("multi_term", [1, 4]) @pytest.mark.parametrize("meas_shape", [(), (4,)]) @pytest.mark.parametrize("par_shape", [(), (3,), (2, 2)]) @@ -697,25 +694,6 @@ def test_multi_measure_multi_shots_broadcast( expected = np.einsum(contraction, _psr_coeffs, _results, _cjacs) assert np.allclose(np.stack(res), expected * prefactor) - # TODO: Once #2690 is resolved and the corresponding error is removed, - # remove the following test - def test_raises_multi_measure_multi_shots_broadcasting(self): - """Test that an error is raised if multiple measurements, a shot vector and broadcasting - all are used simultaneously.""" - - _match = "Broadcasting, multiple measurements and shot vectors are currently" - with pytest.raises(NotImplementedError, match=_match): - # Dummy input values that are barely used before raising the error. - _parshift_and_integrate( - [], - [], - [], - [], - single_measure=False, - has_partitioned_shots=True, - use_broadcasting=True, - ) - @pytest.mark.jax class TestStochPulseGradErrors: @@ -818,12 +796,11 @@ def test_raises_non_pulse_marked_as_trainable(self): with pytest.raises(ValueError, match="stoch_pulse_grad does not support differentiating"): stoch_pulse_grad(tape) - @pytest.mark.skip(reason="This test fails because broadcasted tapes are not allowed at all.") def test_raises_use_broadcasting_with_broadcasted_tape(self): """Test that an error is raised if the option `use_broadcasting` is activated for a tape that already is broadcasted.""" ham = qml.dot([qml.pulse.constant], [qml.PauliX(0)]) - ops = [qml.evolve(ham, return_intermediate=True)([0.152], 0.3)] + ops = [qml.RX(0.5, wires=0), qml.evolve(ham, return_intermediate=True)([0.152], 0.3)] tape = qml.tape.QuantumScript(ops, measurements=[qml.expval(qml.PauliZ(0))]) tape.trainable_params = [0] with pytest.raises(ValueError, match="Broadcasting is not supported for tapes that"): @@ -1142,7 +1119,7 @@ def test_sin_envelope_rx_expval_probs(self, t): jax.clear_caches() @pytest.mark.parametrize("t", [0.02, (0.5, 0.6)]) - def test_pwc_envelope_rx(self, t): + def test_pwc_envelope_rx(self, t, seed): """Test that the derivative of a pulse generated by a piecewise constant Hamiltonian is computed correctly.""" import jax @@ -1160,7 +1137,7 @@ def test_pwc_envelope_rx(self, t): r = qml.execute([tape], dev, None) assert qml.math.isclose(r, jnp.cos(2 * p)) num_split_times = 5 - tapes, fn = stoch_pulse_grad(tape, num_split_times=num_split_times, sampler_seed=7512) + tapes, fn = stoch_pulse_grad(tape, num_split_times=num_split_times, sampler_seed=seed) assert len(tapes) == 2 * num_split_times res = fn(qml.execute(tapes, dev, None)) @@ -1229,6 +1206,11 @@ def qnode(params): num_split_times = 5 qnode.tape.trainable_params = [0, 1, 2] + # FIXME: This test case is not updated to use the pytest-rng generated seed because I'm + # unable to find a local salt that actually allows this test to pass. The 7123 here + # is basically a magic number. Every other seed I tried fails. I believe this test + # should be rewritten to use a better testing strategy because this currently goes + # against the spirit of seeding. tapes, fn = stoch_pulse_grad(qnode.tape, num_split_times=num_split_times, sampler_seed=7123) # Two generating terms with two shifts (X_0 and Z_0), one with eight shifts # (Y_0Y_1+0.4 X_1 has eigenvalues [-1.4, -0.6, 0.6, 1.4] yielding frequencies @@ -1281,7 +1263,7 @@ def test_randomness(self): assert not res_a_0 == res_b jax.clear_caches() - def test_two_pulses(self): + def test_two_pulses(self, seed): """Test that the derivatives of two pulses in a circuit are computed correctly.""" import jax import jax.numpy as jnp @@ -1304,7 +1286,7 @@ def qnode(params_0, params_1): num_split_times = 3 qnode.tape.trainable_params = [0, 1, 2] - tapes, fn = stoch_pulse_grad(qnode.tape, num_split_times=num_split_times, sampler_seed=7123) + tapes, fn = stoch_pulse_grad(qnode.tape, num_split_times=num_split_times, sampler_seed=seed) assert len(tapes) == 3 * 2 * num_split_times res = fn(qml.execute(tapes, dev, None)) @@ -1415,14 +1397,14 @@ class TestStochPulseGradIntegration: @pytest.mark.parametrize("shots, tol", [(None, 1e-4), (100, 0.1), ([100, 99], 0.1)]) @pytest.mark.parametrize("num_split_times", [1, 2]) - def test_simple_qnode_expval(self, num_split_times, shots, tol): + def test_simple_qnode_expval(self, num_split_times, shots, tol, seed): """Test that a simple qnode that returns an expectation value can be differentiated with stoch_pulse_grad.""" import jax import jax.numpy as jnp jax.config.update("jax_enable_x64", True) - dev = qml.device("default.qubit", wires=1, shots=shots, seed=jax.random.PRNGKey(74)) + dev = qml.device("default.qubit", wires=1, shots=shots, seed=jax.random.PRNGKey(seed)) T = 0.2 ham_single_q_const = qml.pulse.constant * qml.PauliY(0) @@ -1443,14 +1425,14 @@ def circuit(params): @pytest.mark.slow @pytest.mark.parametrize("shots, tol", [(None, 1e-4), (100, 0.1), ([100, 99], 0.1)]) @pytest.mark.parametrize("num_split_times", [1, 2]) - def test_simple_qnode_expval_two_evolves(self, num_split_times, shots, tol): + def test_simple_qnode_expval_two_evolves(self, num_split_times, shots, tol, seed): """Test that a simple qnode that returns an expectation value can be differentiated with stoch_pulse_grad.""" import jax import jax.numpy as jnp jax.config.update("jax_enable_x64", True) - dev = qml.device("default.qubit", wires=1, shots=shots, seed=jax.random.PRNGKey(74)) + dev = qml.device("default.qubit", wires=1, shots=shots, seed=jax.random.PRNGKey(seed)) T_x = 0.1 T_y = 0.2 ham_x = qml.pulse.constant * qml.PauliX(0) @@ -1474,14 +1456,14 @@ def circuit(params): @pytest.mark.parametrize("shots, tol", [(None, 1e-4), (100, 0.1), ([100, 99], 0.1)]) @pytest.mark.parametrize("num_split_times", [1, 2]) - def test_simple_qnode_probs(self, num_split_times, shots, tol): + def test_simple_qnode_probs(self, num_split_times, shots, tol, seed): """Test that a simple qnode that returns an probabilities can be differentiated with stoch_pulse_grad.""" import jax import jax.numpy as jnp jax.config.update("jax_enable_x64", True) - dev = qml.device("default.qubit", wires=1, shots=shots, seed=jax.random.PRNGKey(74)) + dev = qml.device("default.qubit", wires=1, shots=shots, seed=jax.random.PRNGKey(seed)) T = 0.2 ham_single_q_const = qml.pulse.constant * qml.PauliY(0) @@ -1501,14 +1483,14 @@ def circuit(params): @pytest.mark.parametrize("shots, tol", [(None, 1e-4), (100, 0.1), ([100, 100], 0.1)]) @pytest.mark.parametrize("num_split_times", [1, 2]) - def test_simple_qnode_probs_expval(self, num_split_times, shots, tol): + def test_simple_qnode_probs_expval(self, num_split_times, shots, tol, seed): """Test that a simple qnode that returns an probabilities can be differentiated with stoch_pulse_grad.""" import jax import jax.numpy as jnp jax.config.update("jax_enable_x64", True) - dev = qml.device("default.qubit", wires=1, shots=shots, seed=jax.random.PRNGKey(74)) + dev = qml.device("default.qubit", wires=1, shots=shots, seed=jax.random.PRNGKey(seed)) T = 0.2 ham_single_q_const = qml.pulse.constant * qml.PauliY(0) @@ -1560,7 +1542,7 @@ def circuit(params, T=None): jax.clear_caches() @pytest.mark.slow - def test_advanced_qnode(self): + def test_advanced_qnode(self, seed): """Test that an advanced qnode can be differentiated with stoch_pulse_grad.""" import jax import jax.numpy as jnp @@ -1586,7 +1568,7 @@ def ansatz(params): interface="jax", diff_method=stoch_pulse_grad, num_split_times=num_split_times, - sampler_seed=7123, + sampler_seed=seed, ) qnode_backprop = qml.QNode(ansatz, dev, interface="jax") @@ -1600,45 +1582,16 @@ def ansatz(params): ) jax.clear_caches() - def test_multi_return_broadcasting_multi_shots_raises(self): - """Test that a simple qnode that returns an expectation value and probabilities - can be differentiated with stoch_pulse_grad with use_broadcasting.""" - import jax - import jax.numpy as jnp - - jax.config.update("jax_enable_x64", True) - shots = [100, 100] - dev = qml.device("default.qubit", wires=1, shots=shots, seed=jax.random.PRNGKey(74)) - T = 0.2 - ham_single_q_const = qml.pulse.constant * qml.PauliY(0) - - @qml.qnode( - dev, - interface="jax", - diff_method=stoch_pulse_grad, - num_split_times=3, - use_broadcasting=True, - ) - def circuit(params): - qml.evolve(ham_single_q_const)(params, T) - return qml.probs(wires=0), qml.expval(qml.PauliZ(0)) - - params = [jnp.array(0.4)] - with pytest.raises(NotImplementedError, match="Broadcasting, multiple measurements and"): - jax.jacobian(circuit)(params) - jax.clear_caches() - - # TODO: delete error test above and uncomment the following test case once #2690 is resolved. - @pytest.mark.parametrize("shots, tol", [(None, 1e-4), (100, 0.1)]) # , ([100, 100], 0.1)]) + @pytest.mark.parametrize("shots, tol", [(None, 1e-4), (100, 0.1), ([100, 100], 0.1)]) @pytest.mark.parametrize("num_split_times", [1, 2]) - def test_qnode_probs_expval_broadcasting(self, num_split_times, shots, tol): + def test_qnode_probs_expval_broadcasting(self, num_split_times, shots, tol, seed): """Test that a simple qnode that returns an expectation value and probabilities can be differentiated with stoch_pulse_grad with use_broadcasting.""" import jax import jax.numpy as jnp jax.config.update("jax_enable_x64", True) - dev = qml.device("default.qubit", wires=1, shots=shots, seed=jax.random.PRNGKey(74)) + dev = qml.device("default.qubit", wires=1, shots=shots, seed=jax.random.PRNGKey(seed)) T = 0.2 ham_single_q_const = qml.pulse.constant * qml.PauliY(0) @@ -1667,7 +1620,7 @@ def circuit(params): jax.clear_caches() @pytest.mark.parametrize("num_split_times", [1, 2]) - def test_broadcasting_coincides_with_nonbroadcasting(self, num_split_times): + def test_broadcasting_coincides_with_nonbroadcasting(self, num_split_times, seed): """Test that using broadcasting or not does not change the result.""" import jax import jax.numpy as jnp @@ -1693,7 +1646,7 @@ def ansatz(params): diff_method=stoch_pulse_grad, num_split_times=num_split_times, use_broadcasting=True, - sampler_seed=324, + sampler_seed=seed, ) circuit_no_bc = qml.QNode( ansatz, @@ -1702,7 +1655,7 @@ def ansatz(params): diff_method=stoch_pulse_grad, num_split_times=num_split_times, use_broadcasting=False, - sampler_seed=324, + sampler_seed=seed, ) params = [jnp.array(0.4)] jac_bc = jax.jacobian(circuit_bc)(params) @@ -1736,7 +1689,7 @@ def ansatz(params): assert qml.math.allclose(res, exact, atol=6e-5) jax.clear_caches() - def test_with_drive_approx(self): + def test_with_drive_approx(self, seed): """Test that a HardwareHamiltonian only containing a drive is differentiated approximately correctly for a constant phase and zero frequency.""" import jax @@ -1758,7 +1711,7 @@ def ansatz(params): diff_method=qml.gradients.stoch_pulse_grad, num_split_times=7, use_broadcasting=True, - sampler_seed=4123, + sampler_seed=seed, ) cost_jax = qml.QNode(ansatz, dev, interface="jax") params = (0.42,) @@ -1771,7 +1724,7 @@ def ansatz(params): @pytest.mark.slow @pytest.mark.parametrize("num_params", [1, 2]) - def test_with_two_drives(self, num_params): + def test_with_two_drives(self, num_params, seed): """Test that a HardwareHamiltonian only containing two drives is differentiated approximately correctly. The two cases of the parametrization test the cases where reordered parameters @@ -1803,7 +1756,7 @@ def ansatz(params): diff_method=qml.gradients.stoch_pulse_grad, num_split_times=7, use_broadcasting=True, - sampler_seed=4123, + sampler_seed=seed, ) cost_jax = qml.QNode(ansatz, dev, interface="jax") diff --git a/tests/gradients/core/test_pulse_odegen.py b/tests/gradients/core/test_pulse_odegen.py index 3a7dbd4bddc..dd8d50c4887 100644 --- a/tests/gradients/core/test_pulse_odegen.py +++ b/tests/gradients/core/test_pulse_odegen.py @@ -386,11 +386,11 @@ def test_all_zero(self, num_wires): assert words == [] @pytest.mark.parametrize("num_wires", [1, 2, 3]) - def test_separate_nonzero(self, num_wires): + def test_separate_nonzero(self, num_wires, seed): """Test that a single coefficient in any of the coefficients is sufficient to keep the Pauli word in the filter.""" # Create many coefficients, each greater or equal ``1`` at distinct places. - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) coeffs = tuple(rng.uniform(1, 2, size=(4**num_wires - 1, 4**num_wires - 1))) new_coeffs, words = _nonzero_coeffs_and_words(coeffs, num_wires) @@ -1001,13 +1001,13 @@ class TestPulseOdegenTape: """Test that differentiating tapes with ``pulse_odegen`` works.""" @pytest.mark.parametrize("shots, tol", [(None, 1e-7), (1000, 0.05), ([1000, 100], 0.05)]) - def test_single_pulse_single_term(self, shots, tol): + def test_single_pulse_single_term(self, shots, tol, seed): """Test that a single pulse with a single Hamiltonian term is differentiated correctly.""" import jax import jax.numpy as jnp - prng_key = jax.random.PRNGKey(8251) + prng_key = jax.random.PRNGKey(seed) dev = qml.device("default.qubit", wires=1, shots=shots, seed=prng_key) H = jnp.polyval * X(0) @@ -1036,13 +1036,13 @@ def test_single_pulse_single_term(self, shots, tol): @pytest.mark.slow @pytest.mark.parametrize("shots, tol", [(None, 1e-7), ([1000, 100], 0.05)]) - def test_single_pulse_multi_term(self, shots, tol): + def test_single_pulse_multi_term(self, shots, tol, seed): """Test that a single pulse with multiple Hamiltonian terms is differentiated correctly.""" import jax import jax.numpy as jnp - prng_key = jax.random.PRNGKey(8251) + prng_key = jax.random.PRNGKey(seed) dev = qml.device("default.qubit", wires=1, shots=None) dev_shots = qml.device("default.qubit", wires=1, shots=shots, seed=prng_key) @@ -1116,13 +1116,13 @@ def test_single_pulse_multi_term_argnum(self, argnum): @pytest.mark.slow @pytest.mark.parametrize("shots, tol", [(None, 1e-7), ([1000, 100], 0.05)]) - def test_multi_pulse(self, shots, tol): + def test_multi_pulse(self, shots, tol, seed): """Test that a single pulse with multiple Hamiltonian terms is differentiated correctly.""" import jax import jax.numpy as jnp - prng_key = jax.random.PRNGKey(8251) + prng_key = jax.random.PRNGKey(seed) dev = qml.device("default.qubit", wires=1, shots=None) dev_shots = qml.device("default.qubit", wires=1, shots=shots, seed=prng_key) diff --git a/tests/gradients/core/test_vjp.py b/tests/gradients/core/test_vjp.py index 6917207f715..ecc398094e5 100644 --- a/tests/gradients/core/test_vjp.py +++ b/tests/gradients/core/test_vjp.py @@ -423,12 +423,12 @@ def test_torch(self, tol): @pytest.mark.tf @pytest.mark.slow - def test_tf(self, tol): + def test_tf(self, tol, seed): """Tests that the output of the VJP transform can be differentiated using TF.""" import tensorflow as tf - dev = qml.device("default.qubit", wires=2) + dev = qml.device("default.qubit", wires=2, seed=seed) params_np = np.array([0.543, -0.654], requires_grad=True) params = tf.Variable(params_np, dtype=tf.float64) @@ -468,7 +468,7 @@ def test_tf(self, tol): # qml.RX(weights[i], wires=i) # return [qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))] - # vanilla_numpy.random.seed(42) + # vanilla_numpy.random.seed(seed) # ndata = 100 # data = [vanilla_numpy.random.randn(nwires).astype("float32") for _ in range(ndata)] diff --git a/tests/gradients/finite_diff/test_finite_difference_shot_vec.py b/tests/gradients/finite_diff/test_finite_difference_shot_vec.py index 14bece2585e..ac6d19f575f 100644 --- a/tests/gradients/finite_diff/test_finite_difference_shot_vec.py +++ b/tests/gradients/finite_diff/test_finite_difference_shot_vec.py @@ -548,11 +548,11 @@ def test_single_expectation_value(self, approx_order, strategy, validate): assert np.allclose(res, expected, atol=0.15, rtol=0) - def test_single_expectation_value_with_argnum_all(self, approx_order, strategy, validate): + def test_single_expectation_value_with_argnum_all(self, approx_order, strategy, validate, seed): """Tests correct output shape and evaluation for a tape with a single expval output where all parameters are chosen to compute the jacobian""" - dev = qml.device("default.qubit", wires=2, shots=many_shots_shot_vector) + dev = qml.device("default.qubit", wires=2, shots=many_shots_shot_vector, seed=seed) x = 0.543 y = -0.654 @@ -590,7 +590,7 @@ def test_single_expectation_value_with_argnum_all(self, approx_order, strategy, assert np.allclose(res, expected, atol=0.15, rtol=0) - def test_single_expectation_value_with_argnum_one(self, approx_order, strategy, validate): + def test_single_expectation_value_with_argnum_one(self, approx_order, strategy, validate, seed): """Tests correct output shape and evaluation for a tape with a single expval output where only one parameter is chosen to estimate the jacobian. @@ -598,7 +598,7 @@ def test_single_expectation_value_with_argnum_one(self, approx_order, strategy, This test relies on the fact that exactly one term of the estimated jacobian will match the expected analytical value. """ - dev = qml.device("default.qubit", wires=2, seed=1967, shots=many_shots_shot_vector) + dev = qml.device("default.qubit", wires=2, seed=seed, shots=many_shots_shot_vector) x = 0.543 y = -0.654 diff --git a/tests/gradients/finite_diff/test_spsa_gradient.py b/tests/gradients/finite_diff/test_spsa_gradient.py index fb543381335..1bd2a198bca 100644 --- a/tests/gradients/finite_diff/test_spsa_gradient.py +++ b/tests/gradients/finite_diff/test_spsa_gradient.py @@ -90,10 +90,10 @@ def test_same_seeds(self): "ids, num", [(list(range(5)), 5), ([0, 2, 4], 5), ([0], 1), ([2, 3], 5)] ) @pytest.mark.parametrize("N", [10, 10000]) - def test_mean_and_var(self, ids, num, N): + def test_mean_and_var(self, ids, num, N, seed): """Test that the mean and variance of many produced samples are close to the theoretical values.""" - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) ids_mask = np.zeros(num, dtype=bool) ids_mask[ids] = True outputs = [_rademacher_sampler(ids, num, rng=rng) for _ in range(N)] @@ -110,7 +110,7 @@ def test_mean_and_var(self, ids, num, N): class TestSpsaGradient: """Tests for the SPSA gradient transform""" - def test_sampler_argument(self): + def test_sampler_argument(self, seed): """Make sure that custom samplers can be created as defined in the docs of spsa_grad.""" def sampler_required_kwarg( @@ -141,7 +141,7 @@ def sampler_required_arg( results = [] for sampler in [sampler_required_arg_or_kwarg, sampler_required_kwarg]: - sampler_rng = np.random.default_rng(42) + sampler_rng = np.random.default_rng(seed) tapes, proc_fn = spsa_grad( tape, sampler=sampler, num_directions=100, sampler_rng=sampler_rng ) @@ -446,10 +446,10 @@ def test_y0_provided(self): # one tape per direction, the unshifted one already was evaluated above assert len(tapes) == n - def test_independent_parameters(self): + def test_independent_parameters(self, seed): """Test the case where expectation values are independent of some parameters. For those parameters, the gradient should be evaluated to zero without executing the device.""" - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) dev = qml.device("default.qubit", wires=2) with qml.queuing.AnnotatedQueue() as q1: @@ -987,12 +987,12 @@ class TestSpsaGradientDifferentiation: """Test that the transform is differentiable""" @pytest.mark.autograd - def test_autograd(self, sampler, num_directions, atol): + def test_autograd(self, sampler, num_directions, atol, seed): """Tests that the output of the SPSA gradient transform can be differentiated using autograd, yielding second derivatives.""" dev = qml.device("default.qubit", wires=2) params = pnp.array([0.543, -0.654], requires_grad=True) - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) def cost_fn(x): with qml.queuing.AnnotatedQueue() as q: @@ -1023,12 +1023,12 @@ def cost_fn(x): assert np.allclose(res, expected, atol=atol, rtol=0) @pytest.mark.autograd - def test_autograd_ragged(self, sampler, num_directions, atol): + def test_autograd_ragged(self, sampler, num_directions, atol, seed): """Tests that the output of the SPSA gradient transform of a ragged tape can be differentiated using autograd, yielding second derivatives.""" dev = qml.device("default.qubit", wires=2) params = pnp.array([0.543, -0.654], requires_grad=True) - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) def cost_fn(x): with qml.queuing.AnnotatedQueue() as q: @@ -1055,14 +1055,14 @@ def cost_fn(x): @pytest.mark.tf @pytest.mark.slow - def test_tf(self, sampler, num_directions, atol): + def test_tf(self, sampler, num_directions, atol, seed): """Tests that the output of the SPSA gradient transform can be differentiated using TF, yielding second derivatives.""" import tensorflow as tf dev = qml.device("default.qubit", wires=2) params = tf.Variable([0.543, -0.654], dtype=tf.float64) - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) with tf.GradientTape(persistent=True) as t: with qml.queuing.AnnotatedQueue() as q: @@ -1096,14 +1096,14 @@ def test_tf(self, sampler, num_directions, atol): @pytest.mark.tf @pytest.mark.slow - def test_tf_ragged(self, sampler, num_directions, atol): + def test_tf_ragged(self, sampler, num_directions, atol, seed): """Tests that the output of the SPSA gradient transform of a ragged tape can be differentiated using TF, yielding second derivatives.""" import tensorflow as tf dev = qml.device("default.qubit", wires=2) params = tf.Variable([0.543, -0.654], dtype=tf.float64) - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) with tf.GradientTape(persistent=True) as t: with qml.queuing.AnnotatedQueue() as q: @@ -1132,14 +1132,14 @@ def test_tf_ragged(self, sampler, num_directions, atol): assert np.allclose(res_01[0], expected, atol=atol, rtol=0) @pytest.mark.torch - def test_torch(self, sampler, num_directions, atol): + def test_torch(self, sampler, num_directions, atol, seed): """Tests that the output of the SPSA gradient transform can be differentiated using Torch, yielding second derivatives.""" import torch dev = qml.device("default.qubit", wires=2) params = torch.tensor([0.543, -0.654], dtype=torch.float64, requires_grad=True) - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) def cost_fn(params): with qml.queuing.AnnotatedQueue() as q: @@ -1172,7 +1172,7 @@ def cost_fn(params): assert np.allclose(hess[1].detach().numpy(), expected[1], atol=atol, rtol=0) @pytest.mark.jax - def test_jax(self, sampler, num_directions, atol): + def test_jax(self, sampler, num_directions, atol, seed): """Tests that the output of the SPSA gradient transform can be differentiated using JAX, yielding second derivatives.""" import jax @@ -1180,7 +1180,7 @@ def test_jax(self, sampler, num_directions, atol): dev = qml.device("default.qubit", wires=2) params = jnp.array([0.543, -0.654]) - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) def cost_fn(x): with qml.queuing.AnnotatedQueue() as q: diff --git a/tests/gradients/finite_diff/test_spsa_gradient_shot_vec.py b/tests/gradients/finite_diff/test_spsa_gradient_shot_vec.py index 69646131de2..37035171a5d 100644 --- a/tests/gradients/finite_diff/test_spsa_gradient_shot_vec.py +++ b/tests/gradients/finite_diff/test_spsa_gradient_shot_vec.py @@ -236,10 +236,10 @@ def circuit(weights): with pytest.raises(qml.QuantumFunctionError, match="No trainable parameters."): spsa_grad(circuit, h=h_val)(weights) - def test_all_zero_diff_methods(self): + def test_all_zero_diff_methods(self, seed): """Test that the transform works correctly when the diff method for every parameter is identified to be 0, and that no tapes were generated.""" - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) dev = qml.device("default.qubit", wires=4, shots=default_shot_vector) @qml.qnode(dev) @@ -260,10 +260,10 @@ def circuit(params): assert result.shape == (4, 3) assert np.allclose(result, 0) - def test_all_zero_diff_methods_multiple_returns(self): + def test_all_zero_diff_methods_multiple_returns(self, seed): """Test that the transform works correctly when the diff method for every parameter is identified to be 0, and that no tapes were generated.""" - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) dev = qml.device("default.qubit", wires=4, shots=many_shots_shot_vector) @@ -333,10 +333,10 @@ def test_y0_provided(self): assert len(tapes) == n - def test_independent_parameters(self): + def test_independent_parameters(self, seed): """Test the case where expectation values are independent of some parameters. For those parameters, the gradient should be evaluated to zero without executing the device.""" - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) dev = qml.device("default.qubit", wires=2, shots=many_shots_shot_vector) with qml.queuing.AnnotatedQueue() as q1: @@ -514,11 +514,11 @@ def reference_qnode(x): class TestSpsaGradientIntegration: """Tests for the SPSA gradient transform""" - def test_ragged_output(self, approx_order, strategy, validate): + def test_ragged_output(self, approx_order, strategy, validate, seed): """Test that the Jacobian is correctly returned for a tape with ragged output""" dev = qml.device("default.qubit", wires=3, shots=many_shots_shot_vector) params = [1.0, 1.0, 1.0] - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) with qml.queuing.AnnotatedQueue() as q: qml.RX(params[0], wires=[0]) @@ -556,10 +556,10 @@ def test_ragged_output(self, approx_order, strategy, validate): assert res[1][1].shape == (4,) assert res[1][2].shape == (4,) - def test_single_expectation_value(self, approx_order, strategy, validate): + def test_single_expectation_value(self, approx_order, strategy, validate, seed): """Tests correct output shape and evaluation for a tape with a single expval output""" - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) dev = qml.device("default.qubit", wires=2, shots=many_shots_shot_vector) x = 0.543 y = -0.654 @@ -602,11 +602,11 @@ def test_single_expectation_value(self, approx_order, strategy, validate): # 1 / num_params here. assert np.allclose([2 * r for r in res], expected, atol=spsa_shot_vec_tol, rtol=0) - def test_single_expectation_value_with_argnum_all(self, approx_order, strategy, validate): + def test_single_expectation_value_with_argnum_all(self, approx_order, strategy, validate, seed): """Tests correct output shape and evaluation for a tape with a single expval output where all parameters are chosen to compute the jacobian""" - rng = np.random.default_rng(5214) + rng = np.random.default_rng(seed) dev = qml.device("default.qubit", wires=2, shots=many_shots_shot_vector) x = 0.543 y = -0.654 @@ -651,7 +651,7 @@ def test_single_expectation_value_with_argnum_all(self, approx_order, strategy, # 1 / num_params here. assert np.allclose([2 * r for r in res], expected, atol=spsa_shot_vec_tol, rtol=0) - def test_single_expectation_value_with_argnum_one(self, approx_order, strategy, validate): + def test_single_expectation_value_with_argnum_one(self, approx_order, strategy, validate, seed): """Tests correct output shape and evaluation for a tape with a single expval output where only one parameter is chosen to estimate the jacobian. @@ -659,7 +659,7 @@ def test_single_expectation_value_with_argnum_one(self, approx_order, strategy, This test relies on the fact that exactly one term of the estimated jacobian will match the expected analytical value. """ - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) dev = qml.device("default.qubit", wires=2, shots=many_shots_shot_vector) x = 0.543 y = -0.654 @@ -704,7 +704,9 @@ def test_single_expectation_value_with_argnum_one(self, approx_order, strategy, # parameter, so that we do not need to compensate like in the other tests assert np.allclose(res, expected, atol=spsa_shot_vec_tol, rtol=0) - def test_multiple_expectation_value_with_argnum_one(self, approx_order, strategy, validate): + def test_multiple_expectation_value_with_argnum_one( + self, approx_order, strategy, validate, seed + ): """Tests correct output shape and evaluation for a tape with a multiple measurement, where only one parameter is chosen to be trainable. @@ -712,7 +714,7 @@ def test_multiple_expectation_value_with_argnum_one(self, approx_order, strategy This test relies on the fact that exactly one term of the estimated jacobian will match the expected analytical value. """ - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) dev = qml.device("default.qubit", wires=2, shots=many_shots_shot_vector) x = 0.543 y = -0.654 @@ -751,10 +753,10 @@ def test_multiple_expectation_value_with_argnum_one(self, approx_order, strategy assert isinstance(res[1], tuple) assert np.allclose(res[1][0], 0) - def test_multiple_expectation_values(self, approx_order, strategy, validate): + def test_multiple_expectation_values(self, approx_order, strategy, validate, seed): """Tests correct output shape and evaluation for a tape with multiple expval outputs""" - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) dev = qml.device("default.qubit", wires=2, shots=many_shots_shot_vector) x = 0.543 y = -0.654 @@ -805,10 +807,10 @@ def test_multiple_expectation_values(self, approx_order, strategy, validate): res_1 = (2 * res[1][0], 2 * res[1][1]) assert np.allclose(res_1, [0, np.cos(y)], atol=spsa_shot_vec_tol, rtol=0) - def test_var_expectation_values(self, approx_order, strategy, validate): + def test_var_expectation_values(self, approx_order, strategy, validate, seed): """Tests correct output shape and evaluation for a tape with expval and var outputs""" - rng = np.random.default_rng(52) + rng = np.random.default_rng(seed) dev = qml.device("default.qubit", wires=2, shots=many_shots_shot_vector, seed=rng) x = 0.543 @@ -860,10 +862,10 @@ def test_var_expectation_values(self, approx_order, strategy, validate): res_1, [0, -2 * np.cos(y) * np.sin(y)], atol=spsa_shot_vec_tol, rtol=0 ) - def test_prob_expectation_values(self, approx_order, strategy, validate): + def test_prob_expectation_values(self, approx_order, strategy, validate, seed): """Tests correct output shape and evaluation for a tape with prob and expval outputs""" - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) dev = qml.device("default.qubit", wires=2, shots=many_shots_shot_vector) x = 0.543 y = -0.654 @@ -944,12 +946,12 @@ class TestSpsaGradientDifferentiation: """Test that the transform is differentiable""" @pytest.mark.autograd - def test_autograd(self, approx_order, strategy): + def test_autograd(self, approx_order, strategy, seed): """Tests that the output of the SPSA gradient transform can be differentiated using autograd, yielding second derivatives.""" dev = qml.device("default.qubit", wires=2, shots=many_shots_shot_vector) params = pnp.array([0.543, -0.654], requires_grad=True) - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) def cost_fn(x): with qml.queuing.AnnotatedQueue() as q: @@ -987,12 +989,12 @@ def cost_fn(x): assert np.allclose(res, expected, atol=spsa_shot_vec_tol, rtol=0) @pytest.mark.autograd - def test_autograd_ragged(self, approx_order, strategy): + def test_autograd_ragged(self, approx_order, strategy, seed): """Tests that the output of the SPSA gradient transform of a ragged tape can be differentiated using autograd, yielding second derivatives.""" dev = qml.device("default.qubit", wires=2, shots=many_shots_shot_vector) params = pnp.array([0.543, -0.654], requires_grad=True) - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) def cost_fn(x): with qml.queuing.AnnotatedQueue() as q: @@ -1027,14 +1029,14 @@ def cost_fn(x): @pytest.mark.tf @pytest.mark.slow - def test_tf(self, approx_order, strategy): + def test_tf(self, approx_order, strategy, seed): """Tests that the output of the SPSA gradient transform can be differentiated using TF, yielding second derivatives.""" import tensorflow as tf dev = qml.device("default.qubit", wires=2, shots=many_shots_shot_vector) params = tf.Variable([0.543, -0.654], dtype=tf.float64) - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) with tf.GradientTape(persistent=True) as t: with qml.queuing.AnnotatedQueue() as q: @@ -1069,14 +1071,14 @@ def test_tf(self, approx_order, strategy): assert np.allclose([res_0, res_1], expected, atol=spsa_shot_vec_tol, rtol=0) @pytest.mark.tf - def test_tf_ragged(self, approx_order, strategy): + def test_tf_ragged(self, approx_order, strategy, seed): """Tests that the output of the SPSA gradient transform of a ragged tape can be differentiated using TF, yielding second derivatives.""" import tensorflow as tf dev = qml.device("default.qubit", wires=2, shots=many_shots_shot_vector) params = tf.Variable([0.543, -0.654], dtype=tf.float64) - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) with tf.GradientTape(persistent=True) as t: with qml.queuing.AnnotatedQueue() as q: @@ -1108,14 +1110,14 @@ def test_tf_ragged(self, approx_order, strategy): assert np.allclose(res_01[0], expected, atol=spsa_shot_vec_tol, rtol=0) @pytest.mark.torch - def test_torch(self, approx_order, strategy): + def test_torch(self, approx_order, strategy, seed): """Tests that the output of the SPSA gradient transform can be differentiated using Torch, yielding second derivatives.""" import torch dev = qml.device("default.qubit", wires=2, shots=many_shots_shot_vector) params = torch.tensor([0.543, -0.654], dtype=torch.float64, requires_grad=True) - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) def cost_fn(params): with qml.queuing.AnnotatedQueue() as q: @@ -1150,7 +1152,7 @@ def cost_fn(params): assert np.allclose(hess[1].detach().numpy(), expected[1], atol=spsa_shot_vec_tol, rtol=0) @pytest.mark.jax - def test_jax(self, approx_order, strategy): + def test_jax(self, approx_order, strategy, seed): """Tests that the output of the SPSA gradient transform can be differentiated using JAX, yielding second derivatives.""" import jax @@ -1158,7 +1160,7 @@ def test_jax(self, approx_order, strategy): dev = qml.device("default.qubit", wires=2, shots=many_shots_shot_vector) params = jnp.array([0.543, -0.654]) - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) def cost_fn(x): with qml.queuing.AnnotatedQueue() as q: diff --git a/tests/gradients/parameter_shift/test_parameter_shift.py b/tests/gradients/parameter_shift/test_parameter_shift.py index 8d8d17bfaa7..5f8d6308a51 100644 --- a/tests/gradients/parameter_shift/test_parameter_shift.py +++ b/tests/gradients/parameter_shift/test_parameter_shift.py @@ -2062,10 +2062,10 @@ def test_non_involutory_variance_multi_param(self, tol): assert gradA[1] == pytest.approx(expected, abs=tol) assert gradF[1] == pytest.approx(expected, abs=tol) - def test_involutory_and_noninvolutory_variance_single_param(self, tol): + def test_involutory_and_noninvolutory_variance_single_param(self, tol, seed): """Tests a qubit Hermitian observable that is not involutory alongside an involutory observable when there's a single trainable parameter.""" - dev = qml.device("default.qubit", wires=2) + dev = qml.device("default.qubit", wires=2, seed=seed) A = np.array([[4, -1 + 6j], [-1 - 6j, 2]]) a = 0.54 diff --git a/tests/gradients/parameter_shift/test_parameter_shift_shot_vec.py b/tests/gradients/parameter_shift/test_parameter_shift_shot_vec.py index 4ad2b84007f..ca55758062c 100644 --- a/tests/gradients/parameter_shift/test_parameter_shift_shot_vec.py +++ b/tests/gradients/parameter_shift/test_parameter_shift_shot_vec.py @@ -1312,11 +1312,10 @@ def test_non_involutory_variance_single_param(self, broadcast): assert gradF.shape == () assert qml.math.allclose(gradF, expected, atol=2 * _herm_shot_vec_tol) - @flaky(max_runs=5) - def test_non_involutory_variance_multi_param(self, broadcast): + def test_non_involutory_variance_multi_param(self, broadcast, seed): """Tests a qubit Hermitian observable that is not involutory with multiple trainable parameters""" shot_vec = many_shots_shot_vector - dev = qml.device("default.qubit", wires=1, shots=shot_vec) + dev = qml.device("default.qubit", wires=1, shots=shot_vec, seed=seed) a = 0.34 b = 0.20 @@ -1721,11 +1720,11 @@ def test_expval_and_variance_single_param(self, broadcast): assert isinstance(gradF, tuple) assert gradF == pytest.approx(expected, abs=finite_diff_tol) - def test_expval_and_variance_multi_param(self, broadcast): + def test_expval_and_variance_multi_param(self, broadcast, seed): """Test an expectation value and the variance of involutory and non-involutory observables work well with multiple trainable parameters""" shot_vec = many_shots_shot_vector - dev = qml.device("default.qubit", wires=3, shots=shot_vec, seed=12393) + dev = qml.device("default.qubit", wires=3, shots=shot_vec, seed=seed) a = 0.54 b = -0.423 diff --git a/tests/interfaces/test_autograd.py b/tests/interfaces/test_autograd.py index d206f1758d3..589ebed952b 100644 --- a/tests/interfaces/test_autograd.py +++ b/tests/interfaces/test_autograd.py @@ -27,6 +27,12 @@ pytestmark = pytest.mark.autograd +def get_device(device_name, seed): + if device_name == "param_shift.qubit": + return ParamShiftDerivativesDevice(seed=seed) + return qml.device(device_name, seed=seed) + + # pylint: disable=too-few-public-methods class TestCaching: """Tests for caching behaviour""" @@ -126,14 +132,14 @@ def f(x): # add tests for lightning 2 when possible # set rng for device when possible test_matrix = [ - ({"gradient_fn": param_shift}, Shots(50000), DefaultQubit(seed=42)), - ({"gradient_fn": param_shift}, Shots((50000, 50000)), DefaultQubit(seed=42)), - ({"gradient_fn": param_shift}, Shots(None), DefaultQubit()), - ({"gradient_fn": "backprop"}, Shots(None), DefaultQubit()), + ({"gradient_fn": param_shift}, Shots(50000), "default.qubit"), + ({"gradient_fn": param_shift}, Shots((50000, 50000)), "default.qubit"), + ({"gradient_fn": param_shift}, Shots(None), "default.qubit"), + ({"gradient_fn": "backprop"}, Shots(None), "default.qubit"), ( {"gradient_fn": "adjoint", "grad_on_execution": True, "device_vjp": False}, Shots(None), - DefaultQubit(), + "default.qubit", ), ( { @@ -142,33 +148,33 @@ def f(x): "device_vjp": False, }, Shots(None), - DefaultQubit(), + "default.qubit", ), - ({"gradient_fn": "adjoint", "device_vjp": True}, Shots(None), DefaultQubit()), + ({"gradient_fn": "adjoint", "device_vjp": True}, Shots(None), "default.qubit"), ( {"gradient_fn": "device", "device_vjp": False}, Shots((50000, 50000)), - ParamShiftDerivativesDevice(seed=904747894), + "param_shift.qubit", ), ( {"gradient_fn": "device", "device_vjp": True}, Shots((100000, 100000)), - ParamShiftDerivativesDevice(seed=10490244), + "param_shift.qubit", ), ( {"gradient_fn": param_shift}, Shots(None), - qml.device("reference.qubit"), + "reference.qubit", ), ( {"gradient_fn": param_shift}, Shots(50000), - qml.device("reference.qubit", seed=8743274), + "reference.qubit", ), ( {"gradient_fn": param_shift}, Shots((50000, 50000)), - qml.device("reference.qubit", seed=8743274), + "reference.qubit", ), ] @@ -178,14 +184,16 @@ def atol_for_shots(shots): return 5e-2 if shots else 1e-6 -@pytest.mark.parametrize("execute_kwargs, shots, device", test_matrix) +@pytest.mark.parametrize("execute_kwargs, shots, device_name", test_matrix) class TestAutogradExecuteIntegration: """Test the autograd interface execute function integrates well for both forward and backward execution""" - def test_execution(self, execute_kwargs, shots, device): + def test_execution(self, execute_kwargs, shots, device_name, seed): """Test execution""" + device = get_device(device_name, seed=seed) + def cost(a, b): ops1 = [qml.RY(a, wires=0), qml.RX(b, wires=0)] tape1 = qml.tape.QuantumScript(ops1, [qml.expval(qml.PauliZ(0))], shots=shots) @@ -214,8 +222,10 @@ def cost(a, b): assert qml.math.allclose(res[0], np.cos(a) * np.cos(b), atol=atol_for_shots(shots)) assert qml.math.allclose(res[1], np.cos(a) * np.cos(b), atol=atol_for_shots(shots)) - def test_scalar_jacobian(self, execute_kwargs, shots, device): + def test_scalar_jacobian(self, execute_kwargs, shots, device_name, seed): """Test scalar jacobian calculation""" + + device = get_device(device_name, seed=seed) a = pnp.array(0.1, requires_grad=True) def cost(a): @@ -238,11 +248,13 @@ def cost(a): assert np.allclose(res, expected, atol=atol_for_shots(shots), rtol=0) assert np.allclose(res, -np.sin(a), atol=atol_for_shots(shots)) - def test_jacobian(self, execute_kwargs, shots, device): + def test_jacobian(self, execute_kwargs, shots, device_name, seed): """Test jacobian calculation""" a = pnp.array(0.1, requires_grad=True) b = pnp.array(0.2, requires_grad=True) + device = get_device(device_name, seed=seed) + def cost(a, b): ops = [qml.RY(a, wires=0), qml.RX(b, wires=1), qml.CNOT(wires=[0, 1])] m = [qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliY(1))] @@ -276,10 +288,12 @@ def cost(a, b): assert np.allclose(_r, _e, atol=atol_for_shots(shots)) @pytest.mark.filterwarnings("ignore:Attempted to compute the gradient") - def test_tape_no_parameters(self, execute_kwargs, shots, device): + def test_tape_no_parameters(self, execute_kwargs, shots, device_name, seed): """Test that a tape with no parameters is correctly ignored during the gradient computation""" + device = get_device(device_name, seed=seed) + def cost(params): tape1 = qml.tape.QuantumScript( [qml.Hadamard(0)], [qml.expval(qml.PauliX(0))], shots=shots @@ -323,12 +337,14 @@ def cost(params): assert np.allclose(grad, expected, atol=atol_for_shots(shots), rtol=0) @pytest.mark.filterwarnings("ignore:Attempted to compute the gradient") - def test_tapes_with_different_return_size(self, execute_kwargs, shots, device): + def test_tapes_with_different_return_size(self, execute_kwargs, shots, device_name, seed): """Test that tapes wit different can be executed and differentiated.""" if execute_kwargs["gradient_fn"] == "backprop": pytest.xfail("backprop is not compatible with something about this situation.") + device = get_device(device_name, seed=seed) + def cost(params): tape1 = qml.tape.QuantumScript( [qml.RY(params[0], 0), qml.RX(params[1], 0)], @@ -406,8 +422,11 @@ def cost(params): assert np.allclose(jac[0, 1], d2, atol=atol_for_shots(shots)) assert np.allclose(jac[3, 1], d2, atol=atol_for_shots(shots)) - def test_reusing_quantum_tape(self, execute_kwargs, shots, device): + def test_reusing_quantum_tape(self, execute_kwargs, shots, device_name, seed): """Test re-using a quantum tape by passing new parameters""" + + device = get_device(device_name, seed=seed) + a = pnp.array(0.1, requires_grad=True) b = pnp.array(0.2, requires_grad=True) @@ -443,12 +462,14 @@ def cost(a, b): for _j, _e in zip(jac, expected): assert np.allclose(_j, _e, atol=atol_for_shots(shots), rtol=0) - def test_classical_processing(self, execute_kwargs, device, shots): + def test_classical_processing(self, execute_kwargs, device_name, seed, shots): """Test classical processing within the quantum tape""" a = pnp.array(0.1, requires_grad=True) b = pnp.array(0.2, requires_grad=False) c = pnp.array(0.3, requires_grad=True) + device = get_device(device_name, seed=seed) + def cost(a, b, c): ops = [ qml.RY(a * c, wires=0), @@ -471,11 +492,13 @@ def cost(a, b, c): # I tried getting analytic results for this circuit but I kept being wrong and am giving up - def test_no_trainable_parameters(self, execute_kwargs, shots, device): + def test_no_trainable_parameters(self, execute_kwargs, shots, device_name, seed): """Test evaluation and Jacobian if there are no trainable parameters""" a = pnp.array(0.1, requires_grad=False) b = pnp.array(0.2, requires_grad=False) + device = get_device(device_name, seed=seed) + def cost(a, b): ops = [qml.RY(a, 0), qml.RX(b, 0), qml.CNOT((0, 1))] m = [qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))] @@ -497,9 +520,10 @@ def loss(a, b): assert np.allclose(res, 0) - def test_matrix_parameter(self, execute_kwargs, device, shots): + def test_matrix_parameter(self, execute_kwargs, device_name, seed, shots): """Test that the autograd interface works correctly with a matrix parameter""" + device = get_device(device_name, seed=seed) U = pnp.array([[0, 1], [1, 0]], requires_grad=False) a = pnp.array(0.1, requires_grad=True) @@ -516,10 +540,12 @@ def cost(a, U): assert isinstance(jac, np.ndarray) assert np.allclose(jac, np.sin(a), atol=atol_for_shots(shots), rtol=0) - def test_differentiable_expand(self, execute_kwargs, device, shots): + def test_differentiable_expand(self, execute_kwargs, device_name, seed, shots): """Test that operation and nested tapes expansion is differentiable""" + device = get_device(device_name, seed=seed) + class U3(qml.U3): """Dummy operator.""" @@ -574,10 +600,12 @@ def cost_fn(a, p): ) assert np.allclose(res, expected, atol=atol_for_shots(shots), rtol=0) - def test_probability_differentiation(self, execute_kwargs, device, shots): + def test_probability_differentiation(self, execute_kwargs, device_name, seed, shots): """Tests correct output shape and evaluation for a tape with prob outputs""" + device = get_device(device_name, seed=seed) + def cost(x, y): ops = [qml.RX(x, 0), qml.RY(y, 1), qml.CNOT((0, 1))] m = [qml.probs(wires=0), qml.probs(wires=1)] @@ -627,10 +655,12 @@ def cost(x, y): assert np.allclose(res[0], expected[0], atol=atol_for_shots(shots), rtol=0) assert np.allclose(res[1], expected[1], atol=atol_for_shots(shots), rtol=0) - def test_ragged_differentiation(self, execute_kwargs, device, shots): + def test_ragged_differentiation(self, execute_kwargs, device_name, seed, shots): """Tests correct output shape and evaluation for a tape with prob and expval outputs""" + device = get_device(device_name, seed=seed) + def cost(x, y): ops = [qml.RX(x, wires=0), qml.RY(y, 1), qml.CNOT((0, 1))] m = [qml.expval(qml.PauliZ(0)), qml.probs(wires=1)] @@ -739,16 +769,18 @@ def cost_fn(x): assert np.allclose(res, expected, atol=tol, rtol=0) -@pytest.mark.parametrize("execute_kwargs, shots, device", test_matrix) +@pytest.mark.parametrize("execute_kwargs, shots, device_name", test_matrix) @pytest.mark.usefixtures("use_legacy_and_new_opmath") class TestHamiltonianWorkflows: """Test that tapes ending with expectations of Hamiltonians provide correct results and gradients""" @pytest.fixture - def cost_fn(self, execute_kwargs, shots, device): + def cost_fn(self, execute_kwargs, shots, device_name, seed): """Cost function for gradient tests""" + device = get_device(device_name, seed=seed) + def _cost_fn(weights, coeffs1, coeffs2): obs1 = [qml.PauliZ(0), qml.PauliZ(0) @ qml.PauliX(1), qml.PauliY(0)] H1 = qml.Hamiltonian(coeffs1, obs1) diff --git a/tests/interfaces/test_autograd_qnode.py b/tests/interfaces/test_autograd_qnode.py index 1d6dcfe397b..f203041279f 100644 --- a/tests/interfaces/test_autograd_qnode.py +++ b/tests/interfaces/test_autograd_qnode.py @@ -69,7 +69,6 @@ pytestmark = pytest.mark.autograd TOL_FOR_SPSA = 1.0 -SEED_FOR_SPSA = 32651 H_FOR_SPSA = 0.01 @@ -128,7 +127,7 @@ def circuit(a): assert grad.shape == tuple() - def test_jacobian(self, interface, dev, diff_method, grad_on_execution, tol, device_vjp): + def test_jacobian(self, interface, dev, diff_method, grad_on_execution, tol, device_vjp, seed): """Test jacobian calculation""" kwargs = dict( diff_method=diff_method, @@ -138,7 +137,7 @@ def test_jacobian(self, interface, dev, diff_method, grad_on_execution, tol, dev ) if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA a = np.array(0.1, requires_grad=True) @@ -175,7 +174,7 @@ def cost(x, y): assert np.allclose(res[1], expected[1], atol=tol, rtol=0) def test_jacobian_no_evaluate( - self, interface, dev, diff_method, grad_on_execution, tol, device_vjp + self, interface, dev, diff_method, grad_on_execution, tol, device_vjp, seed ): """Test jacobian calculation when no prior circuit evaluation has been performed""" kwargs = dict( @@ -185,7 +184,7 @@ def test_jacobian_no_evaluate( device_vjp=device_vjp, ) if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA a = np.array(0.1, requires_grad=True) @@ -416,7 +415,7 @@ def circuit(data1): grad_fn(data1) def test_differentiable_expand( - self, interface, dev, diff_method, grad_on_execution, device_vjp, tol + self, interface, dev, diff_method, grad_on_execution, device_vjp, tol, seed ): """Test that operation and nested tape expansion is differentiable""" @@ -428,7 +427,7 @@ def test_differentiable_expand( ) if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) kwargs["num_directions"] = 10 tol = TOL_FOR_SPSA @@ -572,7 +571,7 @@ class TestQubitIntegration: """Tests that ensure various qubit circuits integrate correctly""" def test_probability_differentiation( - self, interface, dev, diff_method, grad_on_execution, device_vjp, tol + self, interface, dev, diff_method, grad_on_execution, device_vjp, tol, seed ): """Tests correct output shape and evaluation for a tape with a single prob output""" @@ -587,7 +586,7 @@ def test_probability_differentiation( ) if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA x = np.array(0.543, requires_grad=True) @@ -610,7 +609,7 @@ def circuit(x, y): assert all(np.allclose(r, e, atol=tol, rtol=0) for r, e in zip(res, expected)) def test_multiple_probability_differentiation( - self, interface, dev, diff_method, grad_on_execution, device_vjp, tol + self, interface, dev, diff_method, grad_on_execution, device_vjp, tol, seed ): """Tests correct output shape and evaluation for a tape with multiple prob outputs""" @@ -624,7 +623,7 @@ def test_multiple_probability_differentiation( ) if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA x = np.array(0.543, requires_grad=True) @@ -676,7 +675,7 @@ def cost(x, y): assert all(np.allclose(r, e, atol=tol, rtol=0) for r, e in zip(res, expected)) def test_ragged_differentiation( - self, interface, dev, diff_method, grad_on_execution, device_vjp, tol + self, interface, dev, diff_method, grad_on_execution, device_vjp, tol, seed ): """Tests correct output shape and evaluation for a tape with prob and expval outputs""" @@ -691,7 +690,7 @@ def test_ragged_differentiation( ) if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA x = np.array(0.543, requires_grad=True) @@ -728,7 +727,7 @@ def cost(x, y): assert np.allclose(res[1], expected[1], atol=tol, rtol=0) def test_ragged_differentiation_variance( - self, interface, dev, diff_method, grad_on_execution, device_vjp, tol + self, interface, dev, diff_method, grad_on_execution, device_vjp, tol, seed ): """Tests correct output shape and evaluation for a tape with prob and variance outputs""" @@ -742,7 +741,7 @@ def test_ragged_differentiation_variance( ) if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA elif diff_method == "hadamard": pytest.skip("Hadamard gradient does not support variances.") @@ -845,7 +844,7 @@ def cost(w1, w2): assert len(res) == 2 def test_chained_gradient_value( - self, interface, dev, diff_method, grad_on_execution, device_vjp, tol + self, interface, dev, diff_method, grad_on_execution, device_vjp, tol, seed ): """Test that the returned gradient value for two chained qubit QNodes is correct.""" @@ -857,7 +856,7 @@ def test_chained_gradient_value( ) if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA dev1 = qml.device("default.qubit") @@ -1375,7 +1374,7 @@ def cost_fn(x, y): @pytest.mark.parametrize("state", [[1], [0, 1]]) # Basis state and state vector def test_projector( - self, state, interface, dev, diff_method, grad_on_execution, device_vjp, tol + self, state, interface, dev, diff_method, grad_on_execution, device_vjp, tol, seed ): """Test that the variance of a projector is correctly returned""" if diff_method == "adjoint": @@ -1390,7 +1389,7 @@ def test_projector( ) if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA elif diff_method == "hadamard": pytest.skip("Hadamard gradient does not support variances.") @@ -1526,7 +1525,7 @@ def circuit(x, y): @pytest.mark.parametrize("max_diff", [1, 2]) def test_hamiltonian_expansion_analytic( - self, dev, diff_method, grad_on_execution, max_diff, tol, device_vjp + self, dev, diff_method, grad_on_execution, max_diff, tol, device_vjp, seed ): """Test that if there are non-commuting groups and the number of shots is None the first and second order gradients are correctly evaluated""" @@ -1540,7 +1539,7 @@ def test_hamiltonian_expansion_analytic( if diff_method in ["adjoint", "hadamard"]: pytest.skip("The diff method requested does not yet support Hamiltonians") elif diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) kwargs["num_directions"] = 10 tol = TOL_FOR_SPSA @@ -1596,7 +1595,7 @@ def circuit(data, weights, coeffs): @pytest.mark.slow @pytest.mark.parametrize("max_diff", [1, 2]) def test_hamiltonian_finite_shots( - self, dev, diff_method, grad_on_execution, max_diff, device_vjp + self, dev, diff_method, grad_on_execution, max_diff, device_vjp, seed ): """Test that the Hamiltonian is correctly measured if there are non-commuting groups and the number of shots is finite @@ -1608,7 +1607,7 @@ def test_hamiltonian_finite_shots( elif diff_method == "spsa": gradient_kwargs = { "h": H_FOR_SPSA, - "sampler_rng": np.random.default_rng(SEED_FOR_SPSA), + "sampler_rng": np.random.default_rng(seed), "num_directions": 10, } tol = TOL_FOR_SPSA diff --git a/tests/interfaces/test_jax.py b/tests/interfaces/test_jax.py index 561f5ab6512..97f8abaa79c 100644 --- a/tests/interfaces/test_jax.py +++ b/tests/interfaces/test_jax.py @@ -29,6 +29,12 @@ pytestmark = pytest.mark.jax +def get_device(device_name, seed): + if device_name == "param_shift.qubit": + return ParamShiftDerivativesDevice(seed=seed) + return qml.device(device_name, seed=seed) + + def test_jit_execution(): """Test that qml.execute can be directly jitted.""" dev = qml.device("default.qubit") @@ -124,20 +130,17 @@ def cost(x, cache): no_shots = Shots(None) shots_10k = Shots(10000) shots_2_10k = Shots((10000, 10000)) -dev_def = DefaultQubit(seed=42) -dev_ps = ParamShiftDerivativesDevice(seed=54353453) -dev_ref = qml.device("reference.qubit", seed=786345) test_matrix = [ - ({"gradient_fn": param_shift}, shots_10k, dev_def), # 0 - ({"gradient_fn": param_shift}, shots_2_10k, dev_def), # 1 - ({"gradient_fn": param_shift}, no_shots, dev_def), # 2 - ({"gradient_fn": "backprop"}, no_shots, dev_def), # 3 - ({"gradient_fn": "adjoint"}, no_shots, dev_def), # 4 - ({"gradient_fn": "adjoint", "device_vjp": True}, no_shots, dev_def), # 5 - ({"gradient_fn": "device"}, shots_2_10k, dev_ps), # 6 - ({"gradient_fn": param_shift}, no_shots, dev_ref), # 7 - ({"gradient_fn": param_shift}, shots_10k, dev_ref), # 8 - ({"gradient_fn": param_shift}, shots_2_10k, dev_ref), # 9 + ({"gradient_fn": param_shift}, shots_10k, "default.qubit"), # 0 + ({"gradient_fn": param_shift}, shots_2_10k, "default.qubit"), # 1 + ({"gradient_fn": param_shift}, no_shots, "default.qubit"), # 2 + ({"gradient_fn": "backprop"}, no_shots, "default.qubit"), # 3 + ({"gradient_fn": "adjoint"}, no_shots, "default.qubit"), # 4 + ({"gradient_fn": "adjoint", "device_vjp": True}, no_shots, "default.qubit"), # 5 + ({"gradient_fn": "device"}, shots_2_10k, "param_shift.qubit"), # 6 + ({"gradient_fn": param_shift}, no_shots, "reference.qubit"), # 7 + ({"gradient_fn": param_shift}, shots_10k, "reference.qubit"), # 8 + ({"gradient_fn": param_shift}, shots_2_10k, "reference.qubit"), # 9 ] @@ -146,14 +149,16 @@ def atol_for_shots(shots): return 3e-2 if shots else 1e-6 -@pytest.mark.parametrize("execute_kwargs, shots, device", test_matrix) +@pytest.mark.parametrize("execute_kwargs, shots, device_name", test_matrix) class TestJaxExecuteIntegration: """Test the jax interface execute function integrates well for both forward and backward execution""" - def test_execution(self, execute_kwargs, shots, device): + def test_execution(self, execute_kwargs, shots, device_name, seed): """Test execution""" + device = get_device(device_name, seed) + def cost(a, b): ops1 = [qml.RY(a, wires=0), qml.RX(b, wires=0)] tape1 = qml.tape.QuantumScript(ops1, [qml.expval(qml.PauliZ(0))], shots=shots) @@ -182,10 +187,12 @@ def cost(a, b): assert qml.math.allclose(res[0], jnp.cos(a) * jnp.cos(b), atol=atol_for_shots(shots)) assert qml.math.allclose(res[1], jnp.cos(a) * jnp.cos(b), atol=atol_for_shots(shots)) - def test_scalar_jacobian(self, execute_kwargs, shots, device): + def test_scalar_jacobian(self, execute_kwargs, shots, device_name, seed): """Test scalar jacobian calculation""" a = jnp.array(0.1) + device = get_device(device_name, seed) + def cost(a): tape = qml.tape.QuantumScript([qml.RY(a, 0)], [qml.expval(qml.PauliZ(0))], shots=shots) return execute([tape], device, **execute_kwargs)[0] @@ -204,12 +211,14 @@ def cost(a): assert np.allclose(res, expected, atol=atol_for_shots(shots), rtol=0) assert np.allclose(res, -jnp.sin(a), atol=atol_for_shots(shots)) - def test_jacobian(self, execute_kwargs, shots, device): + def test_jacobian(self, execute_kwargs, shots, device_name, seed): """Test jacobian calculation""" a = jnp.array(0.1) b = jnp.array(0.2) + device = get_device(device_name, seed) + def cost(a, b): ops = [qml.RY(a, wires=0), qml.RX(b, wires=1), qml.CNOT(wires=[0, 1])] m = [qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliY(1))] @@ -241,10 +250,12 @@ def cost(a, b): assert np.allclose(g[0][1], expected[1][0], atol=atol_for_shots(shots), rtol=0) assert np.allclose(g[1][1], expected[1][1], atol=atol_for_shots(shots), rtol=0) - def test_tape_no_parameters(self, execute_kwargs, shots, device): + def test_tape_no_parameters(self, execute_kwargs, shots, device_name, seed): """Test that a tape with no parameters is correctly ignored during the gradient computation""" + device = get_device(device_name, seed) + def cost(params): tape1 = qml.tape.QuantumScript( [qml.Hadamard(0)], [qml.expval(qml.PauliX(0))], shots=shots @@ -285,9 +296,11 @@ def cost(params): assert np.allclose(grad, expected, atol=atol_for_shots(shots), rtol=0) # pylint: disable=too-many-statements - def test_tapes_with_different_return_size(self, execute_kwargs, shots, device): + def test_tapes_with_different_return_size(self, execute_kwargs, shots, device_name, seed): """Test that tapes wit different can be executed and differentiated.""" + device = get_device(device_name, seed) + def cost(params): tape1 = qml.tape.QuantumScript( [qml.RY(params[0], 0), qml.RX(params[1], 0)], @@ -362,11 +375,13 @@ def cost(params): assert np.allclose(jac[0, 1], d2, atol=atol_for_shots(shots)) assert np.allclose(jac[3, 1], d2, atol=atol_for_shots(shots)) - def test_reusing_quantum_tape(self, execute_kwargs, shots, device): + def test_reusing_quantum_tape(self, execute_kwargs, shots, device_name, seed): """Test re-using a quantum tape by passing new parameters""" if execute_kwargs["gradient_fn"] == param_shift: pytest.skip("Basic QNode execution wipes out trainable params with param-shift") + device = get_device(device_name, seed) + a = jnp.array(0.1) b = jnp.array(0.2) @@ -414,12 +429,14 @@ def cost(a, b): for _j, _e in zip(jac, expected): assert np.allclose(_j, _e, atol=atol_for_shots(shots), rtol=0) - def test_classical_processing(self, execute_kwargs, shots, device): + def test_classical_processing(self, execute_kwargs, shots, device_name, seed): """Test classical processing within the quantum tape""" a = jnp.array(0.1) b = jnp.array(0.2) c = jnp.array(0.3) + device = get_device(device_name, seed) + def cost(a, b, c): ops = [ qml.RY(a * c, wires=0), @@ -440,12 +457,14 @@ def cost(a, b, c): # I tried getting analytic results for this circuit but I kept being wrong and am giving up - def test_matrix_parameter(self, execute_kwargs, device, shots): + def test_matrix_parameter(self, execute_kwargs, device_name, seed, shots): """Test that the jax interface works correctly with a matrix parameter""" U = jnp.array([[0, 1], [1, 0]]) a = jnp.array(0.1) + device = get_device(device_name, seed) + def cost(a, U): ops = [qml.QubitUnitary(U, wires=0), qml.RY(a, wires=0)] tape = qml.tape.QuantumScript(ops, [qml.expval(qml.PauliZ(0))], shots=shots) @@ -460,10 +479,12 @@ def cost(a, U): assert isinstance(jac, jnp.ndarray) assert np.allclose(jac, jnp.sin(a), atol=atol_for_shots(shots), rtol=0) - def test_differentiable_expand(self, execute_kwargs, device, shots): + def test_differentiable_expand(self, execute_kwargs, device_name, seed, shots): """Test that operation and nested tapes expansion is differentiable""" + device = get_device(device_name, seed) + class U3(qml.U3): """Dummy operator.""" @@ -520,10 +541,12 @@ def cost_fn(a, p): ) assert np.allclose(res, expected, atol=atol_for_shots(shots), rtol=0) - def test_probability_differentiation(self, execute_kwargs, device, shots): + def test_probability_differentiation(self, execute_kwargs, device_name, seed, shots): """Tests correct output shape and evaluation for a tape with prob outputs""" + device = get_device(device_name, seed) + def cost(x, y): ops = [qml.RX(x, 0), qml.RY(y, 1), qml.CNOT((0, 1))] m = [qml.probs(wires=0), qml.probs(wires=1)] @@ -583,10 +606,12 @@ def cost(x, y): assert np.allclose(res[0], expected[0], atol=atol_for_shots(shots), rtol=0) assert np.allclose(res[1], expected[1], atol=atol_for_shots(shots), rtol=0) - def test_ragged_differentiation(self, execute_kwargs, device, shots): + def test_ragged_differentiation(self, execute_kwargs, device_name, seed, shots): """Tests correct output shape and evaluation for a tape with prob and expval outputs""" + device = get_device(device_name, seed) + def cost(x, y): ops = [qml.RX(x, wires=0), qml.RY(y, 1), qml.CNOT((0, 1))] m = [qml.expval(qml.PauliZ(0)), qml.probs(wires=1)] @@ -704,16 +729,18 @@ def cost_fn(x): assert np.allclose(res, expected, atol=tol, rtol=0) -@pytest.mark.parametrize("execute_kwargs, shots, device", test_matrix) +@pytest.mark.parametrize("execute_kwargs, shots, device_name", test_matrix) @pytest.mark.usefixtures("use_legacy_and_new_opmath") class TestHamiltonianWorkflows: """Test that tapes ending with expectations of Hamiltonians provide correct results and gradients""" @pytest.fixture - def cost_fn(self, execute_kwargs, shots, device): + def cost_fn(self, execute_kwargs, shots, device_name, seed): """Cost function for gradient tests""" + device = get_device(device_name, seed) + def _cost_fn(weights, coeffs1, coeffs2): obs1 = [qml.PauliZ(0), qml.PauliZ(0) @ qml.PauliX(1), qml.PauliY(0)] H1 = qml.Hamiltonian(coeffs1, obs1) diff --git a/tests/interfaces/test_jax_jit_qnode.py b/tests/interfaces/test_jax_jit_qnode.py index ea30329106c..fdb4094160d 100644 --- a/tests/interfaces/test_jax_jit_qnode.py +++ b/tests/interfaces/test_jax_jit_qnode.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. """Integration tests for using the JAX-JIT interface with a QNode""" -import copy # pylint: disable=too-many-arguments,too-few-public-methods,protected-access from functools import partial @@ -25,27 +24,33 @@ from pennylane import qnode from pennylane.devices import DefaultQubit -# device, diff_method, grad_on_execution, device_vjp -qubit_device_and_diff_method = [ - [DefaultQubit(seed=123), "backprop", True, False], - [DefaultQubit(seed=123), "finite-diff", False, False], - [DefaultQubit(seed=123), "parameter-shift", False, False], - [DefaultQubit(seed=123), "adjoint", True, False], - [DefaultQubit(seed=123), "adjoint", True, True], - [ParamShiftDerivativesDevice(seed=123), "device", False, True], - [DefaultQubit(seed=123), "adjoint", False, False], - [DefaultQubit(seed=123), "spsa", False, False], - [DefaultQubit(seed=123), "hadamard", False, False], - [qml.device("lightning.qubit", wires=5), "adjoint", False, True], - [qml.device("lightning.qubit", wires=5), "adjoint", True, False], - [qml.device("lightning.qubit", wires=5), "adjoint", False, False], - [qml.device("lightning.qubit", wires=5), "adjoint", True, True], - [qml.device("lightning.qubit", wires=5), "parameter-shift", False, False], - [qml.device("reference.qubit", seed=123), "parameter-shift", False, False], + +def get_device(device_name, wires, seed): + if device_name == "param_shift.qubit": + return ParamShiftDerivativesDevice(seed=seed) + if device_name == "lightning.qubit": + return qml.device("lightning.qubit", wires=wires) + return qml.device(device_name, seed=seed) + + +# device_name, diff_method, grad_on_execution, device_vjp +device_test_cases = [ + ("default.qubit", "backprop", True, False), + ("default.qubit", "finite-diff", False, False), + ("default.qubit", "parameter-shift", False, False), + ("default.qubit", "adjoint", True, False), + ("default.qubit", "adjoint", True, True), + ("default.qubit", "adjoint", False, False), + ("default.qubit", "spsa", False, False), + ("default.qubit", "hadamard", False, False), + ("param_shift.qubit", "device", False, True), + ("lightning.qubit", "adjoint", False, True), + ("lightning.qubit", "adjoint", True, True), + ("lightning.qubit", "adjoint", False, False), + ("lightning.qubit", "adjoint", True, False), + ("lightning.qubit", "parameter-shift", False, False), + ("reference.qubit", "parameter-shift", False, False), ] -interface_and_qubit_device_and_diff_method = [ - ["auto"] + inner_list for inner_list in qubit_device_and_diff_method -] + [["jax-jit"] + inner_list for inner_list in qubit_device_and_diff_method] pytestmark = pytest.mark.jax @@ -53,25 +58,28 @@ jax.config.update("jax_enable_x64", True) TOL_FOR_SPSA = 1.0 -SEED_FOR_SPSA = 32651 H_FOR_SPSA = 0.05 +@pytest.mark.parametrize("interface", ["auto", "jax-jit"]) @pytest.mark.parametrize( - "interface,dev,diff_method,grad_on_execution,device_vjp", - interface_and_qubit_device_and_diff_method, + "dev_name,diff_method,grad_on_execution,device_vjp", + device_test_cases, ) class TestQNode: """Test that using the QNode with JAX integrates with the PennyLane stack""" def test_execution_with_interface( - self, dev, diff_method, grad_on_execution, interface, device_vjp + self, interface, dev_name, diff_method, grad_on_execution, device_vjp, seed ): """Test execution works with the interface""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention") + dev = get_device(dev_name, wires=1, seed=seed) + @qnode( dev, interface=interface, @@ -98,10 +106,11 @@ def circuit(a): assert grad.shape == () def test_changing_trainability( - self, dev, diff_method, grad_on_execution, interface, device_vjp, tol + self, interface, dev_name, diff_method, grad_on_execution, device_vjp, tol, seed ): """Test changing the trainability of parameters changes the number of differentiation requests made""" + if diff_method != "parameter-shift": pytest.skip("Test only supports parameter-shift") @@ -109,7 +118,7 @@ def test_changing_trainability( b = jax.numpy.array(0.2) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method="parameter-shift", grad_on_execution=grad_on_execution, @@ -146,17 +155,20 @@ def circuit(a, b): circuit(a, b) assert circuit.qtape.trainable_params == [1] - def test_classical_processing(self, dev, diff_method, grad_on_execution, device_vjp, interface): + def test_classical_processing( + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, seed + ): """Test classical processing within the quantum tape""" + + if dev_name == "param_shift.qubit": + pytest.xfail("gradient transforms have a different vjp shape convention.") + a = jax.numpy.array(0.1) b = jax.numpy.array(0.2) c = jax.numpy.array(0.3) - if dev.name == "param_shift.qubit": - pytest.xfail("gradient transforms have a different vjp shape convention.") - @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -176,19 +188,19 @@ def circuit(a, b, c): assert len(res) == 2 def test_matrix_parameter( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Test that the jax interface works correctly with a matrix parameter""" - if dev.name == "param_shift.qubit": + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") U = jax.numpy.array([[0, 1], [1, 0]]) a = jax.numpy.array(0.1) @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -206,17 +218,17 @@ def circuit(U, a): assert circuit.qtape.trainable_params == [1] def test_differentiable_expand( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Test that operation and nested tape expansion is differentiable""" - if dev.name == "param_shift.qubit": + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") gradient_kwargs = {} if diff_method == "spsa": - gradient_kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + gradient_kwargs["sampler_rng"] = np.random.default_rng(seed) gradient_kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA @@ -233,7 +245,7 @@ def decomposition(self): p = jax.numpy.array([0.1, 0.2, 0.3]) @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -264,14 +276,18 @@ def circuit(a, p): ) assert np.allclose(res, expected, atol=tol, rtol=0) - def test_jacobian_options(self, dev, diff_method, grad_on_execution, device_vjp, interface): + def test_jacobian_options( + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, seed + ): """Test setting jacobian options""" + if diff_method != "finite-diff": pytest.skip("Test only applies to finite diff.") + a = np.array([0.1, 0.2], requires_grad=True) @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), interface=interface, diff_method="finite-diff", h=1e-8, @@ -291,26 +307,30 @@ def circuit(a): jax.jit(jax.jacobian(circuit))(a) +@pytest.mark.parametrize("interface", ["auto", "jax-jit"]) @pytest.mark.parametrize( - "interface,dev,diff_method,grad_on_execution, device_vjp", - interface_and_qubit_device_and_diff_method, + "dev_name,diff_method,grad_on_execution, device_vjp", + device_test_cases, ) class TestVectorValuedQNode: """Test that using vector-valued QNodes with JAX integrate with the PennyLane stack""" def test_diff_expval_expval( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Test jacobian calculation""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") - if "lightning" in dev.name: + + if dev_name == "lightning.qubit": pytest.xfail("lightning does not support device vjps with jax jacobians.") + gradient_kwargs = {} if diff_method == "spsa": - gradient_kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + gradient_kwargs["sampler_rng"] = np.random.default_rng(seed) gradient_kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA @@ -318,7 +338,7 @@ def test_diff_expval_expval( b = np.array(0.2, requires_grad=True) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -365,17 +385,20 @@ def circuit(a, b): assert np.allclose(res[1][1], expected[1][1], atol=tol, rtol=0) def test_jacobian_no_evaluate( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Test jacobian calculation when no prior circuit evaluation has been performed""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") - if "lightning" in dev.name: + + if dev_name == "lightning.qubit": pytest.xfail("lightning does not support device vjps with jax jacobians.") + gradient_kwargs = {} if diff_method == "spsa": - gradient_kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + gradient_kwargs["sampler_rng"] = np.random.default_rng(seed) gradient_kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA @@ -383,7 +406,7 @@ def test_jacobian_no_evaluate( b = jax.numpy.array(0.2) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -429,17 +452,20 @@ def circuit(a, b): assert np.allclose(r, e, atol=tol, rtol=0) def test_diff_single_probs( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Tests correct output shape and evaluation for a tape with a single prob output""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") - if "lightning" in dev.name: + + if dev_name == "lightning.qubit": pytest.xfail("lightning does not support device vjps with jax jacobians.") + gradient_kwargs = {} if diff_method == "spsa": - gradient_kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + gradient_kwargs["sampler_rng"] = np.random.default_rng(seed) gradient_kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA @@ -447,7 +473,7 @@ def test_diff_single_probs( y = jax.numpy.array(-0.654) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -482,17 +508,20 @@ def circuit(x, y): assert np.allclose(res[1], expected.T[1], atol=tol, rtol=0) def test_diff_multi_probs( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Tests correct output shape and evaluation for a tape with multiple prob outputs""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") - if "lightning" in dev.name: + + if dev_name == "lightning.qubit": pytest.xfail("lightning does not support device vjps with jax jacobians.") + gradient_kwargs = {} if diff_method == "spsa": - gradient_kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + gradient_kwargs["sampler_rng"] = np.random.default_rng(seed) gradient_kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA @@ -500,7 +529,7 @@ def test_diff_multi_probs( y = jax.numpy.array(-0.654) @qnode( - dev, + get_device(dev_name, wires=3, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -568,17 +597,20 @@ def circuit(x, y): assert np.allclose(jac[1][1], expected_1[1], atol=tol, rtol=0) def test_diff_expval_probs( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Tests correct output shape and evaluation for a tape with prob and expval outputs""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") - if "lightning" in dev.name: + + if dev_name == "lightning.qubit": pytest.xfail("lightning does not support device vjps with jax jacobians.") + gradient_kwargs = {} if diff_method == "spsa": - gradient_kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + gradient_kwargs["sampler_rng"] = np.random.default_rng(seed) gradient_kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA @@ -586,7 +618,7 @@ def test_diff_expval_probs( y = jax.numpy.array(-0.654) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -644,24 +676,27 @@ def circuit(x, y): assert np.allclose(jac[1][1], expected[1][1], atol=tol, rtol=0) def test_diff_expval_probs_sub_argnums( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Tests correct output shape and evaluation for a tape with prob and expval outputs with less trainable parameters (argnums) than parameters.""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") - if "lightning" in dev.name: + + if dev_name == "lightning.qubit": pytest.xfail("lightning does not support device vjps with jax jacobians.") + kwargs = {} if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA x = jax.numpy.array(0.543) y = jax.numpy.array(-0.654) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -698,18 +733,23 @@ def circuit(x, y): assert jac[1][0].shape == (2,) assert np.allclose(jac[1][0], expected[1][0], atol=tol, rtol=0) - def test_diff_var_probs(self, dev, diff_method, grad_on_execution, device_vjp, interface, tol): + def test_diff_var_probs( + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed + ): """Tests correct output shape and evaluation for a tape with prob and variance outputs""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") - if "lightning" in dev.name: + + if dev_name == "lightning.qubit": pytest.xfail("lightning does not support device vjps with jax jacobians.") + gradient_kwargs = {} if diff_method == "hadamard": pytest.skip("Hadamard does not support var") elif diff_method == "spsa": - gradient_kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + gradient_kwargs["sampler_rng"] = np.random.default_rng(seed) gradient_kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA @@ -717,7 +757,7 @@ def test_diff_var_probs(self, dev, diff_method, grad_on_execution, device_vjp, i y = jax.numpy.array(-0.654) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -864,10 +904,10 @@ def cost_fn(a, b): assert spy.call_args[1]["gradient_fn"] == "backprop" @pytest.mark.parametrize("shots", [(10000, 10000), (10000, 10005)]) - def test_shot_vectors_single_measurements(self, interface, shots): + def test_shot_vectors_single_measurements(self, interface, shots, seed): """Test jax-jit can work with shot vectors.""" - dev = qml.device("default.qubit", shots=shots, seed=4747) + dev = qml.device("default.qubit", shots=shots, seed=seed) @jax.jit @qml.qnode(dev, interface=interface, diff_method="parameter-shift") @@ -877,20 +917,19 @@ def circuit(x): res = circuit(0.5) expected = 1 - np.cos(0.5) ** 2 - assert qml.math.allclose(res[0], expected, atol=1e-2) - assert qml.math.allclose(res[1], expected, atol=3e-2) + assert qml.math.allclose(res[0], expected, atol=5e-2) + assert qml.math.allclose(res[1], expected, rtol=5e-2) g = jax.jacobian(circuit)(0.5) - expected_g = 2 * np.cos(0.5) * np.sin(0.5) - assert qml.math.allclose(g[0], expected_g, atol=2e-2) - assert qml.math.allclose(g[1], expected_g, atol=2e-2) + assert qml.math.allclose(g[0], expected_g, rtol=5e-2) + assert qml.math.allclose(g[1], expected_g, rtol=5e-2) @pytest.mark.parametrize("shots", [(10000, 10000), (10000, 10005)]) - def test_shot_vectors_multiple_measurements(self, interface, shots): + def test_shot_vectors_multiple_measurements(self, interface, shots, seed): """Test jax-jit can work with shot vectors.""" - dev = qml.device("default.qubit", shots=shots, seed=987548) + dev = qml.device("default.qubit", shots=shots, seed=seed) @jax.jit @qml.qnode(dev, interface=interface, diff_method="parameter-shift") @@ -899,23 +938,27 @@ def circuit(x): return qml.expval(qml.PauliZ(0)), qml.probs(wires=0) res = circuit(0.5) - assert qml.math.allclose(res[0][0], np.cos(0.5), atol=5e-3) - assert qml.math.allclose(res[1][0], np.cos(0.5), atol=5e-3) + assert qml.math.allclose(res[0][0], np.cos(0.5), rtol=5e-2) + assert qml.math.allclose(res[1][0], np.cos(0.5), rtol=5e-2) + expected_probs = np.array([np.cos(0.25) ** 2, np.sin(0.25) ** 2]) - assert qml.math.allclose(res[0][1], expected_probs, atol=5e-3) - assert qml.math.allclose(res[1][1], expected_probs, atol=5e-3) + assert qml.math.allclose(res[0][1], expected_probs, rtol=5e-2) + assert qml.math.allclose(res[1][1][0], expected_probs[0], rtol=5e-2) + assert qml.math.allclose(res[1][1][1], expected_probs[1], atol=5e-3) +@pytest.mark.parametrize("interface", ["auto", "jax-jit"]) @pytest.mark.parametrize( - "interface,dev,diff_method,grad_on_execution, device_vjp", - interface_and_qubit_device_and_diff_method, + "dev_name,diff_method,grad_on_execution, device_vjp", + device_test_cases, ) class TestQubitIntegration: """Tests that ensure various qubit circuits integrate correctly""" @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") - def test_sampling(self, dev, diff_method, grad_on_execution, device_vjp, interface): + def test_sampling(self, dev_name, diff_method, grad_on_execution, device_vjp, interface, seed): """Test sampling works as expected""" + if grad_on_execution: pytest.skip("Sampling not possible with forward grad_on_execution differentiation.") @@ -923,7 +966,7 @@ def test_sampling(self, dev, diff_method, grad_on_execution, device_vjp, interfa pytest.skip("Adjoint warns with finite shots") @qml.qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -944,19 +987,17 @@ def circuit(): assert res[1].shape == (10,) @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") - def test_counts(self, dev, diff_method, grad_on_execution, device_vjp, interface): + def test_counts(self, dev_name, diff_method, grad_on_execution, device_vjp, interface, seed): """Test counts works as expected""" + if grad_on_execution: pytest.skip("Sampling not possible with forward grad_on_execution differentiation.") if diff_method == "adjoint": pytest.skip("Adjoint warns with finite shots") - if isinstance(dev, qml.devices.DefaultQubit): - dev._rng = np.random.default_rng(987654321) - @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -982,15 +1023,20 @@ def circuit(): assert isinstance(res[1], dict) assert len(res[1]) == 2 - def test_chained_qnodes(self, dev, diff_method, grad_on_execution, device_vjp, interface): + def test_chained_qnodes( + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, seed + ): """Test that the gradient of chained QNodes works without error""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") class Template(qml.templates.StronglyEntanglingLayers): def decomposition(self): return [qml.templates.StronglyEntanglingLayers(*self.parameters, self.wires)] + dev = get_device(dev_name, wires=2, seed=seed) + @qnode( dev, interface=interface, @@ -1034,19 +1080,21 @@ def cost(weights): assert len(res) == 2 def test_postselection_differentiation( - self, dev, diff_method, grad_on_execution, device_vjp, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, seed ): """Test that when postselecting with default.qubit, differentiation works correctly.""" if diff_method in ["adjoint", "spsa", "hadamard"]: pytest.skip("Diff method does not support postselection.") - if dev.name == "param_shift.qubit": + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention") - elif dev.name == "lightning.qubit": + elif dev_name == "lightning.qubit": pytest.xfail("lightning qubit does not support postselection.") - if dev.name == "reference.qubit": + if dev_name == "reference.qubit": pytest.skip("reference.qubit does not support postselection.") + dev = get_device(dev_name, wires=2, seed=seed) + @qml.qnode( dev, diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution ) @@ -1079,26 +1127,29 @@ def expected_circuit(theta): assert np.allclose(gradient, [0.0, exp_theta_grad]) +@pytest.mark.parametrize("interface", ["auto", "jax-jit"]) @pytest.mark.parametrize( - "interface,dev,diff_method,grad_on_execution, device_vjp", - interface_and_qubit_device_and_diff_method, + "dev_name,diff_method,grad_on_execution,device_vjp", + device_test_cases, ) class TestQubitIntegrationHigherOrder: """Tests that ensure various qubit circuits integrate correctly when computing higher-order derivatives""" def test_second_derivative( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Test second derivative calculation of a scalar-valued QNode""" gradient_kwargs = {} if diff_method in {"adjoint", "device"}: pytest.skip("Adjoint does not support second derivatives.") elif diff_method == "spsa": - gradient_kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + gradient_kwargs["sampler_rng"] = np.random.default_rng(seed) gradient_kwargs["num_directions"] = 20 gradient_kwargs["h"] = H_FOR_SPSA tol = TOL_FOR_SPSA + dev = get_device(dev_name, wires=1, seed=seed) + @qnode( dev, diff_method=diff_method, @@ -1135,7 +1186,9 @@ def circuit(x): else: assert np.allclose(g2, expected_g2, atol=tol, rtol=0) - def test_hessian(self, dev, diff_method, grad_on_execution, device_vjp, interface, tol): + def test_hessian( + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed + ): """Test hessian calculation of a scalar-valued QNode""" gradient_kwargs = {} if diff_method in {"adjoint", "device"}: @@ -1144,10 +1197,12 @@ def test_hessian(self, dev, diff_method, grad_on_execution, device_vjp, interfac gradient_kwargs = { "h": H_FOR_SPSA, "num_directions": 40, - "sampler_rng": np.random.default_rng(SEED_FOR_SPSA), + "sampler_rng": np.random.default_rng(seed), } tol = TOL_FOR_SPSA + dev = get_device(dev_name, wires=1, seed=seed) + @qnode( dev, diff_method=diff_method, @@ -1188,7 +1243,7 @@ def circuit(x): assert np.allclose(hess, expected_hess, atol=tol, rtol=0) def test_hessian_vector_valued( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Test hessian calculation of a vector-valued QNode""" gradient_kwargs = {} @@ -1198,12 +1253,12 @@ def test_hessian_vector_valued( gradient_kwargs = { "h": H_FOR_SPSA, "num_directions": 20, - "sampler_rng": np.random.default_rng(SEED_FOR_SPSA), + "sampler_rng": np.random.default_rng(seed), } tol = TOL_FOR_SPSA @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -1251,7 +1306,7 @@ def circuit(x): assert np.allclose(hess, expected_hess, atol=tol, rtol=0) def test_hessian_vector_valued_postprocessing( - self, dev, diff_method, interface, device_vjp, grad_on_execution, tol + self, dev_name, diff_method, interface, device_vjp, grad_on_execution, tol, seed ): """Test hessian calculation of a vector valued QNode with post-processing""" gradient_kwargs = {} @@ -1261,12 +1316,12 @@ def test_hessian_vector_valued_postprocessing( gradient_kwargs = { "h": H_FOR_SPSA, "num_directions": 20, - "sampler_rng": np.random.default_rng(SEED_FOR_SPSA), + "sampler_rng": np.random.default_rng(seed), } tol = TOL_FOR_SPSA @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -1317,7 +1372,7 @@ def cost_fn(x): assert np.allclose(hess, expected_hess, atol=tol, rtol=0) def test_hessian_vector_valued_separate_args( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Test hessian calculation of a vector valued QNode that has separate input arguments""" gradient_kwargs = {} @@ -1327,12 +1382,12 @@ def test_hessian_vector_valued_separate_args( gradient_kwargs = { "h": H_FOR_SPSA, "num_directions": 20, - "sampler_rng": np.random.default_rng(SEED_FOR_SPSA), + "sampler_rng": np.random.default_rng(seed), } tol = TOL_FOR_SPSA @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -1382,17 +1437,18 @@ def circuit(a, b): else: assert np.allclose(hess, expected_hess, atol=tol, rtol=0) - def test_state(self, dev, diff_method, grad_on_execution, device_vjp, interface, tol): + def test_state( + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed + ): """Test that the state can be returned and differentiated""" - if dev.name == "lightning.qubit" and diff_method == "adjoint": + if dev_name == "lightning.qubit" and diff_method == "adjoint": pytest.xfail("lightning.qubit does not support adjoint with the state.") + dev = get_device(dev_name, wires=2, seed=seed) + x = jax.numpy.array(0.543) y = jax.numpy.array(-0.654) - if not dev.wires: - dev = copy.copy(dev) - dev._wires = qml.wires.Wires([0, 1]) # pylint:disable=protected-access @qnode( dev, @@ -1424,27 +1480,27 @@ def cost_fn(x, y): @pytest.mark.parametrize("state", [[1], [0, 1]]) # Basis state and state vector def test_projector( - self, state, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, state, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Test that the variance of a projector is correctly returned""" gradient_kwargs = {} - if dev.name == "param_shift.qubit": + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") if diff_method == "adjoint": pytest.skip("Adjoint does not support projectors") elif diff_method == "hadamard": pytest.skip("Hadamard does not support var") elif diff_method == "spsa": - gradient_kwargs = {"h": H_FOR_SPSA, "sampler_rng": np.random.default_rng(SEED_FOR_SPSA)} + gradient_kwargs = {"h": H_FOR_SPSA, "sampler_rng": np.random.default_rng(seed)} tol = TOL_FOR_SPSA - if dev.name == "reference.qubit": + if dev_name == "reference.qubit": pytest.xfail("diagonalize_measurements do not support projectors (sc-72911)") P = jax.numpy.array(state) x, y = 0.765, -0.654 @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -1471,9 +1527,10 @@ def circuit(x, y): assert np.allclose(res, expected, atol=tol, rtol=0) +@pytest.mark.parametrize("interface", ["auto", "jax-jit"]) @pytest.mark.parametrize( - "interface,dev,diff_method,grad_on_execution, device_vjp", - interface_and_qubit_device_and_diff_method, + "dev_name,diff_method,grad_on_execution, device_vjp", + device_test_cases, ) class TestTapeExpansion: """Test that tape expansion within the QNode integrates correctly @@ -1481,10 +1538,11 @@ class TestTapeExpansion: @pytest.mark.parametrize("max_diff", [1, 2]) def test_gradient_expansion_trainable_only( - self, dev, diff_method, grad_on_execution, device_vjp, max_diff, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, max_diff, interface, seed ): """Test that a *supported* operation with no gradient recipe is only expanded for parameter-shift and finite-differences when it is trainable.""" + if diff_method not in ("parameter-shift", "finite-diff", "spsa"): pytest.skip("Only supports gradient transforms") @@ -1495,7 +1553,7 @@ def decomposition(self): return [qml.RY(3 * self.data[0], wires=self.wires)] @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), diff_method=diff_method, grad_on_execution=grad_on_execution, max_diff=max_diff, @@ -1516,18 +1574,28 @@ def circuit(x, y): @pytest.mark.parametrize("max_diff", [1, 2]) def test_hamiltonian_expansion_analytic( - self, dev, diff_method, grad_on_execution, max_diff, device_vjp, interface, mocker, tol + self, + dev_name, + diff_method, + grad_on_execution, + max_diff, + device_vjp, + interface, + mocker, + tol, + seed, ): """Test that the Hamiltonian is not expanded if there are non-commuting groups and the number of shots is None and the first and second order gradients are correctly evaluated""" + gradient_kwargs = {} - if dev.name == "reference.qubit": + if dev_name == "reference.qubit": pytest.skip( "Cannot add transform to the transform program in preprocessing" "when using mocker.spy on it." ) - if dev.name == "param_shift.qubit": + if dev_name == "param_shift.qubit": pytest.xfail("gradients transforms have a different vjp shape convention.") if diff_method == "adjoint": pytest.skip("The adjoint method does not yet support Hamiltonians") @@ -1537,7 +1605,7 @@ def test_hamiltonian_expansion_analytic( gradient_kwargs = { "h": H_FOR_SPSA, "num_directions": 20, - "sampler_rng": np.random.default_rng(SEED_FOR_SPSA), + "sampler_rng": np.random.default_rng(seed), } tol = TOL_FOR_SPSA @@ -1546,7 +1614,7 @@ def test_hamiltonian_expansion_analytic( @jax.jit @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1596,27 +1664,27 @@ def circuit(data, weights, coeffs): @pytest.mark.parametrize("max_diff", [1, 2]) def test_hamiltonian_finite_shots( - self, dev, diff_method, grad_on_execution, device_vjp, interface, max_diff + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, max_diff, seed ): """Test that the Hamiltonian is correctly measured if there are non-commuting groups and the number of shots is finite and the first and second order gradients are correctly evaluated""" gradient_kwargs = {} tol = 0.3 - if dev.name == "param_shift.qubit": + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") if diff_method in ("adjoint", "backprop", "finite-diff"): pytest.skip("The adjoint and backprop methods do not yet support sampling") elif diff_method == "hadamard": pytest.skip("The Hadamard method does not yet support Hamiltonians") elif diff_method == "spsa": - gradient_kwargs = {"sampler_rng": SEED_FOR_SPSA, "h": H_FOR_SPSA, "num_directions": 20} + gradient_kwargs = {"sampler_rng": seed, "h": H_FOR_SPSA, "num_directions": 20} tol = TOL_FOR_SPSA obs = [qml.PauliX(0), qml.PauliX(0) @ qml.PauliZ(1), qml.PauliZ(0) @ qml.PauliZ(1)] @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1667,12 +1735,12 @@ def circuit(data, weights, coeffs): # assert np.allclose(grad2_w_c, expected, atol=tol) def test_vmap_compared_param_broadcasting( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Test that jax.vmap works just as well as parameter-broadcasting with JAX JIT on the forward pass when vectorized=True is specified for the callback when caching is disabled.""" if ( - dev.name == "default.qubit" + dev_name == "default.qubit" and diff_method == "adjoint" and grad_on_execution and not device_vjp @@ -1685,7 +1753,7 @@ def test_vmap_compared_param_broadcasting( def minimal_circ(params): @qml.qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1705,13 +1773,13 @@ def _measure_operator(): assert np.allclose(res1, res2, tol) def test_vmap_compared_param_broadcasting_multi_output( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Test that jax.vmap works just as well as parameter-broadcasting with JAX JIT on the forward pass when vectorized=True is specified for the callback when caching is disabled and when multiple output values are returned.""" if ( - dev.name == "default.qubit" + dev_name == "default.qubit" and diff_method == "adjoint" and grad_on_execution and not device_vjp @@ -1724,7 +1792,7 @@ def test_vmap_compared_param_broadcasting_multi_output( def minimal_circ(params): @qml.qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1745,19 +1813,19 @@ def _measure_operator(): assert np.allclose(res2, vres2, tol) def test_vmap_compared_param_broadcasting_probs( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Test that jax.vmap works just as well as parameter-broadcasting with JAX JIT on the forward pass when vectorized=True is specified for the callback when caching is disabled and when multiple output values are returned.""" if ( - dev.name == "default.qubit" + dev_name == "default.qubit" and diff_method == "adjoint" and grad_on_execution and not device_vjp ): pytest.xfail("adjoint is incompatible with parameter broadcasting.") - elif dev.name == "lightning.qubit" and diff_method == "adjoint": + elif dev_name == "lightning.qubit" and diff_method == "adjoint": pytest.xfail("lightning adjoign cannot differentiate probabilities.") interface = "jax-jit" @@ -1766,7 +1834,7 @@ def test_vmap_compared_param_broadcasting_probs( def minimal_circ(params): @qml.qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1790,32 +1858,33 @@ def _measure_operator(): jacobian_fn = [jax.jacobian, jax.jacrev, jax.jacfwd] +@pytest.mark.parametrize("interface", ["auto", "jax-jit"]) @pytest.mark.parametrize("jacobian", jacobian_fn) @pytest.mark.parametrize( - "interface,dev,diff_method,grad_on_execution, device_vjp", - interface_and_qubit_device_and_diff_method, + "dev_name,diff_method,grad_on_execution,device_vjp", + device_test_cases, ) class TestJIT: """Test JAX JIT integration with the QNode and automatic resolution of the correct JAX interface variant.""" def test_gradient( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, tol, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, tol, interface, seed ): """Test derivative calculation of a scalar valued QNode""" gradient_kwargs = {} - if dev.name == "param_shift.qubit": + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") - if "lightning" in dev.name: + if "lightning" in dev_name: pytest.xfail("lightning device vjps are not compatible with jax jaocbians") if device_vjp and jacobian == jax.jacfwd: pytest.skip("device vjps not compatible with forward diff.") elif diff_method == "spsa": - gradient_kwargs = {"h": H_FOR_SPSA, "sampler_rng": np.random.default_rng(SEED_FOR_SPSA)} + gradient_kwargs = {"h": H_FOR_SPSA, "sampler_rng": np.random.default_rng(seed)} tol = TOL_FOR_SPSA @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -1844,7 +1913,7 @@ def circuit(x): ) @pytest.mark.parametrize("shots", [10, 1000]) def test_hermitian( - self, dev, diff_method, grad_on_execution, device_vjp, shots, jacobian, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, shots, jacobian, interface, seed ): """Test that the jax device works with qml.Hermitian and jitting even when shots>0. @@ -1853,7 +1922,7 @@ def test_hermitian( to different reasons, hence the parametrization in the test. """ # pylint: disable=unused-argument - if dev.name == "reference.qubit": + if dev_name == "reference.qubit": pytest.xfail("diagonalize_measurements do not support Hermitians (sc-72911)") if diff_method == "backprop": @@ -1865,7 +1934,7 @@ def test_hermitian( projector = np.array(qml.matrix(qml.PauliZ(0) @ qml.PauliZ(1))) @qml.qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1882,7 +1951,7 @@ def circ(projector): ) @pytest.mark.parametrize("shots", [10, 1000]) def test_probs_obs_none( - self, dev, diff_method, grad_on_execution, device_vjp, shots, jacobian, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, shots, jacobian, interface, seed ): """Test that the jax device works with qml.probs, a MeasurementProcess that has obs=None even when shots>0.""" @@ -1891,7 +1960,7 @@ def test_probs_obs_none( pytest.skip("Backpropagation is unsupported if shots > 0.") @qml.qnode( - dev, + get_device(dev_name, wires=1, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1902,17 +1971,16 @@ def circuit(): assert jax.numpy.allclose(circuit(), jax.numpy.array([1.0, 0.0])) - # @pytest.mark.xfail( - # reason="Non-trainable parameters are not being correctly unwrapped by the interface" - # ) def test_gradient_subset( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, tol, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, tol, interface, seed ): """Test derivative calculation of a scalar valued QNode with respect to a subset of arguments""" + if diff_method == "spsa" and not grad_on_execution and not device_vjp: pytest.xfail(reason="incorrect jacobian results") - if "lightning" in dev.name: + + if "lightning" in dev_name: pytest.xfail("lightning device vjps are not compatible with jax jaocbians") if diff_method == "device" and not grad_on_execution and device_vjp: @@ -1925,7 +1993,7 @@ def test_gradient_subset( b = jax.numpy.array(0.2) @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -1946,23 +2014,28 @@ def circuit(a, b, c): assert np.allclose(g, expected_g, atol=tol, rtol=0) def test_gradient_scalar_cost_vector_valued_qnode( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, tol, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, tol, interface, seed ): """Test derivative calculation of a scalar valued cost function that uses the output of a vector-valued QNode""" + gradient_kwargs = {} - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") - if "lightning" in dev.name: + + if "lightning" in dev_name: pytest.xfail("lightning device vjps are not compatible with jax jaocbians") + elif jacobian == jax.jacfwd and device_vjp: pytest.skip("device vjps are not compatible with forward differentiation.") + elif diff_method == "spsa": - gradient_kwargs = {"h": H_FOR_SPSA, "sampler_rng": np.random.default_rng(SEED_FOR_SPSA)} + gradient_kwargs = {"h": H_FOR_SPSA, "sampler_rng": np.random.default_rng(seed)} tol = TOL_FOR_SPSA @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -2001,20 +2074,22 @@ def cost(x, y, idx): # pylint: disable=unused-argument def test_matrix_parameter( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, tol, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, tol, interface, seed ): - """Test that the JAX-JIT interface works correctly with a matrix - parameter""" - if dev.name == "param_shift.qubit": + """Test that the JAX-JIT interface works correctly with a matrix parameter""" + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") - if "lightning" in dev.name: + + if "lightning" in dev_name: pytest.xfail("lightning device vjps are not compatible with jax jaocbians") + if jacobian == jax.jacfwd and device_vjp: pytest.skip("device vjps are not compatible with forward differentiation.") # pylint: disable=unused-argument @qml.qnode( - dev, + get_device(dev_name, wires=1, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -2037,29 +2112,34 @@ def circ(p, U): @pytest.mark.parametrize("shots", [None, 10000]) @pytest.mark.parametrize("jacobian", jacobian_fn) +@pytest.mark.parametrize("interface", ["auto", "jax-jit"]) @pytest.mark.parametrize( - "interface,dev,diff_method,grad_on_execution, device_vjp", - interface_and_qubit_device_and_diff_method, + "dev_name,diff_method,grad_on_execution, device_vjp", + device_test_cases, ) class TestReturn: """Class to test the shape of the Grad/Jacobian with different return types.""" @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_grad_single_measurement_param( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface, seed ): """For one measurement and one param, the gradient is a float.""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention") - if "lightning" in dev.name: + + if "lightning" in dev_name: pytest.xfail("lightning device vjps are not compatible with jax jaocbians") + if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") + if jacobian == jax.jacfwd and device_vjp: pytest.skip("jacfwd is not compatible with device_vjp=True.") @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -2079,20 +2159,24 @@ def circuit(a): @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_grad_single_measurement_multiple_param( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface, seed ): """For one measurement and multiple param, the gradient is a tuple of arrays.""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") + if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") + if jacobian == jax.jacfwd and device_vjp: pytest.skip("jacfwd is not compatible with device_vjp=True.") - if "lightning" in dev.name: + + if "lightning" in dev_name: pytest.xfail("lightning device vjps are not compatible with jax jaocbians") @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -2117,20 +2201,24 @@ def circuit(a, b): @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_grad_single_measurement_multiple_param_array( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface, seed ): """For one measurement and multiple param as a single array params, the gradient is an array.""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") - if "lightning" in dev.name: + + if "lightning" in dev_name: pytest.xfail("lightning device vjps are not compatible with jax jaocbians") + if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") + if jacobian == jax.jacfwd and device_vjp: pytest.skip("jacfwd is not compatible with device_vjp=True.") @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -2150,21 +2238,25 @@ def circuit(a): @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_single_measurement_param_probs( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface, seed ): - """For a multi dimensional measurement (probs), check that a single array is returned with the correct - dimension""" - if dev.name == "param_shift.qubit": + """For a multi-dimensional measurement (probs), check that a single array is returned + with the correct dimension""" + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") - if "lightning" in dev.name: + + if "lightning" in dev_name: pytest.xfail("lightning device vjps are not compatible with jax jaocbians") + if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") + if jacobian == jax.jacfwd and device_vjp: pytest.skip("jacfwd is not compatible with device_vjp=True.") @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -2184,21 +2276,25 @@ def circuit(a): @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_single_measurement_probs_multiple_param( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface, seed ): - """For a multi dimensional measurement (probs), check that a single tuple is returned containing arrays with - the correct dimension""" - if dev.name == "param_shift.qubit": + """For a multi-dimensional measurement (probs), check that a single tuple is returned + containing arrays with the correct dimension""" + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") - if "lightning" in dev.name: + + if "lightning" in dev_name: pytest.xfail("lightning device vjps are not compatible with jax jaocbians") + if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") + if jacobian == jax.jacfwd and device_vjp: pytest.skip("jacfwd is not compatible with device_vjp=True.") @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -2224,21 +2320,25 @@ def circuit(a, b): @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_single_measurement_probs_multiple_param_single_array( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface, seed ): - """For a multi dimensional measurement (probs), check that a single tuple is returned containing arrays with - the correct dimension""" - if dev.name == "param_shift.qubit": + """For a multi-dimensional measurement (probs), check that a single tuple is returned + containing arrays with the correct dimension""" + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") + if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") - if "lightning" in dev.name: + + if "lightning" in dev_name: pytest.xfail("lightning device vjps are not compatible with jax jaocbians") + if jacobian == jax.jacfwd and device_vjp: pytest.skip("jacfwd is not compatible with device_vjp=True.") @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -2257,15 +2357,19 @@ def circuit(a): @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_expval_expval_multiple_params( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface, seed ): """The jacobian of multiple measurements with multiple params return a tuple of arrays.""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") - if "lightning" in dev.name: + + if "lightning" in dev_name: pytest.xfail("lightning device vjps are not compatible with jax jaocbians") + if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") + if jacobian == jax.jacfwd and device_vjp: pytest.skip("jacfwd is not compatible with device_vjp=True.") @@ -2273,7 +2377,7 @@ def test_jacobian_expval_expval_multiple_params( par_1 = jax.numpy.array(0.2) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -2307,20 +2411,24 @@ def circuit(x, y): @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_expval_expval_multiple_params_array( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface, seed ): """The jacobian of multiple measurements with a multiple params array return a single array.""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") - if "lightning" in dev.name: + + if "lightning" in dev_name: pytest.xfail("lightning device vjps are not compatible with jax jaocbians") + if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") + if jacobian == jax.jacfwd and device_vjp: pytest.skip("jacfwd is not compatible with device_vjp=True.") @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -2346,17 +2454,22 @@ def circuit(a): @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_var_var_multiple_params( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface, seed ): """The jacobian of multiple measurements with multiple params return a tuple of arrays.""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") + elif diff_method == "hadamard": pytest.skip("Test does not supports hadamard because of var.") + if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") + if jacobian == jax.jacfwd and device_vjp: pytest.skip("jacfwd is not compatible with device_vjp=True.") + if diff_method == "adjoint": pytest.skip("adjoint supports either all expvals or only diagonal measurements") @@ -2364,7 +2477,7 @@ def test_jacobian_var_var_multiple_params( par_1 = jax.numpy.array(0.2) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -2399,22 +2512,27 @@ def circuit(x, y): @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_var_var_multiple_params_array( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface, seed ): """The jacobian of multiple measurements with a multiple params array return a single array.""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") + elif diff_method == "hadamard": pytest.skip("Test does not supports hadamard because of var.") + if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") + if jacobian == jax.jacfwd and device_vjp: pytest.skip("jacfwd is not compatible with device_vjp=True.") + if diff_method == "adjoint": pytest.skip("adjoint supports either all expvals or only diagonal measurements") @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -2440,20 +2558,24 @@ def circuit(a): @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_multiple_measurement_single_param( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface, seed ): """The jacobian of multiple measurements with a single params return an array.""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention") - if "lightning" in dev.name: + + if "lightning" in dev_name: pytest.xfail("lightning device vjps are not compatible with jax jaocbians") + if device_vjp and jacobian == jax.jacfwd: pytest.skip("device vjp not compatible with forward differentiation.") + if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -2479,20 +2601,24 @@ def circuit(a): @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_multiple_measurement_multiple_param( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface, seed ): """The jacobian of multiple measurements with a multiple params return a tuple of arrays.""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") - if "lightning" in dev.name: + + if "lightning" in dev_name: pytest.xfail("lightning device vjps are not compatible with jax jaocbians") + if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") + if jacobian == jax.jacfwd and device_vjp: pytest.skip("jacfwd is not compatible with device_vjp=True.") @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -2527,20 +2653,24 @@ def circuit(a, b): @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_multiple_measurement_multiple_param_array( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface, seed ): """The jacobian of multiple measurements with a multiple params array return a single array.""" - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transforms have a different vjp shape convention.") - if "lightning" in dev.name: + + if "lightning" in dev_name: pytest.xfail("lightning device vjps are not compatible with jax jaocbians") + if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") + if jacobian == jax.jacfwd and device_vjp: pytest.skip("jacfwd is not compatible with device_vjp=True.") @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -2573,15 +2703,16 @@ def circuit(a): @pytest.mark.parametrize("hessian", hessian_fn) +@pytest.mark.parametrize("interface", ["auto", "jax-jit"]) @pytest.mark.parametrize( - "interface,dev,diff_method,grad_on_execution, device_vjp", - interface_and_qubit_device_and_diff_method, + "dev_name,diff_method,grad_on_execution, device_vjp", + device_test_cases, ) class TestReturnHessian: """Class to test the shape of the Hessian with different return types.""" def test_hessian_expval_multiple_params( - self, dev, diff_method, hessian, device_vjp, grad_on_execution, interface + self, dev_name, diff_method, hessian, device_vjp, grad_on_execution, interface, seed ): """The hessian of single a measurement with multiple params return a tuple of arrays.""" if diff_method in {"adjoint", "device"}: @@ -2591,7 +2722,7 @@ def test_hessian_expval_multiple_params( par_1 = jax.numpy.array(0.2) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, max_diff=2, @@ -2624,7 +2755,7 @@ def circuit(x, y): assert hess[1][1].shape == () def test_hessian_expval_multiple_param_array( - self, dev, diff_method, hessian, grad_on_execution, device_vjp, interface + self, dev_name, diff_method, hessian, grad_on_execution, device_vjp, interface, seed ): """The hessian of single measurement with a multiple params array return a single array.""" @@ -2634,7 +2765,7 @@ def test_hessian_expval_multiple_param_array( params = jax.numpy.array([0.1, 0.2], dtype=jax.numpy.float64) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, max_diff=2, @@ -2653,7 +2784,7 @@ def circuit(x): assert hess.shape == (2, 2) def test_hessian_var_multiple_params( - self, dev, diff_method, hessian, device_vjp, grad_on_execution, interface + self, dev_name, diff_method, hessian, device_vjp, grad_on_execution, interface, seed ): """The hessian of single a measurement with multiple params return a tuple of arrays.""" if diff_method in {"adjoint", "device"}: @@ -2665,7 +2796,7 @@ def test_hessian_var_multiple_params( par_1 = jax.numpy.array(0.2, dtype=jax.numpy.float64) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, max_diff=2, @@ -2698,18 +2829,20 @@ def circuit(x, y): assert hess[1][1].shape == () def test_hessian_var_multiple_param_array( - self, dev, diff_method, hessian, grad_on_execution, device_vjp, interface + self, dev_name, diff_method, hessian, grad_on_execution, device_vjp, interface, seed ): """The hessian of single measurement with a multiple params array return a single array.""" + if diff_method in {"adjoint", "device"}: pytest.skip("Test does not supports adjoint because second order diff.") + elif diff_method == "hadamard": pytest.skip("Test does not supports hadamard because of var.") params = jax.numpy.array([0.1, 0.2], dtype=jax.numpy.float64) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, max_diff=2, @@ -2728,11 +2861,13 @@ def circuit(x): assert hess.shape == (2, 2) def test_hessian_probs_expval_multiple_params( - self, dev, diff_method, hessian, grad_on_execution, device_vjp, interface + self, dev_name, diff_method, hessian, grad_on_execution, device_vjp, interface, seed ): """The hessian of multiple measurements with multiple params return a tuple of arrays.""" + if diff_method in {"adjoint", "device"}: pytest.skip("Test does not supports adjoint because second order diff.") + elif diff_method == "hadamard": pytest.skip("Test does not supports hadamard because of non commuting obs.") @@ -2740,7 +2875,7 @@ def test_hessian_probs_expval_multiple_params( par_1 = jax.numpy.array(0.2, dtype=jax.numpy.float64) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, max_diff=2, @@ -2772,7 +2907,7 @@ def circuit(x, y): assert h_comp.shape == (2,) def test_hessian_probs_expval_multiple_param_array( - self, dev, diff_method, hessian, grad_on_execution, device_vjp, interface + self, dev_name, diff_method, hessian, grad_on_execution, device_vjp, interface, seed ): """The hessian of multiple measurements with a multiple param array return a single array.""" @@ -2784,7 +2919,7 @@ def test_hessian_probs_expval_multiple_param_array( params = jax.numpy.array([0.1, 0.2], dtype=jax.numpy.float64) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, max_diff=2, @@ -2808,7 +2943,7 @@ def circuit(x): assert hess[1].shape == (2, 2, 2) def test_hessian_probs_var_multiple_params( - self, dev, diff_method, hessian, grad_on_execution, device_vjp, interface + self, dev_name, diff_method, hessian, grad_on_execution, device_vjp, interface, seed ): """The hessian of multiple measurements with multiple params return a tuple of arrays.""" if diff_method in {"adjoint", "device"}: @@ -2820,7 +2955,7 @@ def test_hessian_probs_var_multiple_params( par_1 = jax.numpy.array(0.2, dtype=jax.numpy.float64) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, max_diff=2, @@ -2852,7 +2987,7 @@ def circuit(x, y): assert h_comp.shape == (2,) def test_hessian_probs_var_multiple_param_array( - self, dev, diff_method, hessian, grad_on_execution, device_vjp, interface + self, dev_name, diff_method, hessian, grad_on_execution, device_vjp, interface, seed ): """The hessian of multiple measurements with a multiple param array return a single array.""" if diff_method in {"adjoint", "device"}: @@ -2863,7 +2998,7 @@ def test_hessian_probs_var_multiple_param_array( params = jax.numpy.array([0.1, 0.2], dtype=jax.numpy.float64) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, max_diff=2, @@ -2914,17 +3049,18 @@ def circuit(x): @pytest.mark.parametrize("jit_inside", [True, False]) +@pytest.mark.parametrize("interface", ["auto", "jax-jit"]) @pytest.mark.parametrize("argnums", [0, 1, [0, 1]]) @pytest.mark.parametrize("jacobian", jacobian_fn) @pytest.mark.parametrize( - "interface,dev,diff_method,grad_on_execution, device_vjp", - interface_and_qubit_device_and_diff_method, + "dev_name,diff_method,grad_on_execution, device_vjp", + device_test_cases, ) class TestSubsetArgnums: def test_single_measurement( self, interface, - dev, + dev_name, diff_method, grad_on_execution, device_vjp, @@ -2932,21 +3068,22 @@ def test_single_measurement( argnums, jit_inside, tol, + seed, ): """Test single measurement with different diff methods with argnums.""" kwargs = {} if jacobian == jax.jacfwd and device_vjp: pytest.skip("jacfwd is not compatible with device_vjp=True.") - if "lightning" in dev.name: + if "lightning" in dev_name: pytest.xfail("lightning device vjps are not compatible with jax jaocbians") - if dev.name == "param_shift.qubit": + if dev_name == "param_shift.qubit": pytest.xfail("gradient transform have a different vjp shape convention.") if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA @qml.qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -2981,7 +3118,7 @@ def circuit(a, b): def test_multi_measurements( self, interface, - dev, + dev_name, diff_method, grad_on_execution, device_vjp, @@ -2989,22 +3126,26 @@ def test_multi_measurements( argnums, jit_inside, tol, + seed, ): """Test multiple measurements with different diff methods with argnums.""" + if jacobian == jax.jacfwd and device_vjp: pytest.skip("jacfwd is not compatible with device_vjp=True.") - if "lightning" in dev.name: + + if "lightning" in dev_name: pytest.xfail("lightning device vjps are not compatible with jax jaocbians") - if dev.name == "param_shift.qubit": + + if dev_name == "param_shift.qubit": pytest.xfail("gradient transform have a different vjp shape convention.") kwargs = {} if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA @qml.qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, diff --git a/tests/interfaces/test_jax_qnode.py b/tests/interfaces/test_jax_qnode.py index d536288fa4b..261a93a3e18 100644 --- a/tests/interfaces/test_jax_qnode.py +++ b/tests/interfaces/test_jax_qnode.py @@ -23,57 +23,57 @@ from pennylane import qnode from pennylane.devices import DefaultQubit -device_seed = 42 + +def get_device(device_name, wires, seed): + if device_name == "lightning.qubit": + return qml.device("lightning.qubit", wires=wires) + return qml.device(device_name, seed=seed) + # device, diff_method, grad_on_execution, device_vjp device_and_diff_method = [ - [DefaultQubit(seed=device_seed), "backprop", True, False], - [DefaultQubit(seed=device_seed), "finite-diff", False, False], - [DefaultQubit(seed=device_seed), "parameter-shift", False, False], - [DefaultQubit(seed=device_seed), "adjoint", True, False], - [DefaultQubit(seed=device_seed), "adjoint", False, False], - [DefaultQubit(seed=device_seed), "adjoint", True, True], - [DefaultQubit(seed=device_seed), "adjoint", False, True], - [DefaultQubit(seed=device_seed), "spsa", False, False], - [DefaultQubit(seed=device_seed), "hadamard", False, False], - [qml.device("lightning.qubit", wires=5), "adjoint", False, True], - [qml.device("lightning.qubit", wires=5), "adjoint", True, True], - [qml.device("lightning.qubit", wires=5), "adjoint", False, False], - [qml.device("lightning.qubit", wires=5), "adjoint", True, False], - [qml.device("reference.qubit"), "parameter-shift", False, False], + ["default.qubit", "backprop", True, False], + ["default.qubit", "finite-diff", False, False], + ["default.qubit", "parameter-shift", False, False], + ["default.qubit", "adjoint", True, False], + ["default.qubit", "adjoint", False, False], + ["default.qubit", "adjoint", True, True], + ["default.qubit", "adjoint", False, True], + ["default.qubit", "spsa", False, False], + ["default.qubit", "hadamard", False, False], + ["lightning.qubit", "adjoint", False, True], + ["lightning.qubit", "adjoint", True, True], + ["lightning.qubit", "adjoint", False, False], + ["lightning.qubit", "adjoint", True, False], + ["reference.qubit", "parameter-shift", False, False], ] -interface_and_device_and_diff_method = [ - ["auto"] + inner_list for inner_list in device_and_diff_method -] + [["jax"] + inner_list for inner_list in device_and_diff_method] - - pytestmark = pytest.mark.jax jax = pytest.importorskip("jax") jax.config.update("jax_enable_x64", True) TOL_FOR_SPSA = 1.0 -SEED_FOR_SPSA = 32651 H_FOR_SPSA = 0.05 +@pytest.mark.parametrize("interface", ["auto", "jax"]) @pytest.mark.parametrize( - "interface,dev,diff_method,grad_on_execution,device_vjp", interface_and_device_and_diff_method + "dev_name,diff_method,grad_on_execution,device_vjp", device_and_diff_method ) class TestQNode: """Test that using the QNode with JAX integrates with the PennyLane stack""" def test_execution_with_interface( - self, dev, diff_method, grad_on_execution, interface, device_vjp + self, dev_name, diff_method, grad_on_execution, interface, device_vjp, seed ): """Test execution works with the interface""" if diff_method == "backprop": pytest.skip("Test does not support backprop") @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -100,7 +100,7 @@ def circuit(a): assert grad.shape == () def test_changing_trainability( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): # pylint:disable=unused-argument """Test changing the trainability of parameters changes the number of differentiation requests made""" @@ -110,7 +110,11 @@ def test_changing_trainability( a = jax.numpy.array(0.1) b = jax.numpy.array(0.2) - @qnode(dev, interface=interface, diff_method="parameter-shift") + @qnode( + get_device(dev_name, wires=2, seed=seed), + interface=interface, + diff_method="parameter-shift", + ) def circuit(a, b): qml.RY(a, wires=0) qml.RX(b, wires=1) @@ -136,14 +140,16 @@ def circuit(a, b): expected = [-np.sin(a) + np.sin(a) * np.sin(b)] assert np.allclose(res, expected, atol=tol, rtol=0) - def test_classical_processing(self, dev, diff_method, grad_on_execution, device_vjp, interface): + def test_classical_processing( + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, seed + ): """Test classical processing within the quantum tape""" a = jax.numpy.array(0.1) b = jax.numpy.array(0.2) c = jax.numpy.array(0.3) @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -163,7 +169,7 @@ def circuit(a, b, c): assert len(res) == 2 def test_matrix_parameter( - self, dev, diff_method, grad_on_execution, interface, device_vjp, tol + self, dev_name, diff_method, grad_on_execution, interface, device_vjp, tol, seed ): """Test that the jax interface works correctly with a matrix parameter""" @@ -171,7 +177,7 @@ def test_matrix_parameter( a = jax.numpy.array(0.1) @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -189,7 +195,7 @@ def circuit(U, a): assert circuit.qtape.trainable_params == [1] def test_differentiable_expand( - self, dev, diff_method, grad_on_execution, interface, device_vjp, tol + self, dev_name, diff_method, grad_on_execution, interface, device_vjp, tol, seed ): """Test that operation and nested tape expansion is differentiable""" @@ -201,7 +207,7 @@ def test_differentiable_expand( } if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) kwargs["num_directions"] = 10 tol = TOL_FOR_SPSA @@ -217,7 +223,7 @@ def decomposition(self): a = jax.numpy.array(0.1) p = jax.numpy.array([0.1, 0.2, 0.3]) - @qnode(dev, **kwargs) + @qnode(get_device(dev_name, wires=1, seed=seed), **kwargs) def circuit(a, p): qml.RX(a, wires=0) U3(p[0], p[1], p[2], wires=0) @@ -243,7 +249,7 @@ def circuit(a, p): assert np.allclose(res, expected, atol=tol, rtol=0) def test_jacobian_options( - self, dev, diff_method, grad_on_execution, device_vjp, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, seed ): # pylint:disable=unused-argument """Test setting jacobian options""" if diff_method != "finite-diff": @@ -251,7 +257,13 @@ def test_jacobian_options( a = jax.numpy.array([0.1, 0.2]) - @qnode(dev, interface=interface, diff_method="finite-diff", h=1e-8, approx_order=2) + @qnode( + get_device(dev_name, wires=1, seed=seed), + interface=interface, + diff_method="finite-diff", + h=1e-8, + approx_order=2, + ) def circuit(a): qml.RY(a[0], wires=0) qml.RX(a[1], wires=0) @@ -260,15 +272,16 @@ def circuit(a): jax.jacobian(circuit)(a) +@pytest.mark.parametrize("interface", ["auto", "jax"]) @pytest.mark.parametrize( - "interface,dev,diff_method,grad_on_execution, device_vjp", interface_and_device_and_diff_method + "dev_name,diff_method,grad_on_execution, device_vjp", device_and_diff_method ) class TestVectorValuedQNode: """Test that using vector-valued QNodes with JAX integrate with the PennyLane stack""" def test_diff_expval_expval( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Test jacobian calculation""" kwargs = { @@ -279,15 +292,15 @@ def test_diff_expval_expval( } if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA - if "lightning" in dev.name: + if "lightning" in dev_name: pytest.xfail("lightning device_vjp not compatible with jax.jacobian.") a = jax.numpy.array(0.1) b = jax.numpy.array(0.2) - @qnode(dev, **kwargs) + @qnode(get_device(dev_name, wires=2, seed=seed), **kwargs) def circuit(a, b): qml.RY(a, wires=0) qml.RX(b, wires=1) @@ -327,7 +340,7 @@ def circuit(a, b): assert np.allclose(res[1][1], expected[1][1], atol=tol, rtol=0) def test_jacobian_no_evaluate( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Test jacobian calculation when no prior circuit evaluation has been performed""" kwargs = { @@ -337,17 +350,17 @@ def test_jacobian_no_evaluate( "device_vjp": device_vjp, } - if "lightning" in dev.name: + if "lightning" in dev_name: pytest.xfail("lightning device_vjp not compatible with jax.jacobian.") if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA a = jax.numpy.array(0.1) b = jax.numpy.array(0.2) - @qnode(dev, **kwargs) + @qnode(get_device(dev_name, wires=2, seed=seed), **kwargs) def circuit(a, b): qml.RY(a, wires=0) qml.RX(b, wires=1) @@ -383,7 +396,7 @@ def circuit(a, b): assert np.allclose(res[i][j], expected[i][j], atol=tol, rtol=0) def test_diff_single_probs( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Tests correct output shape and evaluation for a tape with a single prob output""" @@ -394,15 +407,15 @@ def test_diff_single_probs( "device_vjp": device_vjp, } if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA - if "lightning" in dev.name: + if "lightning" in dev_name: pytest.xfail("lightning device_vjp not compatible with jax.jacobian.") x = jax.numpy.array(0.543) y = jax.numpy.array(-0.654) - @qnode(dev, **kwargs) + @qnode(get_device(dev_name, wires=2, seed=seed), **kwargs) def circuit(x, y): qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) @@ -431,7 +444,7 @@ def circuit(x, y): assert np.allclose(res[1], expected.T[1], atol=tol, rtol=0) def test_diff_multi_probs( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Tests correct output shape and evaluation for a tape with multiple prob outputs""" @@ -443,15 +456,15 @@ def test_diff_multi_probs( } if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA - if "lightning" in dev.name: + if "lightning" in dev_name: pytest.xfail("lightning device_vjp not compatible with jax.jacobian.") x = jax.numpy.array(0.543) y = jax.numpy.array(-0.654) - @qnode(dev, **kwargs) + @qnode(get_device(dev_name, wires=1, seed=seed), **kwargs) def circuit(x, y): qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) @@ -513,7 +526,7 @@ def circuit(x, y): assert np.allclose(jac[1][1], expected_1[1], atol=tol, rtol=0) def test_diff_expval_probs( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Tests correct output shape and evaluation for a tape with prob and expval outputs""" @@ -524,15 +537,15 @@ def test_diff_expval_probs( "device_vjp": device_vjp, } if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA - if "lightning" in dev.name: + if "lightning" in dev_name: pytest.xfail("lightning device_vjp not compatible with jax.jacobian.") x = jax.numpy.array(0.543) y = jax.numpy.array(-0.654) - @qnode(dev, **kwargs) + @qnode(get_device(dev_name, wires=1, seed=seed), **kwargs) def circuit(x, y): qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) @@ -583,13 +596,13 @@ def circuit(x, y): assert np.allclose(jac[1][1], expected[1][1], atol=tol, rtol=0) def test_diff_expval_probs_sub_argnums( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Tests correct output shape and evaluation for a tape with prob and expval outputs with less trainable parameters (argnums) than parameters.""" kwargs = {} if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA x = jax.numpy.array(0.543) @@ -600,7 +613,7 @@ def test_diff_expval_probs_sub_argnums( y = y + 0j @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -613,8 +626,9 @@ def circuit(x, y): qml.CNOT(wires=[0, 1]) return qml.expval(qml.PauliZ(0)), qml.probs(wires=[1]) - if "lightning" in dev.name: + if "lightning" in dev_name: pytest.xfail("lightning does not support measuring probabilities with adjoint.") + jac = jax.jacobian(circuit, argnums=[0])(x, y) expected = [ @@ -639,7 +653,9 @@ def circuit(x, y): assert jac[1][0].shape == (2,) assert np.allclose(jac[1][0], expected[1][0], atol=tol, rtol=0) - def test_diff_var_probs(self, dev, diff_method, grad_on_execution, device_vjp, interface, tol): + def test_diff_var_probs( + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed + ): """Tests correct output shape and evaluation for a tape with prob and variance outputs""" kwargs = { @@ -651,16 +667,16 @@ def test_diff_var_probs(self, dev, diff_method, grad_on_execution, device_vjp, i if diff_method == "hadamard": pytest.skip("Hadamard does not support var") - if "lightning" in dev.name: + if "lightning" in dev_name: pytest.xfail("lightning device_vjp not compatible with jax.jacobian.") elif diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA x = jax.numpy.array(0.543) y = jax.numpy.array(-0.654) - @qnode(dev, **kwargs) + @qnode(get_device(dev_name, wires=1, seed=seed), **kwargs) def circuit(x, y): qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) @@ -795,11 +811,13 @@ def cost_fn(a, b): assert spy.call_args[1]["gradient_fn"] == "backprop" -@pytest.mark.parametrize("dev,diff_method,grad_on_execution, device_vjp", device_and_diff_method) +@pytest.mark.parametrize( + "dev_name,diff_method,grad_on_execution,device_vjp", device_and_diff_method +) class TestQubitIntegration: """Tests that ensure various qubit circuits integrate correctly""" - def test_sampling(self, dev, diff_method, grad_on_execution, device_vjp): + def test_sampling(self, dev_name, diff_method, grad_on_execution, device_vjp, seed): """Test sampling works as expected""" if grad_on_execution is True: pytest.skip("Sampling not possible with grad_on_execution differentiation.") @@ -808,7 +826,7 @@ def test_sampling(self, dev, diff_method, grad_on_execution, device_vjp): pytest.skip("Adjoint warns with finite shots") @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface="jax", grad_on_execution=grad_on_execution, @@ -828,7 +846,7 @@ def circuit(): assert isinstance(res[1], jax.Array) assert res[1].shape == (10,) # pylint:disable=comparison-with-callable - def test_counts(self, dev, diff_method, grad_on_execution, device_vjp): + def test_counts(self, dev_name, diff_method, grad_on_execution, device_vjp, seed): """Test counts works as expected""" if grad_on_execution is True: pytest.skip("Sampling not possible with grad_on_execution differentiation.") @@ -837,7 +855,7 @@ def test_counts(self, dev, diff_method, grad_on_execution, device_vjp): pytest.skip("Adjoint errors with finite shots") @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface="jax", grad_on_execution=grad_on_execution, @@ -860,7 +878,7 @@ def circuit(): assert isinstance(res[1], dict) assert len(res[1]) == 2 - def test_chained_qnodes(self, dev, diff_method, grad_on_execution, device_vjp): + def test_chained_qnodes(self, dev_name, diff_method, grad_on_execution, device_vjp, seed): """Test that the gradient of chained QNodes works without error""" # pylint:disable=too-few-public-methods @@ -869,7 +887,7 @@ def decomposition(self): return [qml.templates.StronglyEntanglingLayers(*self.parameters, self.wires)] @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface="jax", diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -880,7 +898,7 @@ def circuit1(weights): return qml.expval(qml.PauliZ(0)) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface="jax", diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -910,16 +928,18 @@ def cost(weights): assert len(res) == 2 - def test_postselection_differentiation(self, dev, diff_method, grad_on_execution, device_vjp): + def test_postselection_differentiation( + self, dev_name, diff_method, grad_on_execution, device_vjp, seed + ): """Test that when postselecting with default.qubit, differentiation works correctly.""" if diff_method in ["adjoint", "spsa", "hadamard"]: pytest.skip("Diff method does not support postselection.") - if dev.name == "reference.qubit": + if dev_name == "reference.qubit": pytest.xfail("reference.qubit does not support postselection.") @qml.qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface="jax", grad_on_execution=grad_on_execution, @@ -933,7 +953,7 @@ def circuit(phi, theta): return qml.expval(qml.PauliZ(1)) @qml.qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface="jax", grad_on_execution=grad_on_execution, @@ -954,14 +974,16 @@ def expected_circuit(theta): assert np.allclose(gradient, [0.0, exp_theta_grad]) +@pytest.mark.parametrize("interface", ["auto", "jax"]) @pytest.mark.parametrize( - "interface,dev,diff_method,grad_on_execution, device_vjp", interface_and_device_and_diff_method + "dev_name,diff_method,grad_on_execution, device_vjp", device_and_diff_method ) class TestQubitIntegrationHigherOrder: """Tests that ensure various qubit circuits integrate correctly when computing higher-order derivatives""" + @pytest.mark.local_salt(1) def test_second_derivative( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Test second derivative calculation of a scalar-valued QNode""" kwargs = { @@ -975,10 +997,10 @@ def test_second_derivative( if diff_method == "adjoint": pytest.skip("Adjoint does not second derivative.") elif diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA - @qnode(dev, **kwargs) + @qnode(get_device(dev_name, wires=0, seed=seed), **kwargs) def circuit(x): qml.RY(x[0], wires=0) qml.RX(x[1], wires=0) @@ -1006,7 +1028,9 @@ def circuit(x): else: assert np.allclose(g2, expected_g2, atol=tol, rtol=0) - def test_hessian(self, dev, diff_method, grad_on_execution, device_vjp, interface, tol): + def test_hessian( + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed + ): """Test hessian calculation of a scalar-valued QNode""" gradient_kwargs = {} if diff_method == "adjoint": @@ -1015,12 +1039,12 @@ def test_hessian(self, dev, diff_method, grad_on_execution, device_vjp, interfac gradient_kwargs = { "h": H_FOR_SPSA, "num_directions": 20, - "sampler_rng": np.random.default_rng(SEED_FOR_SPSA), + "sampler_rng": np.random.default_rng(seed), } tol = TOL_FOR_SPSA @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -1059,23 +1083,23 @@ def circuit(x): assert np.allclose(hess, expected_hess, atol=tol, rtol=0) def test_hessian_vector_valued( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Test hessian calculation of a vector-valued QNode""" gradient_kwargs = {} if diff_method == "adjoint": pytest.skip("Adjoint does not support second derivative.") elif diff_method == "spsa": - qml.math.random.seed(42) + qml.math.random.seed(seed) gradient_kwargs = { "h": H_FOR_SPSA, "num_directions": 20, - "sampler_rng": np.random.default_rng(SEED_FOR_SPSA), + "sampler_rng": np.random.default_rng(seed), } tol = TOL_FOR_SPSA @qnode( - dev, + get_device(dev_name, wires=0, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -1123,7 +1147,7 @@ def circuit(x): assert np.allclose(hess, expected_hess, atol=tol, rtol=0) def test_hessian_vector_valued_postprocessing( - self, dev, diff_method, interface, grad_on_execution, device_vjp, tol + self, dev_name, diff_method, interface, grad_on_execution, device_vjp, tol, seed ): """Test hessian calculation of a vector valued QNode with post-processing""" gradient_kwargs = {} @@ -1133,12 +1157,12 @@ def test_hessian_vector_valued_postprocessing( gradient_kwargs = { "h": H_FOR_SPSA, "num_directions": 20, - "sampler_rng": np.random.default_rng(SEED_FOR_SPSA), + "sampler_rng": np.random.default_rng(seed), } tol = TOL_FOR_SPSA @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -1189,7 +1213,7 @@ def cost_fn(x): assert np.allclose(hess, expected_hess, atol=tol, rtol=0) def test_hessian_vector_valued_separate_args( - self, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Test hessian calculation of a vector valued QNode that has separate input arguments""" gradient_kwargs = {} @@ -1199,12 +1223,12 @@ def test_hessian_vector_valued_separate_args( gradient_kwargs = { "h": H_FOR_SPSA, "num_directions": 20, - "sampler_rng": np.random.default_rng(SEED_FOR_SPSA), + "sampler_rng": np.random.default_rng(seed), } tol = TOL_FOR_SPSA @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -1254,17 +1278,19 @@ def circuit(a, b): else: assert np.allclose(hess, expected_hess, atol=tol, rtol=0) - def test_state(self, dev, diff_method, grad_on_execution, device_vjp, interface, tol): + def test_state( + self, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed + ): """Test that the state can be returned and differentiated""" - if "lightning" in getattr(dev, "name", "").lower(): + if "lightning" in dev_name: pytest.xfail("Lightning does not support state adjoint differentiation.") x = jax.numpy.array(0.543) y = jax.numpy.array(-0.654) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -1293,7 +1319,7 @@ def cost_fn(x, y): @pytest.mark.parametrize("state", [[1], [0, 1]]) # Basis state and state vector def test_projector( - self, state, dev, diff_method, grad_on_execution, device_vjp, interface, tol + self, state, dev_name, diff_method, grad_on_execution, device_vjp, interface, tol, seed ): """Test that the variance of a projector is correctly returned""" gradient_kwargs = {} @@ -1302,16 +1328,16 @@ def test_projector( if diff_method == "hadamard": pytest.skip("Hadamard does not support var.") elif diff_method == "spsa": - gradient_kwargs = {"h": H_FOR_SPSA, "sampler_rng": np.random.default_rng(SEED_FOR_SPSA)} + gradient_kwargs = {"h": H_FOR_SPSA, "sampler_rng": np.random.default_rng(seed)} tol = TOL_FOR_SPSA - if dev.name == "reference.qubit": + if dev_name == "reference.qubit": pytest.xfail("diagonalize_measurements do not support projectors (sc-72911)") P = jax.numpy.array(state) x, y = 0.765, -0.654 @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution, @@ -1338,8 +1364,9 @@ def circuit(x, y): assert np.allclose(res, expected, atol=tol, rtol=0) +@pytest.mark.parametrize("interface", ["auto", "jax"]) @pytest.mark.parametrize( - "interface,dev,diff_method,grad_on_execution, device_vjp", interface_and_device_and_diff_method + "dev_name,diff_method,grad_on_execution, device_vjp", device_and_diff_method ) class TestTapeExpansion: """Test that tape expansion within the QNode integrates correctly @@ -1347,7 +1374,7 @@ class TestTapeExpansion: @pytest.mark.parametrize("max_diff", [1, 2]) def test_gradient_expansion_trainable_only( - self, dev, diff_method, grad_on_execution, device_vjp, max_diff, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, max_diff, interface, seed ): """Test that a *supported* operation with no gradient recipe is only expanded for parameter-shift and finite-differences when it is trainable.""" @@ -1361,7 +1388,7 @@ def decomposition(self): return [qml.RY(3 * self.data[0], wires=self.wires)] @qnode( - dev, + get_device(dev_name, wires=1, seed=seed), diff_method=diff_method, grad_on_execution=grad_on_execution, max_diff=max_diff, @@ -1382,7 +1409,16 @@ def circuit(x, y): @pytest.mark.parametrize("max_diff", [1, 2]) def test_split_non_commuting_analytic( - self, dev, diff_method, grad_on_execution, max_diff, interface, device_vjp, mocker, tol + self, + dev_name, + diff_method, + grad_on_execution, + max_diff, + interface, + device_vjp, + mocker, + tol, + seed, ): """Test that the Hamiltonian is not expanded if there are non-commuting groups and the number of shots is None @@ -1396,10 +1432,10 @@ def test_split_non_commuting_analytic( gradient_kwargs = { "h": H_FOR_SPSA, "num_directions": 20, - "sampler_rng": np.random.default_rng(SEED_FOR_SPSA), + "sampler_rng": np.random.default_rng(seed), } tol = TOL_FOR_SPSA - if dev.name == "reference.qubit": + if dev_name == "reference.qubit": pytest.skip( "Cannot add transform to the transform program in preprocessing" "when using mocker.spy on it." @@ -1409,7 +1445,7 @@ def test_split_non_commuting_analytic( obs = [qml.PauliX(0), qml.PauliX(0) @ qml.PauliZ(1), qml.PauliZ(0) @ qml.PauliZ(1)] @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1459,13 +1495,21 @@ def circuit(data, weights, coeffs): @pytest.mark.parametrize("max_diff", [1, 2]) def test_hamiltonian_finite_shots( - self, dev, diff_method, grad_on_execution, device_vjp, interface, max_diff, mocker + self, + dev_name, + diff_method, + grad_on_execution, + device_vjp, + interface, + max_diff, + mocker, + seed, ): """Test that the Hamiltonian is correctly measured (and not expanded) if there are non-commuting groups and the number of shots is finite and the first and second order gradients are correctly evaluated""" - if dev.name == "reference.qubit": + if dev_name == "reference.qubit": pytest.skip( "Cannot added to a transform to the transform program in " "preprocessing when using mocker.spy on it." @@ -1480,7 +1524,7 @@ def test_hamiltonian_finite_shots( elif diff_method == "spsa": gradient_kwargs = { "h": H_FOR_SPSA, - "sampler_rng": np.random.default_rng(SEED_FOR_SPSA), + "sampler_rng": np.random.default_rng(seed), "num_directions": 20, } tol = TOL_FOR_SPSA @@ -1489,7 +1533,7 @@ def test_hamiltonian_finite_shots( obs = [qml.PauliX(0), qml.PauliX(0) @ qml.PauliZ(1), qml.PauliZ(0) @ qml.PauliZ(1)] @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1545,21 +1589,22 @@ def circuit(data, weights, coeffs): @pytest.mark.parametrize("shots", [None, 10000]) +@pytest.mark.parametrize("interface", ["auto", "jax"]) @pytest.mark.parametrize( - "interface,dev,diff_method,grad_on_execution, device_vjp", interface_and_device_and_diff_method + "dev_name,diff_method,grad_on_execution, device_vjp", device_and_diff_method ) class TestReturn: # pylint:disable=too-many-public-methods """Class to test the shape of the Grad/Jacobian/Hessian with different return types.""" def test_grad_single_measurement_param( - self, dev, diff_method, grad_on_execution, device_vjp, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, shots, interface, seed ): """For one measurement and one param, the gradient is a float.""" if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1578,14 +1623,14 @@ def circuit(a): assert grad.shape == () def test_grad_single_measurement_multiple_param( - self, dev, diff_method, grad_on_execution, shots, device_vjp, interface + self, dev_name, diff_method, grad_on_execution, shots, device_vjp, interface, seed ): """For one measurement and multiple param, the gradient is a tuple of arrays.""" if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1607,14 +1652,14 @@ def circuit(a, b): assert grad[1].shape == () def test_grad_single_measurement_multiple_param_array( - self, dev, diff_method, grad_on_execution, shots, device_vjp, interface + self, dev_name, diff_method, grad_on_execution, shots, device_vjp, interface, seed ): """For one measurement and multiple param as a single array params, the gradient is an array.""" if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1634,7 +1679,7 @@ def circuit(a): @pytest.mark.parametrize("jacobian", jacobian_fn) def test_jacobian_single_measurement_param_probs( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface, seed ): """For a multi dimensional measurement (probs), check that a single array is returned with the correct dimension""" @@ -1645,7 +1690,7 @@ def test_jacobian_single_measurement_param_probs( pytest.skip("Test does not supports adjoint because of probabilities.") @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1665,7 +1710,7 @@ def circuit(a): @pytest.mark.parametrize("jacobian", jacobian_fn) def test_jacobian_single_measurement_probs_multiple_param( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface, seed ): """For a multi dimensional measurement (probs), check that a single tuple is returned containing arrays with the correct dimension""" @@ -1675,7 +1720,7 @@ def test_jacobian_single_measurement_probs_multiple_param( pytest.skip("Test does not support finite shots and adjoint/backprop") @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1701,7 +1746,7 @@ def circuit(a, b): @pytest.mark.parametrize("jacobian", jacobian_fn) def test_jacobian_single_measurement_probs_multiple_param_single_array( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface, seed ): """For a multi dimensional measurement (probs), check that a single tuple is returned containing arrays with the correct dimension""" @@ -1711,7 +1756,7 @@ def test_jacobian_single_measurement_probs_multiple_param_single_array( pytest.skip("Test does not support finite shots and adjoint/backprop") @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1730,27 +1775,20 @@ def circuit(a): @pytest.mark.parametrize("jacobian", jacobian_fn) def test_jacobian_expval_expval_multiple_params( - self, - dev, - diff_method, - grad_on_execution, - jacobian, - shots, - interface, - device_vjp, + self, dev_name, diff_method, grad_on_execution, jacobian, shots, interface, device_vjp, seed ): """The hessian of multiple measurements with multiple params return a tuple of arrays.""" if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") if device_vjp and jacobian is jax.jacfwd: pytest.skip("forward pass can't be done with registered vjp.") - if "lightning" in dev.name: + if "lightning" in dev_name: pytest.xfail("lightning device_vjp not compatible with jax.jacobian.") par_0 = jax.numpy.array(0.1) par_1 = jax.numpy.array(0.2) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, max_diff=2, @@ -1783,18 +1821,18 @@ def circuit(x, y): @pytest.mark.parametrize("jacobian", jacobian_fn) def test_jacobian_expval_expval_multiple_params_array( - self, dev, diff_method, grad_on_execution, jacobian, device_vjp, shots, interface + self, dev_name, diff_method, grad_on_execution, jacobian, device_vjp, shots, interface, seed ): """The jacobian of multiple measurements with a multiple params array return a single array.""" if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") if device_vjp and jacobian is jax.jacfwd: pytest.skip("forward pass can't be done with registered vjp.") - if "lightning" in dev.name: + if "lightning" in dev_name: pytest.xfail("lightning device_vjp not compatible with jax.jacobian.") @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1820,7 +1858,7 @@ def circuit(a): @pytest.mark.parametrize("jacobian", jacobian_fn) def test_jacobian_var_var_multiple_params( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface, seed ): """The hessian of multiple measurements with multiple params return a tuple of arrays.""" if diff_method == "adjoint": @@ -1834,7 +1872,7 @@ def test_jacobian_var_var_multiple_params( par_1 = jax.numpy.array(0.2) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, max_diff=2, @@ -1868,7 +1906,7 @@ def circuit(x, y): @pytest.mark.parametrize("jacobian", jacobian_fn) def test_jacobian_var_var_multiple_params_array( - self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface, seed ): """The jacobian of multiple measurements with a multiple params array return a single array.""" if diff_method == "adjoint": @@ -1879,7 +1917,7 @@ def test_jacobian_var_var_multiple_params_array( pytest.skip("Test does not support finite shots and adjoint/backprop") @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1905,18 +1943,18 @@ def circuit(a): @pytest.mark.parametrize("jacobian", jacobian_fn) def test_jacobian_multiple_measurement_single_param( - self, dev, diff_method, grad_on_execution, jacobian, device_vjp, shots, interface + self, dev_name, diff_method, grad_on_execution, jacobian, device_vjp, shots, interface, seed ): """The jacobian of multiple measurements with a single params return an array.""" if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") - if "lightning" in dev.name: + if "lightning" in dev_name: pytest.xfail("lightning device_vjp not compatible with jax.jacobian.") if diff_method == "adjoint" and jacobian == jax.jacfwd: pytest.skip("jacfwd doesn't like complex numbers") @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1942,18 +1980,18 @@ def circuit(a): @pytest.mark.parametrize("jacobian", jacobian_fn) def test_jacobian_multiple_measurement_multiple_param( - self, dev, diff_method, grad_on_execution, jacobian, device_vjp, shots, interface + self, dev_name, diff_method, grad_on_execution, jacobian, device_vjp, shots, interface, seed ): """The jacobian of multiple measurements with a multiple params return a tuple of arrays.""" if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") - if "lightning" in dev.name: + if "lightning" in dev_name: pytest.xfail("lightning device_vjp not compatible with jax.jacobian.") if diff_method == "adjoint" and jacobian == jax.jacfwd: pytest.skip("jacfwd doesn't like complex numbers") @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -1988,18 +2026,18 @@ def circuit(a, b): @pytest.mark.parametrize("jacobian", jacobian_fn) def test_jacobian_multiple_measurement_multiple_param_array( - self, dev, diff_method, grad_on_execution, jacobian, device_vjp, shots, interface + self, dev_name, diff_method, grad_on_execution, jacobian, device_vjp, shots, interface, seed ): """The jacobian of multiple measurements with a multiple params array return a single array.""" if shots is not None and diff_method in ("backprop", "adjoint"): pytest.skip("Test does not support finite shots and adjoint/backprop") - if "lightning" in dev.name: + if "lightning" in dev_name: pytest.xfail("lightning device_vjp not compatible with jax.jacobian.") if diff_method == "adjoint" and jacobian == jax.jacfwd: pytest.skip("jacfwd doesn't like complex numbers") @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, grad_on_execution=grad_on_execution, @@ -2024,7 +2062,7 @@ def circuit(a): assert jac[1].shape == (4, 2) def test_hessian_expval_multiple_params( - self, dev, diff_method, grad_on_execution, device_vjp, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, shots, interface, seed ): """The hessian of single a measurement with multiple params return a tuple of arrays.""" if shots is not None and diff_method in ("backprop", "adjoint"): @@ -2037,7 +2075,7 @@ def test_hessian_expval_multiple_params( par_1 = jax.numpy.array(0.2) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, max_diff=2, @@ -2068,7 +2106,7 @@ def circuit(x, y): assert hess[1][1].shape == () def test_hessian_expval_multiple_param_array( - self, dev, diff_method, grad_on_execution, device_vjp, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, shots, interface, seed ): """The hessian of single measurement with a multiple params array return a single array.""" if diff_method == "adjoint": @@ -2079,7 +2117,7 @@ def test_hessian_expval_multiple_param_array( params = jax.numpy.array([0.1, 0.2]) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, max_diff=2, @@ -2098,7 +2136,7 @@ def circuit(x): assert hess.shape == (2, 2) def test_hessian_var_multiple_params( - self, dev, diff_method, grad_on_execution, device_vjp, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, shots, interface, seed ): """The hessian of single a measurement with multiple params return a tuple of arrays.""" if diff_method == "adjoint": @@ -2112,7 +2150,7 @@ def test_hessian_var_multiple_params( par_1 = jax.numpy.array(0.2) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, max_diff=2, @@ -2143,7 +2181,7 @@ def circuit(x, y): assert hess[1][1].shape == () def test_hessian_var_multiple_param_array( - self, dev, diff_method, grad_on_execution, device_vjp, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, shots, interface, seed ): """The hessian of single measurement with a multiple params array return a single array.""" if diff_method == "adjoint": @@ -2156,7 +2194,7 @@ def test_hessian_var_multiple_param_array( params = jax.numpy.array([0.1, 0.2]) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, max_diff=2, @@ -2175,7 +2213,7 @@ def circuit(x): assert hess.shape == (2, 2) def test_hessian_probs_expval_multiple_params( - self, dev, diff_method, grad_on_execution, device_vjp, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, shots, interface, seed ): """The hessian of multiple measurements with multiple params return a tuple of arrays.""" if diff_method == "adjoint": @@ -2190,7 +2228,7 @@ def test_hessian_probs_expval_multiple_params( par_1 = jax.numpy.array(0.2) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, max_diff=2, @@ -2239,7 +2277,7 @@ def circuit(x, y): assert hess[1][1][1].shape == (2,) def test_hessian_expval_probs_multiple_param_array( - self, dev, diff_method, grad_on_execution, device_vjp, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, shots, interface, seed ): """The hessian of multiple measurements with a multiple param array return a single array.""" if diff_method == "adjoint": @@ -2252,7 +2290,7 @@ def test_hessian_expval_probs_multiple_param_array( params = jax.numpy.array([0.1, 0.2]) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, device_vjp=device_vjp, @@ -2277,7 +2315,7 @@ def circuit(x): assert hess[1].shape == (2, 2, 2) def test_hessian_probs_var_multiple_params( - self, dev, diff_method, grad_on_execution, device_vjp, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, shots, interface, seed ): """The hessian of multiple measurements with multiple params return a tuple of arrays.""" if diff_method == "adjoint": @@ -2291,7 +2329,7 @@ def test_hessian_probs_var_multiple_params( par_1 = qml.numpy.array(0.2) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, max_diff=2, @@ -2340,7 +2378,7 @@ def circuit(x, y): assert hess[1][1][1].shape == (2,) def test_hessian_var_probs_multiple_param_array( - self, dev, diff_method, grad_on_execution, device_vjp, shots, interface + self, dev_name, diff_method, grad_on_execution, device_vjp, shots, interface, seed ): """The hessian of multiple measurements with a multiple param array return a single array.""" if diff_method == "adjoint": @@ -2353,7 +2391,7 @@ def test_hessian_var_probs_multiple_param_array( params = jax.numpy.array([0.1, 0.2]) @qnode( - dev, + get_device(dev_name, wires=2, seed=seed), interface=interface, diff_method=diff_method, max_diff=2, diff --git a/tests/interfaces/test_tensorflow.py b/tests/interfaces/test_tensorflow.py index 0abe82c1942..57a38669445 100644 --- a/tests/interfaces/test_tensorflow.py +++ b/tests/interfaces/test_tensorflow.py @@ -109,24 +109,20 @@ def cost(x, cache): # add tests for lightning 2 when possible # set rng for device when possible test_matrix = [ - ({"gradient_fn": param_shift, "interface": "tensorflow"}, 100000, DefaultQubit(seed=42)), # 0 - ({"gradient_fn": param_shift, "interface": "tensorflow"}, None, DefaultQubit()), # 1 - ({"gradient_fn": "backprop", "interface": "tensorflow"}, None, DefaultQubit()), # 2 - ({"gradient_fn": "adjoint", "interface": "tensorflow"}, None, DefaultQubit()), # 3 - ({"gradient_fn": param_shift, "interface": "tf-autograph"}, 100000, DefaultQubit(seed=42)), # 4 - ({"gradient_fn": param_shift, "interface": "tf-autograph"}, None, DefaultQubit()), # 5 - ({"gradient_fn": "backprop", "interface": "tf-autograph"}, None, DefaultQubit()), # 6 - ({"gradient_fn": "adjoint", "interface": "tf-autograph"}, None, DefaultQubit()), # 7 - ({"gradient_fn": "adjoint", "interface": "tf", "device_vjp": True}, None, DefaultQubit()), # 8 - ( - {"gradient_fn": param_shift, "interface": "tensorflow"}, - None, - qml.device("reference.qubit"), - ), # 9 + ({"gradient_fn": param_shift, "interface": "tensorflow"}, 100000, "default.qubit"), # 0 + ({"gradient_fn": param_shift, "interface": "tensorflow"}, None, "default.qubit"), # 1 + ({"gradient_fn": "backprop", "interface": "tensorflow"}, None, "default.qubit"), # 2 + ({"gradient_fn": "adjoint", "interface": "tensorflow"}, None, "default.qubit"), # 3 + ({"gradient_fn": param_shift, "interface": "tf-autograph"}, 100000, "default.qubit"), # 4 + ({"gradient_fn": param_shift, "interface": "tf-autograph"}, None, "default.qubit"), # 5 + ({"gradient_fn": "backprop", "interface": "tf-autograph"}, None, "default.qubit"), # 6 + ({"gradient_fn": "adjoint", "interface": "tf-autograph"}, None, "default.qubit"), # 7 + ({"gradient_fn": "adjoint", "interface": "tf", "device_vjp": True}, None, "default.qubit"), # 8 + ({"gradient_fn": param_shift, "interface": "tensorflow"}, None, "reference.qubit"), # 9 ( {"gradient_fn": param_shift, "interface": "tensorflow"}, 100000, - qml.device("reference.qubit"), + "reference.qubit", ), # 10 ] @@ -136,14 +132,16 @@ def atol_for_shots(shots): return 1e-2 if shots else 1e-6 -@pytest.mark.parametrize("execute_kwargs, shots, device", test_matrix) +@pytest.mark.parametrize("execute_kwargs, shots, device_name", test_matrix) class TestTensorflowExecuteIntegration: """Test the tensorflow interface execute function integrates well for both forward and backward execution""" - def test_execution(self, execute_kwargs, shots, device): + def test_execution(self, execute_kwargs, shots, device_name, seed): """Test execution""" + device = qml.device(device_name, seed=seed) + def cost(a, b): ops1 = [qml.RY(a, wires=0), qml.RX(b, wires=0)] tape1 = qml.tape.QuantumScript(ops1, [qml.expval(qml.PauliZ(0))], shots=shots) @@ -173,12 +171,14 @@ def cost(a, b): assert qml.math.allclose(res[0], tf.cos(a) * tf.cos(b), atol=atol_for_shots(shots)) assert qml.math.allclose(res[1], tf.cos(a) * tf.cos(b), atol=atol_for_shots(shots)) - def test_scalar_jacobian(self, execute_kwargs, shots, device): + def test_scalar_jacobian(self, execute_kwargs, shots, device_name, seed): """Test scalar jacobian calculation""" a = tf.Variable(0.1, dtype=tf.float64) device_vjp = execute_kwargs.get("device_vjp", False) + device = qml.device(device_name, seed=seed) + def cost(a): tape = qml.tape.QuantumScript([qml.RY(a, 0)], [qml.expval(qml.PauliZ(0))], shots=shots) return execute([tape], device, **execute_kwargs)[0] @@ -198,11 +198,12 @@ def cost(a): assert np.allclose(res, expected, atol=atol_for_shots(shots), rtol=0) assert np.allclose(res, -tf.sin(a), atol=atol_for_shots(shots)) - def test_jacobian(self, execute_kwargs, shots, device): + def test_jacobian(self, execute_kwargs, shots, device_name, seed): """Test jacobian calculation""" a = tf.Variable(0.1) b = tf.Variable(0.2) + device = qml.device(device_name, seed=seed) device_vjp = execute_kwargs.get("device_vjp", False) def cost(a, b): @@ -225,10 +226,12 @@ def cost(a, b): for _r, _e in zip(jac, expected): assert np.allclose(_r, _e, atol=atol_for_shots(shots)) - def test_tape_no_parameters(self, execute_kwargs, shots, device): + def test_tape_no_parameters(self, execute_kwargs, shots, device_name, seed): """Test that a tape with no parameters is correctly ignored during the gradient computation""" + device = qml.device(device_name, seed=seed) + def cost(params): tape1 = qml.tape.QuantumScript( [qml.Hadamard(0)], [qml.expval(qml.PauliX(0))], shots=shots @@ -278,9 +281,11 @@ def cost(params): expected = [-tf.cos(y) * tf.sin(x), -tf.cos(x) * tf.sin(y)] assert np.allclose(grad, expected, atol=atol_for_shots(shots), rtol=0) - def test_tapes_with_different_return_size(self, execute_kwargs, shots, device): + def test_tapes_with_different_return_size(self, execute_kwargs, shots, device_name, seed): """Test that tapes wit different can be executed and differentiated.""" + device = qml.device(device_name, seed=seed) + if ( execute_kwargs["gradient_fn"] == "adjoint" and execute_kwargs["interface"] == "tf-autograph" @@ -339,10 +344,11 @@ def cost(params): assert np.allclose(jac[0, 1], d2, atol=atol_for_shots(shots)) assert np.allclose(jac[3, 1], d2, atol=atol_for_shots(shots)) - def test_reusing_quantum_tape(self, execute_kwargs, shots, device): + def test_reusing_quantum_tape(self, execute_kwargs, shots, device_name, seed): """Test re-using a quantum tape by passing new parameters""" a = tf.Variable(0.1) b = tf.Variable(0.2) + device = qml.device(device_name, seed=seed) tape = qml.tape.QuantumScript( [qml.RY(a, 0), qml.RX(b, 1), qml.CNOT((0, 1))], @@ -386,11 +392,12 @@ def cost(a, b): for _j, _e in zip(jac, expected): assert np.allclose(_j, _e, atol=atol_for_shots(shots), rtol=0) - def test_classical_processing(self, execute_kwargs, device, shots): + def test_classical_processing(self, execute_kwargs, device_name, seed, shots): """Test classical processing within the quantum tape""" a = tf.Variable(0.1, dtype=tf.float64) b = tf.constant(0.2, dtype=tf.float64) c = tf.Variable(0.3, dtype=tf.float64) + device = qml.device(device_name, seed=seed) device_vjp = execute_kwargs.get("device_vjp", False) @@ -416,10 +423,11 @@ def cost(a, b, c): # I tried getting analytic results for this circuit but I kept being wrong and am giving up - def test_no_trainable_parameters(self, execute_kwargs, shots, device): + def test_no_trainable_parameters(self, execute_kwargs, shots, device_name, seed): """Test evaluation and Jacobian if there are no trainable parameters""" a = tf.constant(0.1) b = tf.constant(0.2) + device = qml.device(device_name, seed=seed) def cost(a, b): ops = [qml.RY(a, 0), qml.RX(b, 0), qml.CNOT((0, 1))] @@ -445,12 +453,12 @@ def loss(a, b): res = tape.gradient(loss_res, [a, b]) assert all(r is None for r in res) - def test_matrix_parameter(self, execute_kwargs, device, shots): + def test_matrix_parameter(self, execute_kwargs, device_name, seed, shots): """Test that the tensorflow interface works correctly with a matrix parameter""" U = tf.constant([[0, 1], [1, 0]], dtype=tf.complex128) a = tf.Variable(0.1) - + device = qml.device(device_name, seed=seed) device_vjp = execute_kwargs.get("device_vjp", False) def cost(a, U): @@ -467,10 +475,11 @@ def cost(a, U): assert isinstance(jac, tf.Tensor) assert np.allclose(jac, tf.sin(a), atol=atol_for_shots(shots), rtol=0) - def test_differentiable_expand(self, execute_kwargs, device, shots): + def test_differentiable_expand(self, execute_kwargs, device_name, seed, shots): """Test that operation and nested tapes expansion is differentiable""" + device = qml.device(device_name, seed=seed) device_vjp = execute_kwargs.get("device_vjp", False) class U3(qml.U3): @@ -527,10 +536,12 @@ def cost_fn(a, p): ) assert np.allclose(res, expected, atol=atol_for_shots(shots), rtol=0) - def test_probability_differentiation(self, execute_kwargs, device, shots): + def test_probability_differentiation(self, execute_kwargs, device_name, seed, shots): """Tests correct output shape and evaluation for a tape with prob outputs""" + device = qml.device(device_name, seed=seed) + def cost(x, y): ops = [qml.RX(x, 0), qml.RY(y, 1), qml.CNOT((0, 1))] m = [qml.probs(wires=0), qml.probs(wires=1)] @@ -590,11 +601,12 @@ def cost(x, y): assert np.allclose(res[0], expected[0], atol=atol_for_shots(shots), rtol=0) assert np.allclose(res[1], expected[1], atol=atol_for_shots(shots), rtol=0) - def test_ragged_differentiation(self, execute_kwargs, device, shots): + def test_ragged_differentiation(self, execute_kwargs, device_name, seed, shots): """Tests correct output shape and evaluation for a tape with prob and expval outputs""" device_vjp = execute_kwargs.get("device_vjp", False) + device = qml.device(device_name, seed=seed) def cost(x, y): ops = [qml.RX(x, wires=0), qml.RY(y, 1), qml.CNOT((0, 1))] @@ -716,16 +728,18 @@ def cost_fn(x): assert hess is None -@pytest.mark.parametrize("execute_kwargs, shots, device", test_matrix) +@pytest.mark.parametrize("execute_kwargs, shots, device_name", test_matrix) @pytest.mark.usefixtures("use_legacy_and_new_opmath") class TestHamiltonianWorkflows: """Test that tapes ending with expectations of Hamiltonians provide correct results and gradients""" @pytest.fixture - def cost_fn(self, execute_kwargs, shots, device): + def cost_fn(self, execute_kwargs, shots, device_name, seed): """Cost function for gradient tests""" + device = qml.device(device_name, seed=seed) + def _cost_fn(weights, coeffs1, coeffs2): obs1 = [qml.PauliZ(0), qml.PauliZ(0) @ qml.PauliX(1), qml.PauliY(0)] H1 = qml.Hamiltonian(coeffs1, obs1) diff --git a/tests/interfaces/test_tensorflow_autograph_qnode_shot_vector.py b/tests/interfaces/test_tensorflow_autograph_qnode_shot_vector.py index 227e8a96e3c..9045992d467 100644 --- a/tests/interfaces/test_tensorflow_autograph_qnode_shot_vector.py +++ b/tests/interfaces/test_tensorflow_autograph_qnode_shot_vector.py @@ -18,7 +18,6 @@ import pennylane as qml from pennylane import numpy as np from pennylane import qnode -from pennylane.devices import DefaultQubit pytestmark = pytest.mark.tf @@ -34,9 +33,9 @@ } qubit_device_and_diff_method = [ - [DefaultQubit(seed=123), "finite-diff"], - [DefaultQubit(seed=123), "parameter-shift"], - [DefaultQubit(seed=123), "spsa"], + ["default.qubit", "finite-diff"], + ["default.qubit", "parameter-shift"], + ["default.qubit", "spsa"], ] TOLS = { @@ -49,13 +48,14 @@ @pytest.fixture def gradient_kwargs(request): diff_method = request.node.funcargs["diff_method"] + seed = request.getfixturevalue("seed") return kwargs[diff_method] | ( - {"sampler_rng": np.random.default_rng(42)} if diff_method == "spsa" else {} + {"sampler_rng": np.random.default_rng(seed)} if diff_method == "spsa" else {} ) @pytest.mark.parametrize("shots,num_copies", shots_and_num_copies) -@pytest.mark.parametrize("dev,diff_method", qubit_device_and_diff_method) +@pytest.mark.parametrize("dev_name,diff_method", qubit_device_and_diff_method) @pytest.mark.parametrize( "decorator,interface", [(tf.function, "tf"), (lambda x: x, "tf-autograph")], @@ -64,12 +64,17 @@ class TestReturnWithShotVectors: """Class to test the shape of the Grad/Jacobian/Hessian with different return types and shot vectors.""" def test_jac_single_measurement_param( - self, dev, diff_method, gradient_kwargs, shots, num_copies, decorator, interface + self, dev_name, seed, diff_method, gradient_kwargs, shots, num_copies, decorator, interface ): """For one measurement and one param, the gradient is a float.""" @decorator - @qnode(dev, diff_method=diff_method, interface=interface, **gradient_kwargs) + @qnode( + qml.device(dev_name, seed=seed), + diff_method=diff_method, + interface=interface, + **gradient_kwargs, + ) def circuit(a, **_): qml.RY(a, wires=0) qml.RX(0.7, wires=0) @@ -87,12 +92,17 @@ def circuit(a, **_): assert jac.shape == (num_copies,) def test_jac_single_measurement_multiple_param( - self, dev, diff_method, gradient_kwargs, shots, num_copies, decorator, interface + self, dev_name, seed, diff_method, gradient_kwargs, shots, num_copies, decorator, interface ): """For one measurement and multiple param, the gradient is a tuple of arrays.""" @decorator - @qnode(dev, diff_method=diff_method, interface=interface, **gradient_kwargs) + @qnode( + qml.device(dev_name, seed=seed), + diff_method=diff_method, + interface=interface, + **gradient_kwargs, + ) def circuit(a, b, **_): qml.RY(a, wires=0) qml.RX(b, wires=0) @@ -114,12 +124,17 @@ def circuit(a, b, **_): assert j.shape == (num_copies,) def test_jacobian_single_measurement_multiple_param_array( - self, dev, diff_method, gradient_kwargs, shots, num_copies, decorator, interface + self, dev_name, seed, diff_method, gradient_kwargs, shots, num_copies, decorator, interface ): """For one measurement and multiple param as a single array params, the gradient is an array.""" @decorator - @qnode(dev, diff_method=diff_method, interface=interface, **gradient_kwargs) + @qnode( + qml.device(dev_name, seed=seed), + diff_method=diff_method, + interface=interface, + **gradient_kwargs, + ) def circuit(a, **_): qml.RY(a[0], wires=0) qml.RX(a[1], wires=0) @@ -137,13 +152,18 @@ def circuit(a, **_): assert jac.shape == (num_copies, 2) def test_jacobian_single_measurement_param_probs( - self, dev, diff_method, gradient_kwargs, shots, num_copies, decorator, interface + self, dev_name, seed, diff_method, gradient_kwargs, shots, num_copies, decorator, interface ): """For a multi dimensional measurement (probs), check that a single array is returned with the correct dimension""" @decorator - @qnode(dev, diff_method=diff_method, interface=interface, **gradient_kwargs) + @qnode( + qml.device(dev_name, seed=seed), + diff_method=diff_method, + interface=interface, + **gradient_kwargs, + ) def circuit(a, **_): qml.RY(a, wires=0) qml.RX(0.7, wires=0) @@ -161,13 +181,18 @@ def circuit(a, **_): assert jac.shape == (num_copies, 4) def test_jacobian_single_measurement_probs_multiple_param( - self, dev, diff_method, gradient_kwargs, shots, num_copies, decorator, interface + self, dev_name, seed, diff_method, gradient_kwargs, shots, num_copies, decorator, interface ): """For a multi dimensional measurement (probs), check that a single tuple is returned containing arrays with the correct dimension""" @decorator - @qnode(dev, diff_method=diff_method, interface=interface, **gradient_kwargs) + @qnode( + qml.device(dev_name, seed=seed), + diff_method=diff_method, + interface=interface, + **gradient_kwargs, + ) def circuit(a, b, **_): qml.RY(a, wires=0) qml.RX(b, wires=0) @@ -189,13 +214,18 @@ def circuit(a, b, **_): assert j.shape == (num_copies, 4) def test_jacobian_single_measurement_probs_multiple_param_single_array( - self, dev, diff_method, gradient_kwargs, shots, num_copies, decorator, interface + self, dev_name, seed, diff_method, gradient_kwargs, shots, num_copies, decorator, interface ): """For a multi dimensional measurement (probs), check that a single tuple is returned containing arrays with the correct dimension""" @decorator - @qnode(dev, diff_method=diff_method, interface=interface, **gradient_kwargs) + @qnode( + qml.device(dev_name, seed=seed), + diff_method=diff_method, + interface=interface, + **gradient_kwargs, + ) def circuit(a, **_): qml.RY(a[0], wires=0) qml.RX(a[1], wires=0) @@ -213,14 +243,20 @@ def circuit(a, **_): assert jac.shape == (num_copies, 4, 2) def test_jacobian_expval_expval_multiple_params( - self, dev, diff_method, gradient_kwargs, shots, num_copies, decorator, interface + self, dev_name, seed, diff_method, gradient_kwargs, shots, num_copies, decorator, interface ): """The gradient of multiple measurements with multiple params return a tuple of arrays.""" par_0 = tf.Variable(1.5, dtype=tf.float64) par_1 = tf.Variable(0.7, dtype=tf.float64) @decorator - @qnode(dev, diff_method=diff_method, interface=interface, max_diff=1, **gradient_kwargs) + @qnode( + qml.device(dev_name, seed=seed), + diff_method=diff_method, + interface=interface, + max_diff=1, + **gradient_kwargs, + ) def circuit(x, y, **_): qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) @@ -240,12 +276,17 @@ def circuit(x, y, **_): assert j.shape == (num_copies, 2) def test_jacobian_expval_expval_multiple_params_array( - self, dev, diff_method, gradient_kwargs, shots, num_copies, decorator, interface + self, dev_name, seed, diff_method, gradient_kwargs, shots, num_copies, decorator, interface ): """The jacobian of multiple measurements with a multiple params array return a single array.""" @decorator - @qnode(dev, diff_method=diff_method, interface=interface, **gradient_kwargs) + @qnode( + qml.device(dev_name, seed=seed), + diff_method=diff_method, + interface=interface, + **gradient_kwargs, + ) def circuit(a, **_): qml.RY(a[0], wires=0) qml.RX(a[1], wires=0) @@ -264,12 +305,17 @@ def circuit(a, **_): assert jac.shape == (num_copies, 2, 3) def test_jacobian_multiple_measurement_single_param( - self, dev, diff_method, gradient_kwargs, shots, num_copies, decorator, interface + self, dev_name, seed, diff_method, gradient_kwargs, shots, num_copies, decorator, interface ): """The jacobian of multiple measurements with a single params return an array.""" @decorator - @qnode(dev, diff_method=diff_method, interface=interface, **gradient_kwargs) + @qnode( + qml.device(dev_name, seed=seed), + diff_method=diff_method, + interface=interface, + **gradient_kwargs, + ) def circuit(a, **_): qml.RY(a, wires=0) qml.RX(0.7, wires=0) @@ -287,12 +333,17 @@ def circuit(a, **_): assert jac.shape == (num_copies, 5) def test_jacobian_multiple_measurement_multiple_param( - self, dev, diff_method, gradient_kwargs, shots, num_copies, decorator, interface + self, dev_name, diff_method, gradient_kwargs, shots, num_copies, decorator, interface, seed ): """The jacobian of multiple measurements with a multiple params return a tuple of arrays.""" @decorator - @qnode(dev, diff_method=diff_method, interface=interface, **gradient_kwargs) + @qnode( + qml.device(dev_name, seed=seed), + diff_method=diff_method, + interface=interface, + **gradient_kwargs, + ) def circuit(a, b, **_): qml.RY(a, wires=0) qml.RX(b, wires=0) @@ -314,12 +365,17 @@ def circuit(a, b, **_): assert j.shape == (num_copies, 5) def test_jacobian_multiple_measurement_multiple_param_array( - self, dev, diff_method, gradient_kwargs, shots, num_copies, decorator, interface + self, dev_name, seed, diff_method, gradient_kwargs, shots, num_copies, decorator, interface ): """The jacobian of multiple measurements with a multiple params array return a single array.""" @decorator - @qnode(dev, diff_method=diff_method, interface=interface, **gradient_kwargs) + @qnode( + qml.device(dev_name, seed=seed), + diff_method=diff_method, + interface=interface, + **gradient_kwargs, + ) def circuit(a, **_): qml.RY(a[0], wires=0) qml.RX(a[1], wires=0) @@ -339,7 +395,7 @@ def circuit(a, **_): @pytest.mark.slow @pytest.mark.parametrize("shots,num_copies", shots_and_num_copies_hess) -@pytest.mark.parametrize("dev,diff_method", qubit_device_and_diff_method) +@pytest.mark.parametrize("dev_name,diff_method", qubit_device_and_diff_method) @pytest.mark.parametrize( "decorator,interface", [(tf.function, "tf"), (lambda x: x, "tf-autograph")], @@ -348,7 +404,7 @@ class TestReturnShotVectorHessian: """Class to test the shape of the Hessian with different return types and shot vectors.""" def test_hessian_expval_multiple_params( - self, dev, diff_method, gradient_kwargs, shots, num_copies, decorator, interface + self, dev_name, seed, diff_method, gradient_kwargs, shots, num_copies, decorator, interface ): """The hessian of a single measurement with multiple params return a tuple of arrays.""" @@ -360,7 +416,13 @@ def test_hessian_expval_multiple_params( par_1 = tf.Variable(0.7, dtype=tf.float64) @decorator - @qnode(dev, diff_method=diff_method, interface=interface, max_diff=2, **gradient_kwargs) + @qnode( + qml.device(dev_name, seed=seed), + diff_method=diff_method, + interface=interface, + max_diff=2, + **gradient_kwargs, + ) def circuit(x, y, **_): qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) @@ -388,7 +450,7 @@ def circuit(x, y, **_): @pytest.mark.parametrize("shots,num_copies", shots_and_num_copies) -@pytest.mark.parametrize("dev,diff_method", qubit_device_and_diff_method) +@pytest.mark.parametrize("dev_name,diff_method", qubit_device_and_diff_method) @pytest.mark.parametrize( "decorator,interface", [(tf.function, "tf"), (lambda x: x, "tf-autograph")], @@ -397,7 +459,7 @@ class TestReturnShotVectorIntegration: """Tests for the integration of shots with the TF interface.""" def test_single_expectation_value( - self, dev, diff_method, gradient_kwargs, shots, num_copies, decorator, interface + self, dev_name, seed, diff_method, gradient_kwargs, shots, num_copies, decorator, interface ): """Tests correct output shape and evaluation for a tape with a single expval output""" @@ -405,7 +467,12 @@ def test_single_expectation_value( y = tf.Variable(-0.654, dtype=tf.float64) @decorator - @qnode(dev, diff_method=diff_method, interface=interface, **gradient_kwargs) + @qnode( + qml.device(dev_name, seed=seed), + diff_method=diff_method, + interface=interface, + **gradient_kwargs, + ) def circuit(x, y, **_): qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) @@ -430,7 +497,7 @@ def circuit(x, y, **_): assert np.allclose(res, exp, atol=tol, rtol=0) def test_prob_expectation_values( - self, dev, diff_method, gradient_kwargs, shots, num_copies, decorator, interface + self, dev_name, seed, diff_method, gradient_kwargs, shots, num_copies, decorator, interface ): """Tests correct output shape and evaluation for a tape with prob and expval outputs""" @@ -438,7 +505,12 @@ def test_prob_expectation_values( y = tf.Variable(-0.654, dtype=tf.float64) @decorator - @qnode(dev, diff_method=diff_method, interface=interface, **gradient_kwargs) + @qnode( + qml.device(dev_name, seed=seed), + diff_method=diff_method, + interface=interface, + **gradient_kwargs, + ) def circuit(x, y, **_): qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) diff --git a/tests/interfaces/test_tensorflow_qnode.py b/tests/interfaces/test_tensorflow_qnode.py index c01d32091c6..109f5d5cf4e 100644 --- a/tests/interfaces/test_tensorflow_qnode.py +++ b/tests/interfaces/test_tensorflow_qnode.py @@ -42,7 +42,6 @@ ] TOL_FOR_SPSA = 1.0 -SEED_FOR_SPSA = 32651 H_FOR_SPSA = 0.01 interface_and_qubit_device_and_diff_method = [ @@ -163,7 +162,7 @@ def circuit(p1, p2=y, **kwargs): expected = "0: ──RX(0.10)──RX(0.40)─╭●─┤ \n1: ──RY(0.06)───────────╰X─┤ " assert result == expected - def test_jacobian(self, dev, diff_method, grad_on_execution, device_vjp, tol, interface): + def test_jacobian(self, dev, diff_method, grad_on_execution, device_vjp, tol, interface, seed): """Test jacobian calculation""" kwargs = { "diff_method": diff_method, @@ -172,7 +171,7 @@ def test_jacobian(self, dev, diff_method, grad_on_execution, device_vjp, tol, in "device_vjp": device_vjp, } if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA @@ -391,7 +390,7 @@ def circuit(U, a): assert np.allclose(res, tf.sin(a), atol=tol, rtol=0) def test_differentiable_expand( - self, dev, diff_method, grad_on_execution, device_vjp, tol, interface + self, dev, diff_method, grad_on_execution, device_vjp, tol, interface, seed ): """Test that operation and nested tapes expansion is differentiable""" @@ -402,7 +401,7 @@ def test_differentiable_expand( "device_vjp": device_vjp, } if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA @@ -564,7 +563,7 @@ class TestQubitIntegration: """Tests that ensure various qubit circuits integrate correctly""" def test_probability_differentiation( - self, dev, diff_method, grad_on_execution, device_vjp, tol, interface + self, dev, diff_method, grad_on_execution, device_vjp, tol, interface, seed ): """Tests correct output shape and evaluation for a tape with multiple probs outputs""" @@ -579,7 +578,7 @@ def test_probability_differentiation( "device_vjp": device_vjp, } if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA @@ -621,7 +620,7 @@ def circuit(x, y): assert np.allclose(res, expected, atol=tol, rtol=0) def test_ragged_differentiation( - self, dev, diff_method, grad_on_execution, device_vjp, tol, interface + self, dev, diff_method, grad_on_execution, device_vjp, tol, interface, seed ): """Tests correct output shape and evaluation for a tape with prob and expval outputs""" @@ -636,7 +635,7 @@ def test_ragged_differentiation( "device_vjp": device_vjp, } if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA @@ -964,7 +963,7 @@ def cost_fn(x, y): @pytest.mark.parametrize("state", [[1], [0, 1]]) # Basis state and state vector @pytest.mark.parametrize("dtype", ("int32", "int64")) def test_projector( - self, state, dev, diff_method, grad_on_execution, device_vjp, tol, interface, dtype + self, state, dev, diff_method, grad_on_execution, device_vjp, tol, interface, dtype, seed ): """Test that the variance of a projector is correctly returned""" kwargs = { @@ -978,7 +977,7 @@ def test_projector( if diff_method == "hadamard": pytest.skip("Variance not implemented yet.") elif diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA if dev.name == "reference.qubit": @@ -1148,7 +1147,7 @@ def circuit(x, y): @pytest.mark.parametrize("max_diff", [1, 2]) def test_hamiltonian_expansion_analytic( - self, dev, diff_method, grad_on_execution, device_vjp, max_diff, tol, interface + self, dev, diff_method, grad_on_execution, device_vjp, max_diff, tol, interface, seed ): """Test that if there are non-commuting groups and the number of shots is None the first and second order gradients are correctly evaluated""" @@ -1162,7 +1161,7 @@ def test_hamiltonian_expansion_analytic( if diff_method in ["adjoint", "hadamard"]: pytest.skip("The adjoint/hadamard method does not yet support Hamiltonians") elif diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA @@ -1213,7 +1212,7 @@ def circuit(data, weights, coeffs): @pytest.mark.parametrize("max_diff", [1, 2]) def test_hamiltonian_finite_shots( - self, dev, diff_method, grad_on_execution, device_vjp, max_diff, interface + self, dev, diff_method, grad_on_execution, device_vjp, max_diff, interface, seed ): """Test that the Hamiltonian is correctly measured if there are non-commuting groups and the number of shots is finite @@ -1225,7 +1224,7 @@ def test_hamiltonian_finite_shots( elif diff_method == "spsa": gradient_kwargs = { "h": H_FOR_SPSA, - "sampler_rng": np.random.default_rng(SEED_FOR_SPSA), + "sampler_rng": np.random.default_rng(seed), "num_directions": 20, } tol = TOL_FOR_SPSA diff --git a/tests/interfaces/test_tensorflow_qnode_shot_vector.py b/tests/interfaces/test_tensorflow_qnode_shot_vector.py index be55538c3e4..037469657bc 100644 --- a/tests/interfaces/test_tensorflow_qnode_shot_vector.py +++ b/tests/interfaces/test_tensorflow_qnode_shot_vector.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Integration tests for using the TF interface with shot vectors and with a QNode""" -# pylint: disable=too-many-arguments,unexpected-keyword-arg,redefined-outer-name +# pylint: disable=too-many-arguments,unexpected-keyword-arg,redefined-outer-name,unused-argument import pytest import pennylane as qml @@ -54,7 +54,9 @@ def gradient_kwargs(request): diff_method = request.node.funcargs["diff_method"] return kwargs[diff_method] | ( - {"sampler_rng": np.random.default_rng(42)} if diff_method == "spsa" else {} + {"sampler_rng": np.random.default_rng(request.getfixturevalue("seed"))} + if diff_method == "spsa" + else {} ) @@ -64,7 +66,7 @@ class TestReturnWithShotVectors: """Class to test the shape of the Grad/Jacobian/Hessian with different return types and shot vectors.""" def test_jac_single_measurement_param( - self, dev, diff_method, gradient_kwargs, shots, num_copies, interface + self, dev, diff_method, gradient_kwargs, shots, num_copies, interface, seed ): """For one measurement and one param, the gradient is a float.""" @@ -86,7 +88,7 @@ def circuit(a): assert jac.shape == (num_copies,) def test_jac_single_measurement_multiple_param( - self, dev, diff_method, gradient_kwargs, shots, num_copies, interface + self, dev, diff_method, gradient_kwargs, shots, num_copies, interface, seed ): """For one measurement and multiple param, the gradient is a tuple of arrays.""" @@ -112,7 +114,7 @@ def circuit(a, b): assert j.shape == (num_copies,) def test_jacobian_single_measurement_multiple_param_array( - self, dev, diff_method, gradient_kwargs, shots, num_copies, interface + self, dev, diff_method, gradient_kwargs, shots, num_copies, interface, seed ): """For one measurement and multiple param as a single array params, the gradient is an array.""" @@ -134,7 +136,7 @@ def circuit(a): assert jac.shape == (num_copies, 2) def test_jacobian_single_measurement_param_probs( - self, dev, diff_method, gradient_kwargs, shots, num_copies, interface + self, dev, diff_method, gradient_kwargs, shots, num_copies, interface, seed ): """For a multi dimensional measurement (probs), check that a single array is returned with the correct dimension""" @@ -157,7 +159,7 @@ def circuit(a): assert jac.shape == (num_copies, 4) def test_jacobian_single_measurement_probs_multiple_param( - self, dev, diff_method, gradient_kwargs, shots, num_copies, interface + self, dev, diff_method, gradient_kwargs, shots, num_copies, interface, seed ): """For a multi dimensional measurement (probs), check that a single tuple is returned containing arrays with the correct dimension""" @@ -184,7 +186,7 @@ def circuit(a, b): assert j.shape == (num_copies, 4) def test_jacobian_single_measurement_probs_multiple_param_single_array( - self, dev, diff_method, gradient_kwargs, shots, num_copies, interface + self, dev, diff_method, gradient_kwargs, shots, num_copies, interface, seed ): """For a multi dimensional measurement (probs), check that a single tuple is returned containing arrays with the correct dimension""" @@ -207,7 +209,7 @@ def circuit(a): assert jac.shape == (num_copies, 4, 2) def test_jacobian_expval_expval_multiple_params( - self, dev, diff_method, gradient_kwargs, shots, num_copies, interface + self, dev, diff_method, gradient_kwargs, shots, num_copies, interface, seed ): """The gradient of multiple measurements with multiple params return a tuple of arrays.""" @@ -234,7 +236,7 @@ def circuit(x, y): assert j.shape == (num_copies, 2) def test_jacobian_expval_expval_multiple_params_array( - self, dev, diff_method, gradient_kwargs, shots, num_copies, interface + self, dev, diff_method, gradient_kwargs, shots, num_copies, interface, seed ): """The jacobian of multiple measurements with a multiple params array return a single array.""" @@ -257,7 +259,7 @@ def circuit(a): assert jac.shape == (num_copies, 2, 3) def test_jacobian_multiple_measurement_single_param( - self, dev, diff_method, gradient_kwargs, shots, num_copies, interface + self, dev, diff_method, gradient_kwargs, shots, num_copies, interface, seed ): """The jacobian of multiple measurements with a single params return an array.""" @@ -279,7 +281,7 @@ def circuit(a): assert jac.shape == (num_copies, 5) def test_jacobian_multiple_measurement_multiple_param( - self, dev, diff_method, gradient_kwargs, shots, num_copies, interface + self, dev, diff_method, gradient_kwargs, shots, num_copies, interface, seed ): """The jacobian of multiple measurements with a multiple params return a tuple of arrays.""" @@ -305,7 +307,7 @@ def circuit(a, b): assert j.shape == (num_copies, 5) def test_jacobian_multiple_measurement_multiple_param_array( - self, dev, diff_method, gradient_kwargs, shots, num_copies, interface + self, dev, diff_method, gradient_kwargs, shots, num_copies, interface, seed ): """The jacobian of multiple measurements with a multiple params array return a single array.""" @@ -334,7 +336,7 @@ class TestReturnShotVectorHessian: """Class to test the shape of the Hessian with different return types and shot vectors.""" def test_hessian_expval_multiple_params( - self, dev, diff_method, gradient_kwargs, shots, num_copies, interface + self, dev, diff_method, gradient_kwargs, shots, num_copies, interface, seed ): """The hessian of a single measurement with multiple params return a tuple of arrays.""" @@ -365,7 +367,7 @@ def circuit(x, y): assert h.shape == (2, num_copies) def test_hessian_expval_multiple_param_array( - self, dev, diff_method, gradient_kwargs, shots, num_copies, interface + self, dev, diff_method, gradient_kwargs, shots, num_copies, interface, seed ): """The hessian of single measurement with a multiple params array return a single array.""" @@ -391,7 +393,7 @@ def circuit(x): assert hess.shape == (num_copies, 2, 2) def test_hessian_probs_expval_multiple_params( - self, dev, diff_method, gradient_kwargs, shots, num_copies, interface + self, dev, diff_method, gradient_kwargs, shots, num_copies, interface, seed ): """The hessian of multiple measurements with multiple params return a tuple of arrays.""" @@ -422,7 +424,7 @@ def circuit(x, y): assert h.shape == (2, num_copies, 3) def test_hessian_expval_probs_multiple_param_array( - self, dev, diff_method, gradient_kwargs, shots, num_copies, interface + self, dev, diff_method, gradient_kwargs, shots, num_copies, interface, seed ): """The hessian of multiple measurements with a multiple param array return a single array.""" @@ -457,7 +459,7 @@ class TestReturnShotVectorIntegration: """Tests for the integration of shots with the TF interface.""" def test_single_expectation_value( - self, dev, diff_method, gradient_kwargs, shots, num_copies, interface + self, dev, diff_method, gradient_kwargs, shots, num_copies, interface, seed ): """Tests correct output shape and evaluation for a tape with a single expval output""" @@ -490,7 +492,7 @@ def circuit(x, y): assert np.allclose(res, exp, atol=tol, rtol=0) def test_prob_expectation_values( - self, dev, diff_method, gradient_kwargs, shots, num_copies, interface + self, dev, diff_method, gradient_kwargs, shots, num_copies, interface, seed ): """Tests correct output shape and evaluation for a tape with prob and expval outputs""" diff --git a/tests/interfaces/test_torch.py b/tests/interfaces/test_torch.py index d70fdcc39c1..8b1d5ddb978 100644 --- a/tests/interfaces/test_torch.py +++ b/tests/interfaces/test_torch.py @@ -127,17 +127,23 @@ def cost_cache(x): assert expected_runs_ideal < expected_runs +def get_device(dev_name, seed): + if dev_name == "param_shift.qubit": + return ParamShiftDerivativesDevice(seed=seed) + return qml.device(dev_name, seed=seed) + + # add tests for lightning 2 when possible # set rng for device when possible test_matrix = [ - ({"gradient_fn": param_shift}, Shots(100000), DefaultQubit(seed=42)), - ({"gradient_fn": param_shift}, Shots((100000, 100000)), DefaultQubit(seed=42)), - ({"gradient_fn": param_shift}, Shots(None), DefaultQubit()), - ({"gradient_fn": "backprop"}, Shots(None), DefaultQubit()), + ({"gradient_fn": param_shift}, Shots(100000), "default.qubit"), + ({"gradient_fn": param_shift}, Shots((100000, 100000)), "default.qubit"), + ({"gradient_fn": param_shift}, Shots(None), "default.qubit"), + ({"gradient_fn": "backprop"}, Shots(None), "default.qubit"), ( {"gradient_fn": "adjoint", "grad_on_execution": True, "device_vjp": False}, Shots(None), - DefaultQubit(), + "default.qubit", ), ( { @@ -146,35 +152,25 @@ def cost_cache(x): "device_vjp": False, }, Shots(None), - DefaultQubit(), - ), - ({"gradient_fn": "adjoint", "device_vjp": True}, Shots(None), DefaultQubit()), - ( - {"gradient_fn": "device", "device_vjp": False}, - Shots((100000, 100000)), - ParamShiftDerivativesDevice(seed=42), - ), - ( - {"gradient_fn": "device", "device_vjp": True}, - Shots((100000, 100000)), - ParamShiftDerivativesDevice(seed=42), + "default.qubit", ), + ({"gradient_fn": "adjoint", "device_vjp": True}, Shots(None), "default.qubit"), + ({"gradient_fn": "device", "device_vjp": False}, Shots((100000, 100000)), "param_shift.qubit"), + ({"gradient_fn": "device", "device_vjp": True}, Shots((100000, 100000)), "param_shift.qubit"), ( {"gradient_fn": param_shift}, Shots(None), - qml.device( - "reference.qubit", - ), + "reference.qubit", ), ( {"gradient_fn": param_shift}, Shots(100000), - qml.device("reference.qubit", seed=42), + "reference.qubit", ), ( {"gradient_fn": param_shift}, Shots((100000, 100000)), - qml.device("reference.qubit", seed=42), + "reference.qubit", ), ] @@ -184,14 +180,16 @@ def atol_for_shots(shots): return 1e-2 if shots else 1e-6 -@pytest.mark.parametrize("execute_kwargs, shots, device", test_matrix) +@pytest.mark.parametrize("execute_kwargs, shots, device_name", test_matrix) class TestTorchExecuteIntegration: """Test the torch interface execute function integrates well for both forward and backward execution""" - def test_execution(self, execute_kwargs, shots, device): + def test_execution(self, execute_kwargs, shots, device_name, seed): """Test execution""" + device = get_device(device_name, seed) + def cost(a, b): ops1 = [qml.RY(a, wires=0), qml.RX(b, wires=0)] tape1 = qml.tape.QuantumScript(ops1, [qml.expval(qml.PauliZ(0))], shots=shots) @@ -226,9 +224,10 @@ def cost(a, b): for wire in range(2): assert qml.math.allclose(res[wire], exp, atol=atol_for_shots(shots)) - def test_scalar_jacobian(self, execute_kwargs, shots, device): + def test_scalar_jacobian(self, execute_kwargs, shots, device_name, seed): """Test scalar jacobian calculation""" a = torch.tensor(0.1, requires_grad=True) + device = get_device(device_name, seed) def cost(a): tape = qml.tape.QuantumScript([qml.RY(a, 0)], [qml.expval(qml.PauliZ(0))], shots=shots) @@ -253,11 +252,14 @@ def cost(a): assert torch.allclose(res, expected, atol=atol_for_shots(shots), rtol=0) assert torch.allclose(res, -torch.sin(a), atol=atol_for_shots(shots)) - def test_jacobian(self, execute_kwargs, shots, device): + @pytest.mark.local_salt(1) + def test_jacobian(self, execute_kwargs, shots, device_name, seed): """Test jacobian calculation""" a = torch.tensor(0.1, requires_grad=True) b = torch.tensor(0.2, requires_grad=True) + device = get_device(device_name, seed) + def cost(a, b): ops = [qml.RY(a, wires=0), qml.RX(b, wires=1), qml.CNOT(wires=[0, 1])] m = [qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliY(1))] @@ -297,10 +299,12 @@ def cost(a, b): for _r, _e in zip(res, expected): assert torch.allclose(_r, _e, atol=atol_for_shots(shots)) - def test_tape_no_parameters(self, execute_kwargs, shots, device): + def test_tape_no_parameters(self, execute_kwargs, shots, device_name, seed): """Test that a tape with no parameters is correctly ignored during the gradient computation""" + device = get_device(device_name, seed) + def cost(params): tape1 = qml.tape.QuantumScript( [qml.Hadamard(0)], [qml.expval(qml.PauliX(0))], shots=shots @@ -349,12 +353,14 @@ def cost(params): assert torch.allclose(params.grad, expected, atol=atol_for_shots(shots), rtol=0) @pytest.mark.skip("torch cannot reuse tensors in various computations") - def test_tapes_with_different_return_size(self, execute_kwargs, shots, device): + def test_tapes_with_different_return_size(self, execute_kwargs, shots, device_name, seed): """Test that tapes wit different can be executed and differentiated.""" if execute_kwargs["gradient_fn"] == "backprop": pytest.xfail("backprop is not compatible with something about this situation.") + device = get_device(device_name, seed) + def cost(params): tape1 = qml.tape.QuantumScript( [qml.RY(params[0], 0), qml.RX(params[1], 0)], @@ -402,10 +408,11 @@ def cost(params): assert torch.allclose(jac[0, 1], d2, atol=atol_for_shots(shots)) # fails for torch assert torch.allclose(jac[3, 1], d2, atol=atol_for_shots(shots)) - def test_reusing_quantum_tape(self, execute_kwargs, shots, device): + def test_reusing_quantum_tape(self, execute_kwargs, shots, device_name, seed): """Test re-using a quantum tape by passing new parameters""" a = torch.tensor(0.1, requires_grad=True) b = torch.tensor(0.2, requires_grad=True) + device = get_device(device_name, seed) tape = qml.tape.QuantumScript( [qml.RY(a, 0), qml.RX(b, 1), qml.CNOT((0, 1))], @@ -437,11 +444,12 @@ def cost(a, b): for _j, _e in zip(jac, expected): assert torch.allclose(_j, _e, atol=atol_for_shots(shots), rtol=0) - def test_classical_processing(self, execute_kwargs, device, shots): + def test_classical_processing(self, execute_kwargs, device_name, seed, shots): """Test classical processing within the quantum tape""" a = torch.tensor(0.1, requires_grad=True) b = torch.tensor(0.2, requires_grad=False) c = torch.tensor(0.3, requires_grad=True) + device = get_device(device_name, seed) def cost(a, b, c): ops = [ @@ -466,11 +474,13 @@ def cost(a, b, c): # I tried getting analytic results for this circuit but I kept being wrong and am giving up @pytest.mark.skip("torch handles gradients and jacobians differently") - def test_no_trainable_parameters(self, execute_kwargs, shots, device): + def test_no_trainable_parameters(self, execute_kwargs, shots, device_name, seed): """Test evaluation and Jacobian if there are no trainable parameters""" a = torch.tensor(0.1, requires_grad=False) b = torch.tensor(0.2, requires_grad=False) + device = get_device(device_name, seed) + def cost(a, b): ops = [qml.RY(a, 0), qml.RX(b, 0), qml.CNOT((0, 1))] m = [qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))] @@ -491,11 +501,12 @@ def loss(a, b): assert torch.allclose(torch.tensor([a.grad, b.grad]), 0) - def test_matrix_parameter(self, execute_kwargs, device, shots): + def test_matrix_parameter(self, execute_kwargs, device_name, seed, shots): """Test that the torch interface works correctly with a matrix parameter""" U = torch.tensor([[0, 1], [1, 0]], requires_grad=False, dtype=torch.float64) a = torch.tensor(0.1, requires_grad=True) + device = get_device(device_name, seed) def cost(a, U): ops = [qml.QubitUnitary(U, wires=0), qml.RY(a, wires=0)] @@ -509,10 +520,12 @@ def cost(a, U): assert isinstance(jac, torch.Tensor) assert torch.allclose(jac, torch.sin(a), atol=atol_for_shots(shots), rtol=0) - def test_differentiable_expand(self, execute_kwargs, device, shots): + def test_differentiable_expand(self, execute_kwargs, device_name, seed, shots): """Test that operation and nested tapes expansion is differentiable""" + device = get_device(device_name, seed) + class U3(qml.U3): """Dummy operator.""" @@ -575,9 +588,10 @@ def cost_fn(a, p): ) assert torch.allclose(res, expected, atol=atol_for_shots(shots), rtol=0) - def test_probability_differentiation(self, execute_kwargs, device, shots): + def test_probability_differentiation(self, execute_kwargs, device_name, seed, shots): """Tests correct output shape and evaluation for a tape with prob outputs""" + device = get_device(device_name, seed) def cost(x, y): ops = [qml.RX(x, 0), qml.RY(y, 1), qml.CNOT((0, 1))] @@ -627,9 +641,10 @@ def cost(x, y): assert torch.allclose(res[0], expected[0], atol=atol_for_shots(shots), rtol=0) assert torch.allclose(res[1], expected[1], atol=atol_for_shots(shots), rtol=0) - def test_ragged_differentiation(self, execute_kwargs, device, shots): + def test_ragged_differentiation(self, execute_kwargs, device_name, seed, shots): """Tests correct output shape and evaluation for a tape with prob and expval outputs""" + device = get_device(device_name, seed) def cost(x, y): ops = [qml.RX(x, wires=0), qml.RY(y, 1), qml.CNOT((0, 1))] @@ -742,15 +757,16 @@ def cost_fn(x): assert torch.allclose(res, expected, atol=tol, rtol=0) -@pytest.mark.parametrize("execute_kwargs, shots, device", test_matrix) +@pytest.mark.parametrize("execute_kwargs, shots, device_name", test_matrix) @pytest.mark.usefixtures("use_legacy_and_new_opmath") class TestHamiltonianWorkflows: """Test that tapes ending with expectations of Hamiltonians provide correct results and gradients""" @pytest.fixture - def cost_fn(self, execute_kwargs, shots, device): + def cost_fn(self, execute_kwargs, shots, device_name, seed): """Cost function for gradient tests""" + device = get_device(device_name, seed) def _cost_fn(weights, coeffs1, coeffs2): obs1 = [qml.PauliZ(0), qml.PauliZ(0) @ qml.PauliX(1), qml.PauliY(0)] diff --git a/tests/interfaces/test_torch_qnode.py b/tests/interfaces/test_torch_qnode.py index 5ecf181d343..c71b74108be 100644 --- a/tests/interfaces/test_torch_qnode.py +++ b/tests/interfaces/test_torch_qnode.py @@ -55,7 +55,6 @@ ] + [["torch"] + inner_list for inner_list in qubit_device_and_diff_method] TOL_FOR_SPSA = 1.0 -SEED_FOR_SPSA = 32651 H_FOR_SPSA = 0.01 @@ -168,7 +167,7 @@ def circuit(p1, p2=y, **kwargs): assert result == expected - def test_jacobian(self, interface, dev, diff_method, grad_on_execution, device_vjp, tol): + def test_jacobian(self, interface, dev, diff_method, grad_on_execution, device_vjp, tol, seed): """Test jacobian calculation""" kwargs = dict( diff_method=diff_method, @@ -177,7 +176,7 @@ def test_jacobian(self, interface, dev, diff_method, grad_on_execution, device_v device_vjp=device_vjp, ) if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA @@ -495,7 +494,7 @@ def circuit(U, a): assert np.allclose(a.grad, np.sin(a_val), atol=tol, rtol=0) def test_differentiable_expand( - self, interface, dev, diff_method, grad_on_execution, device_vjp, tol + self, interface, dev, diff_method, grad_on_execution, device_vjp, tol, seed ): """Test that operation and nested tapes expansion is differentiable""" @@ -506,7 +505,7 @@ def test_differentiable_expand( device_vjp=device_vjp, ) if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA @@ -662,7 +661,7 @@ class TestQubitIntegration: """Tests that ensure various qubit circuits integrate correctly""" def test_probability_differentiation( - self, interface, dev, diff_method, grad_on_execution, device_vjp, tol + self, interface, dev, diff_method, grad_on_execution, device_vjp, tol, seed ): """Tests correct output shape and evaluation for a tape with prob and expval outputs""" @@ -670,7 +669,7 @@ def test_probability_differentiation( pytest.xfail("lightning does not support measureing probabilities with adjoint.") kwargs = {} if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) tol = TOL_FOR_SPSA x_val = 0.543 @@ -720,7 +719,7 @@ def circuit(x, y): assert np.allclose(jac[1][1], res_3, atol=tol, rtol=0) def test_ragged_differentiation( - self, interface, dev, diff_method, grad_on_execution, device_vjp, tol + self, interface, dev, diff_method, grad_on_execution, device_vjp, tol, seed ): """Tests correct output shape and evaluation for a tape with prob and expval outputs""" @@ -733,7 +732,7 @@ def test_ragged_differentiation( device_vjp=device_vjp, ) if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA @@ -1115,7 +1114,7 @@ def cost_fn(x, y): @pytest.mark.parametrize("state", [[1], [0, 1]]) # Basis state and state vector def test_projector( - self, state, interface, dev, diff_method, grad_on_execution, device_vjp, tol + self, state, interface, dev, diff_method, grad_on_execution, device_vjp, tol, seed ): """Test that the variance of a projector is correctly returned""" kwargs = dict( @@ -1127,7 +1126,7 @@ def test_projector( if diff_method == "adjoint": pytest.skip("adjoint supports either all expvals or all diagonal measurements") if diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA elif diff_method == "hadamard": @@ -1297,7 +1296,7 @@ def circuit(x, y): @pytest.mark.parametrize("max_diff", [1, 2]) def test_hamiltonian_expansion_analytic( - self, dev, diff_method, grad_on_execution, max_diff, device_vjp, tol + self, dev, diff_method, grad_on_execution, max_diff, device_vjp, tol, seed ): """Test that if there are non-commuting groups and the number of shots is None @@ -1312,7 +1311,7 @@ def test_hamiltonian_expansion_analytic( if diff_method == "adjoint": pytest.skip("The adjoint method does not yet support Hamiltonians") elif diff_method == "spsa": - kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) + kwargs["sampler_rng"] = np.random.default_rng(seed) kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA elif diff_method == "hadamard": @@ -1382,7 +1381,7 @@ def circuit(data, weights, coeffs): @pytest.mark.parametrize("max_diff", [1, 2]) def test_hamiltonian_finite_shots( - self, dev, diff_method, device_vjp, grad_on_execution, max_diff + self, dev, diff_method, device_vjp, grad_on_execution, max_diff, seed ): """Test that the Hamiltonian is correctly measured if there are non-commuting groups and the number of shots is finite @@ -1394,7 +1393,7 @@ def test_hamiltonian_finite_shots( elif diff_method == "spsa": gradient_kwargs = { "h": H_FOR_SPSA, - "sampler_rng": np.random.default_rng(SEED_FOR_SPSA), + "sampler_rng": np.random.default_rng(seed), "num_directions": 20, } tol = TOL_FOR_SPSA diff --git a/tests/measurements/test_counts.py b/tests/measurements/test_counts.py index 08da35015c9..e49ab042718 100644 --- a/tests/measurements/test_counts.py +++ b/tests/measurements/test_counts.py @@ -91,10 +91,10 @@ def test_repr(self): class TestProcessSamples: """Unit tests for the counts.process_samples method""" - def test_counts_shape_single_wires(self): + def test_counts_shape_single_wires(self, seed): """Test that the counts output is correct for single wires""" shots = 1000 - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) samples = rng.choice([0, 1], size=(shots, 2)).astype(np.int64) result = qml.counts(wires=0).process_samples(samples, wire_order=[0]) @@ -104,11 +104,11 @@ def test_counts_shape_single_wires(self): assert result["0"] == np.count_nonzero(samples[:, 0] == 0) assert result["1"] == np.count_nonzero(samples[:, 0] == 1) - def test_counts_shape_multi_wires(self): + def test_counts_shape_multi_wires(self, seed): """Test that the counts function outputs counts of the right size for multiple wires""" shots = 1000 - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) samples = rng.choice([0, 1], size=(shots, 2)).astype(np.int64) result = qml.counts(wires=[0, 1]).process_samples(samples, wire_order=[0, 1]) @@ -128,11 +128,11 @@ def test_counts_shape_multi_wires(self): np.logical_and(samples[:, 0] == 1, samples[:, 1] == 1) ) - def test_counts_with_nan_samples(self): + def test_counts_with_nan_samples(self, seed): """Test that the counts function disregards failed measurements (samples including NaN values) when totalling counts""" shots = 1000 - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) samples = rng.choice([0, 1], size=(shots, 2)).astype(np.float64) samples[0][0] = np.nan @@ -173,10 +173,10 @@ def test_counts_multi_wires_no_overflow(self, n_wires, all_outcomes, batch_size) assert sum(result.values()) == shots assert all("-" not in sample for sample in result.keys()) - def test_counts_obs(self): + def test_counts_obs(self, seed): """Test that the counts function outputs counts of the right size for observables""" shots = 1000 - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) samples = rng.choice([0, 1], size=(shots, 2)).astype(np.int64) result = qml.counts(qml.PauliZ(0)).process_samples(samples, wire_order=[0]) @@ -186,11 +186,11 @@ def test_counts_obs(self): assert result[1] == np.count_nonzero(samples[:, 0] == 0) assert result[-1] == np.count_nonzero(samples[:, 0] == 1) - def test_count_eigvals(self): + def test_count_eigvals(self, seed): """Tests that eigvals are used instead of obs for counts""" shots = 100 - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) samples = rng.choice([0, 1], size=(shots, 2)).astype(np.int64) result = CountsMP(eigvals=[1, -1], wires=0).process_samples(samples, wire_order=[0]) assert len(result) == 2 @@ -198,11 +198,11 @@ def test_count_eigvals(self): assert result[1] == np.count_nonzero(samples[:, 0] == 0) assert result[-1] == np.count_nonzero(samples[:, 0] == 1) - def test_counts_shape_single_measurement_value(self): + def test_counts_shape_single_measurement_value(self, seed): """Test that the counts output is correct for single mid-circuit measurement values.""" shots = 1000 - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) samples = rng.choice([0, 1], size=(shots, 2)).astype(np.int64) mv = qml.measure(0) @@ -213,11 +213,11 @@ def test_counts_shape_single_measurement_value(self): assert result[0] == np.count_nonzero(samples[:, 0] == 0) assert result[1] == np.count_nonzero(samples[:, 0] == 1) - def test_counts_shape_composite_measurement_value(self): + def test_counts_shape_composite_measurement_value(self, seed): """Test that the counts output is correct for composite mid-circuit measurement values.""" shots = 1000 - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) samples = rng.choice([0, 1], size=(shots, 2)).astype(np.int64) m0 = qml.measure(0) m1 = qml.measure(1) @@ -230,11 +230,11 @@ def test_counts_shape_composite_measurement_value(self): assert result[0] == np.count_nonzero(samples == 0) assert result[1] == np.count_nonzero(samples == 1) - def test_counts_shape_measurement_value_list(self): + def test_counts_shape_measurement_value_list(self, seed): """Test that the counts output is correct for list mid-circuit measurement values.""" shots = 1000 - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) samples = rng.choice([0, 1], size=(shots, 2)).astype(np.int64) m0 = qml.measure(0) m1 = qml.measure(1) diff --git a/tests/measurements/test_expval.py b/tests/measurements/test_expval.py index 2ff27d0a91c..0aebcec600c 100644 --- a/tests/measurements/test_expval.py +++ b/tests/measurements/test_expval.py @@ -109,11 +109,11 @@ def circuit(phi): @pytest.mark.parametrize("shots", [None, 1111, [1111, 1111]]) @pytest.mark.parametrize("phi", np.arange(0, 2 * np.pi, np.pi / 3)) def test_observable_is_composite_measurement_value( - self, shots, phi, tol, tol_stochastic + self, shots, phi, tol, tol_stochastic, seed ): # pylint: disable=too-many-arguments """Test that expectation values for mid-circuit measurement values are correct for a composite measurement value.""" - dev = qml.device("default.qubit", seed=123) + dev = qml.device("default.qubit", seed=seed) @qml.qnode(dev) def circuit(phi): @@ -144,11 +144,11 @@ def expected_circuit(phi): res = func(phi, shots=shots) assert np.allclose(np.array(res), expected, atol=atol, rtol=0) - def test_eigvals_instead_of_observable(self): + def test_eigvals_instead_of_observable(self, seed): """Tests process samples with eigvals instead of observables""" shots = 100 - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) samples = rng.choice([0, 1], size=(shots, 2)).astype(np.int64) expected = qml.expval(qml.PauliZ(0)).process_samples(samples, [0, 1]) assert ( @@ -186,12 +186,13 @@ def test_shape(self, obs): assert res.shape(None, 1) == () assert res.shape(100, 1) == () + @pytest.mark.local_salt(2) @pytest.mark.parametrize("state", [np.array([0, 0, 0]), np.array([1, 0, 0, 0, 0, 0, 0, 0])]) @pytest.mark.parametrize("shots", [None, 1000, [1000, 1111]]) - def test_projector_expval(self, state, shots): + def test_projector_expval(self, state, shots, seed): """Tests that the expectation of a ``Projector`` object is computed correctly for both of its subclasses.""" - dev = qml.device("default.qubit", wires=3, shots=shots, seed=123) + dev = qml.device("default.qubit", wires=3, shots=shots, seed=seed) @qml.qnode(dev) def circuit(): @@ -335,7 +336,7 @@ def test_expval_process_density_matrix_no_wires(self, state, expected): result = mp.process_density_matrix(state, wire_order=qml.wires.Wires([0])) assert np.allclose(result, expected) - def test_batched_hamiltonian(self): + def test_batched_hamiltonian(self, seed): """Test that the expval interface works""" dev = qml.device("default.qubit") ops = (qml.Hadamard(0), qml.PauliZ(0) @ qml.PauliY(1) @ qml.PauliY(2) @ qml.PauliX(3)) @@ -347,7 +348,7 @@ def cost_circuit(params): qml.CNOT([0, 1]) return qml.expval(H) - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) params = rng.normal(0, np.pi, 4) energy = [cost_circuit(p) for p in params] energy_batched = cost_circuit(params) diff --git a/tests/measurements/test_probs.py b/tests/measurements/test_probs.py index 3c04b6de861..03e4b78d797 100644 --- a/tests/measurements/test_probs.py +++ b/tests/measurements/test_probs.py @@ -12,6 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. """Unit tests for the probs module""" + +# pylint:disable=too-many-arguments + from collections.abc import Sequence import numpy as np @@ -27,9 +30,9 @@ def fixture_init_state(): """Fixture that creates an initial state""" - def _init_state(n): + def _init_state(n, seed): """An initial state over n wires""" - rng = np.random.default_rng(42) + rng = np.random.default_rng(seed) state = rng.random([2**n]) + rng.random([2**n]) * 1j state /= np.linalg.norm(state) return state @@ -104,11 +107,11 @@ def circuit(): assert qml.math.allequal(res, [1, 0, 0, 0, 0, 0, 0, 0]) - def test_full_prob(self, init_state, tol): + def test_full_prob(self, init_state, tol, seed): """Test that the correct probability is returned.""" dev = qml.device("default.qubit", wires=4) - state = init_state(4) + state = init_state(4, seed) @qml.qnode(dev) def circuit(): @@ -119,11 +122,11 @@ def circuit(): expected = np.abs(state) ** 2 assert np.allclose(res, expected, atol=tol, rtol=0) - def test_marginal_prob(self, init_state, tol): + def test_marginal_prob(self, init_state, tol, seed): """Test that the correct marginal probability is returned.""" dev = qml.device("default.qubit", wires=4) - state = init_state(4) + state = init_state(4, seed) @qml.qnode(dev) def circuit(): @@ -135,11 +138,11 @@ def circuit(): expected = np.einsum("ijkl->jl", expected).flatten() assert np.allclose(res, expected, atol=tol, rtol=0) - def test_marginal_prob_more_wires(self, init_state, tol): + def test_marginal_prob_more_wires(self, init_state, tol, seed): """Test that the correct marginal probability is returned, when the states_to_binary method is used for probability computations.""" dev = qml.device("default.qubit", wires=4) - state = init_state(4) + state = init_state(4, seed) @qml.qnode(dev) def circuit(): @@ -332,11 +335,11 @@ def circuit(): @pytest.mark.parametrize("shots", (None, 500)) @pytest.mark.parametrize("obs", ([0, 1], qml.PauliZ(0) @ qml.PauliZ(1))) @pytest.mark.parametrize("params", ([np.pi / 2], [np.pi / 2, np.pi / 2, np.pi / 2])) - def test_integration_jax(self, tol_stochastic, shots, obs, params): + def test_integration_jax(self, tol_stochastic, shots, obs, params, seed): """Test the probability is correct for a known state preparation when jitted with JAX.""" jax = pytest.importorskip("jax") - dev = qml.device("default.qubit", wires=2, shots=shots, seed=jax.random.PRNGKey(0)) + dev = qml.device("default.qubit", wires=2, shots=shots, seed=jax.random.PRNGKey(seed)) params = jax.numpy.array(params) @qml.qnode(dev, diff_method=None) @@ -635,12 +638,12 @@ def circuit_rotated(x, y): @pytest.mark.parametrize("hermitian", [1 / np.sqrt(2) * np.array([[1, 1], [1, -1]])]) @pytest.mark.parametrize("wire", [0, 1, 2, 3]) - def test_prob_generalize_initial_state(self, hermitian, wire, init_state, tol): + def test_prob_generalize_initial_state(self, hermitian, wire, init_state, tol, seed): """Test that the correct probability is returned.""" # pylint:disable=too-many-arguments dev = qml.device("default.qubit", wires=4) - state = init_state(4) + state = init_state(4, seed) @qml.qnode(dev) def circuit(): @@ -677,12 +680,12 @@ def circuit_rotated(): @pytest.mark.parametrize("operation", [qml.PauliX, qml.PauliY, qml.Hadamard]) @pytest.mark.parametrize("wire", [0, 1, 2, 3]) - def test_operation_prob(self, operation, wire, init_state, tol): + def test_operation_prob(self, operation, wire, init_state, tol, seed): "Test the rotated probability with different wires and rotating operations." # pylint:disable=too-many-arguments dev = qml.device("default.qubit", wires=4) - state = init_state(4) + state = init_state(4, seed) @qml.qnode(dev) def circuit(): @@ -718,11 +721,11 @@ def circuit_rotated(): assert np.allclose(res, expected, atol=tol, rtol=0) @pytest.mark.parametrize("observable", [(qml.PauliX, qml.PauliY)]) - def test_observable_tensor_prob(self, observable, init_state, tol): + def test_observable_tensor_prob(self, observable, init_state, tol, seed): "Test the rotated probability with a tensor observable." dev = qml.device("default.qubit", wires=4) - state = init_state(4) + state = init_state(4, seed) @qml.qnode(dev) def circuit(): @@ -752,12 +755,12 @@ def circuit_rotated(): assert np.allclose(res, expected, atol=tol, rtol=0) @pytest.mark.parametrize("coeffs, obs", [([1, 1], [qml.PauliX(wires=0), qml.PauliX(wires=1)])]) - def test_hamiltonian_error(self, coeffs, obs, init_state): + def test_hamiltonian_error(self, coeffs, obs, init_state, seed): "Test that an error is returned for hamiltonians." H = qml.Hamiltonian(coeffs, obs) dev = qml.device("default.qubit", wires=4) - state = init_state(4) + state = init_state(4, seed) @qml.qnode(dev) def circuit(): diff --git a/tests/measurements/test_var.py b/tests/measurements/test_var.py index 96da58662ce..3eec826f0dd 100644 --- a/tests/measurements/test_var.py +++ b/tests/measurements/test_var.py @@ -15,7 +15,6 @@ import numpy as np import pytest -from flaky import flaky import pennylane as qml from pennylane.measurements import Variance, VarianceMP @@ -82,15 +81,14 @@ def circuit(phi): res = func(phi) assert np.allclose(np.array(res), expected, atol=atol, rtol=0) - @flaky(max_runs=5) @pytest.mark.parametrize("shots", [None, 5555, [5555, 5555]]) @pytest.mark.parametrize("phi", np.arange(0, 2 * np.pi, np.pi / 3)) def test_observable_is_composite_measurement_value( - self, shots, phi, tol, tol_stochastic + self, shots, phi, tol, tol_stochastic, seed ): # pylint: disable=too-many-arguments """Test that expectation values for mid-circuit measurement values are correct for a composite measurement value.""" - dev = qml.device("default.qubit") + dev = qml.device("default.qubit", seed=seed) @qml.qnode(dev) def circuit(phi): @@ -122,11 +120,11 @@ def expected_circuit(phi): res = func(phi, shots=shots) assert np.allclose(np.array(res), expected, atol=atol, rtol=0) - def test_eigvals_instead_of_observable(self): + def test_eigvals_instead_of_observable(self, seed): """Tests process samples with eigvals instead of observables""" shots = 100 - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) samples = rng.choice([0, 1], size=(shots, 2)).astype(np.int64) expected = qml.var(qml.PauliZ(0)).process_samples(samples, [0, 1]) assert VarianceMP(eigvals=[1, -1], wires=[0]).process_samples(samples, [0, 1]) == expected diff --git a/tests/ops/op_math/test_adjoint.py b/tests/ops/op_math/test_adjoint.py index da4a446098c..e4268870b31 100644 --- a/tests/ops/op_math/test_adjoint.py +++ b/tests/ops/op_math/test_adjoint.py @@ -161,9 +161,9 @@ def test_parametric_ops(self): assert op.wires == qml.wires.Wires("b") - def test_template_base(self): + def test_template_base(self, seed): """Test adjoint initialization for a template.""" - rng = np.random.default_rng(seed=42) + rng = np.random.default_rng(seed=seed) shape = qml.StronglyEntanglingLayers.shape(n_layers=2, n_wires=2) params = rng.random(shape) @@ -642,9 +642,9 @@ def test_matrix_tf(self): self.check_matrix(tf.Variable(1.2345), "tensorflow") - def test_no_matrix_defined(self): + def test_no_matrix_defined(self, seed): """Test that if the base has no matrix defined, then Adjoint.matrix also raises a MatrixUndefinedError.""" - rng = np.random.default_rng(seed=42) + rng = np.random.default_rng(seed=seed) shape = qml.StronglyEntanglingLayers.shape(n_layers=2, n_wires=2) params = rng.random(shape) diff --git a/tests/ops/op_math/test_controlled_decompositions.py b/tests/ops/op_math/test_controlled_decompositions.py index 03290fca0d5..25f1e37f572 100644 --- a/tests/ops/op_math/test_controlled_decompositions.py +++ b/tests/ops/op_math/test_controlled_decompositions.py @@ -158,11 +158,11 @@ def decomp_circuit(): decomp_circuit() @pytest.mark.parametrize("control_wires", ([1], [1, 2], [1, 2, 3])) - def test_decomposition_circuit_gradient(self, control_wires, tol): + def test_decomposition_circuit_gradient(self, control_wires, tol, seed): """Tests that the controlled decomposition of a single-qubit operation behaves as expected in a quantum circuit""" n_qubits = 4 - rng = np.random.default_rng(1337) + rng = np.random.default_rng(seed) dev = qml.device("default.qubit", wires=n_qubits) init_state = rng.random(2**n_qubits) + 1.0j * rng.random(2**n_qubits) diff --git a/tests/ops/op_math/test_pow_op.py b/tests/ops/op_math/test_pow_op.py index b323744c56f..cefb6bfec10 100644 --- a/tests/ops/op_math/test_pow_op.py +++ b/tests/ops/op_math/test_pow_op.py @@ -236,9 +236,9 @@ def test_parametric_ops(self, power_method): assert op.wires == qml.wires.Wires("b") assert op.num_wires == 1 - def test_template_base(self, power_method): + def test_template_base(self, power_method, seed): """Test pow initialization for a template.""" - rng = np.random.default_rng(seed=42) + rng = np.random.default_rng(seed=seed) shape = qml.StronglyEntanglingLayers.shape(n_layers=2, n_wires=2) params = rng.random(shape) # pylint:disable=no-member diff --git a/tests/ops/qubit/test_matrix_ops.py b/tests/ops/qubit/test_matrix_ops.py index 54e56e1d3c5..c432eacf2b7 100644 --- a/tests/ops/qubit/test_matrix_ops.py +++ b/tests/ops/qubit/test_matrix_ops.py @@ -385,9 +385,9 @@ def test_compare_analytic_results(self, inp, exp): @pytest.mark.parametrize("n", [1, 2, 3]) @pytest.mark.parametrize("provide_n", [True, False]) - def test_compare_matrix_mult(self, n, provide_n): + def test_compare_matrix_mult(self, n, provide_n, seed): """Test against matrix multiplication for a few random inputs.""" - rng = np.random.default_rng(382) + rng = np.random.default_rng(seed) inp = rng.random(2**n) output = _walsh_hadamard_transform(inp, n=n if provide_n else None) h = np.array([[0.5, 0.5], [0.5, -0.5]]) @@ -404,9 +404,9 @@ def test_compare_analytic_results_broadcasted(self): @pytest.mark.parametrize("n", [1, 2, 3]) @pytest.mark.parametrize("provide_n", [True, False]) - def test_compare_matrix_mult_broadcasted(self, n, provide_n): + def test_compare_matrix_mult_broadcasted(self, n, provide_n, seed): """Test against matrix multiplication for a few random inputs.""" - rng = np.random.default_rng(382) + rng = np.random.default_rng(seed) inp = rng.random((5, 2**n)) output = _walsh_hadamard_transform(inp, n=n if provide_n else None) h = np.array([[0.5, 0.5], [0.5, -0.5]]) @@ -529,9 +529,9 @@ def test_decomposition_three_qubits_broadcasted(self): qml.assert_equal(decomp[7], qml.MultiRZ(angles[6], [0, 1, 2])) @pytest.mark.parametrize("n", [1, 2, 3]) - def test_decomposition_matrix_match(self, n): + def test_decomposition_matrix_match(self, n, seed): """Test that the matrix of the decomposition matches the original matrix.""" - rng = np.random.default_rng(382) + rng = np.random.default_rng(seed) D = np.exp(1j * rng.random(2**n)) wires = list(range(n)) decomp = qml.DiagonalQubitUnitary.compute_decomposition(D, wires) @@ -544,9 +544,9 @@ def test_decomposition_matrix_match(self, n): assert qml.math.allclose(orig_mat, decomp_mat2) @pytest.mark.parametrize("n", [1, 2, 3]) - def test_decomposition_matrix_match_broadcasted(self, n): + def test_decomposition_matrix_match_broadcasted(self, n, seed): """Test that the broadcasted matrix of the decomposition matches the original matrix.""" - rng = np.random.default_rng(382) + rng = np.random.default_rng(seed) D = np.exp(1j * rng.random((5, 2**n))) wires = list(range(n)) decomp = qml.DiagonalQubitUnitary.compute_decomposition(D, wires) diff --git a/tests/ops/qubit/test_special_unitary.py b/tests/ops/qubit/test_special_unitary.py index 7b84f99fd2f..85201219306 100644 --- a/tests/ops/qubit/test_special_unitary.py +++ b/tests/ops/qubit/test_special_unitary.py @@ -113,14 +113,14 @@ def get_one_parameter_generators(theta, num_wires, interface): @pytest.mark.jax @pytest.mark.parametrize("n", [1, 2, 3]) @pytest.mark.parametrize("use_jit", [True, False]) - def test_jax(self, n, use_jit): + def test_jax(self, n, use_jit, seed): """Test that generators are computed correctly in JAX.""" import jax jax.config.update("jax_enable_x64", True) from jax import numpy as jnp - rng = np.random.default_rng(14521) + rng = np.random.default_rng(seed) d = 4**n - 1 theta = jnp.array(rng.random(d)) fn = ( @@ -406,7 +406,6 @@ def interface_array(x, interface): @pytest.mark.parametrize("interface", interfaces) @pytest.mark.parametrize("n", [1, 2, 3]) - @pytest.mark.parametrize("seed", [214, 2491, 8623]) def test_compute_matrix_random(self, n, seed, interface): """Test that ``compute_matrix`` returns a correctly-shaped unitary matrix for random input parameters.""" @@ -426,7 +425,6 @@ def test_compute_matrix_random(self, n, seed, interface): assert np.allclose(matrix @ qml.math.conj(qml.math.T(matrix)), I) @pytest.mark.parametrize("interface", interfaces) - @pytest.mark.parametrize("seed", [214, 8623]) def test_compute_matrix_random_many_wires(self, seed, interface): """Test that ``compute_matrix`` returns a correctly-shaped unitary matrix for random input parameters and more than 5 wires.""" @@ -448,7 +446,6 @@ def test_compute_matrix_random_many_wires(self, seed, interface): @pytest.mark.parametrize("interface", interfaces) @pytest.mark.parametrize("n", [1, 2]) - @pytest.mark.parametrize("seed", [214, 2491, 8623]) def test_compute_matrix_random_broadcasted(self, n, seed, interface): """Test that ``compute_matrix`` returns a correctly-shaped unitary matrix for broadcasted random input parameters.""" diff --git a/tests/optimize/test_optimize_shot_adaptive.py b/tests/optimize/test_optimize_shot_adaptive.py index b8643143cbb..e1c4c3256ed 100644 --- a/tests/optimize/test_optimize_shot_adaptive.py +++ b/tests/optimize/test_optimize_shot_adaptive.py @@ -627,9 +627,8 @@ def circuit(x): assert np.allclose(circuit(params), -1, atol=0.1, rtol=0.2) assert opt.shots_used > min_shots - @flaky(max_runs=3) @pytest.mark.slow - def test_vqe_optimization(self): + def test_vqe_optimization(self, seed): """Test that a simple VQE circuit can be optimized""" dev = qml.device("default.qubit", wires=2, shots=100) coeffs = [0.1, 0.2] @@ -649,7 +648,7 @@ def cost(params): ansatz(params) return qml.expval(H) - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) params = rng.random((4, 3), requires_grad=True) initial_loss = cost(params) diff --git a/tests/optimize/test_qnspsa.py b/tests/optimize/test_qnspsa.py index 66fa613cdd6..c9292c57876 100644 --- a/tests/optimize/test_qnspsa.py +++ b/tests/optimize/test_qnspsa.py @@ -129,7 +129,6 @@ def get_state_overlap(params1, params2): return metric_tensor_expected -@pytest.mark.parametrize("seed", [1, 151, 1231]) @pytest.mark.parametrize("finite_diff_step", [1e-3, 1e-2, 1e-1]) class TestQNSPSAOptimizer: def test_gradient_from_single_input(self, finite_diff_step, seed): @@ -424,7 +423,7 @@ def test_blocking(self, finite_diff_step, seed): assert np.allclose(new_params, params) -def test_template_no_adjoint(): +def test_template_no_adjoint(seed): """Test that qnspsa iterates when the operations do not have a custom adjoint.""" num_qubits = 2 @@ -432,7 +431,7 @@ def test_template_no_adjoint(): @qml.qnode(dev) def cost(params): - qml.RandomLayers(weights=params, wires=range(num_qubits), seed=42) + qml.RandomLayers(weights=params, wires=range(num_qubits), seed=seed) return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1)) params = np.random.normal(0, np.pi, (2, 4)) diff --git a/tests/pytest.ini b/tests/pytest.ini index 2cdb4b8ef8f..036b5196116 100644 --- a/tests/pytest.ini +++ b/tests/pytest.ini @@ -19,6 +19,7 @@ markers = logging: marks tests for pennylane logging external: marks tests that require external packages such as matplotlib and PyZX catalyst: marks tests for catalyst testing (select with '-m "catalyst"') + local_salt(salt): adds a salt to the seed provided by the pytest-rng fixture filterwarnings = ignore::DeprecationWarning:autograd.numpy.numpy_wrapper ignore:Casting complex values to real::autograd.numpy.numpy_wrapper @@ -33,3 +34,4 @@ filterwarnings = ignore:PauliWord.hamiltonian:pennylane.PennyLaneDeprecationWarning addopts = --benchmark-disable xfail_strict=true +rng_salt = v0.39.0 diff --git a/tests/resource/test_specs.py b/tests/resource/test_specs.py index 00af26777f5..aebe0af0d19 100644 --- a/tests/resource/test_specs.py +++ b/tests/resource/test_specs.py @@ -248,7 +248,7 @@ def circuit(x): assert specs_list[1]["num_device_wires"] == specs_list[1]["num_tape_wires"] == 3 assert specs_list[2]["num_device_wires"] == specs_list[1]["num_tape_wires"] == 3 - def make_qnode_and_params(self): + def make_qnode_and_params(self, seed): """Generates a qnode and params for use in other tests""" n_layers = 2 n_wires = 5 @@ -261,7 +261,7 @@ def circuit(params): return qml.expval(qml.PauliZ(0)) params_shape = qml.BasicEntanglerLayers.shape(n_layers=n_layers, n_wires=n_wires) - rng = pnp.random.default_rng(seed=10) + rng = pnp.random.default_rng(seed=seed) params = rng.standard_normal(params_shape) # pylint:disable=no-member return circuit, params diff --git a/tests/shadow/test_shadow_transforms.py b/tests/shadow/test_shadow_transforms.py index 1910293264b..90300a13030 100644 --- a/tests/shadow/test_shadow_transforms.py +++ b/tests/shadow/test_shadow_transforms.py @@ -360,7 +360,7 @@ def test_hadamard_forward(self): assert qml.math.allclose(actual, expected, atol=1e-1) - def test_basic_entangler_backward(self): + def test_basic_entangler_backward(self, seed): """Test the gradient of the expval transform""" obs = [ @@ -377,7 +377,7 @@ def test_basic_entangler_backward(self): shadow_circuit = qml.shadows.shadow_expval(shadow_circuit, obs) exact_circuit = basic_entangler_circuit_exact_expval(3, "autograd") - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) x = rng.uniform(0.8, 2, size=qml.BasicEntanglerLayers.shape(n_layers=1, n_wires=3)) def shadow_cost(x): diff --git a/tests/templates/test_embeddings/test_amplitude.py b/tests/templates/test_embeddings/test_amplitude.py index 7096b3206e6..6909cd22387 100644 --- a/tests/templates/test_embeddings/test_amplitude.py +++ b/tests/templates/test_embeddings/test_amplitude.py @@ -592,12 +592,12 @@ def test_torch(self, tol, features, pad_with, dtype): @pytest.mark.jax @pytest.mark.parametrize("shots, atol", [(10000, 0.05), (None, 1e-8)]) -def test_jacobian_with_and_without_jit_has_same_output(shots, atol): +def test_jacobian_with_and_without_jit_has_same_output(shots, atol, seed): """Test that the jacobian of AmplitudeEmbedding is the same with and without jit.""" import jax - dev = qml.device("default.qubit", shots=shots, seed=7890234) + dev = qml.device("default.qubit", shots=shots, seed=seed) @qml.qnode(dev, diff_method="parameter-shift") def circuit(coeffs): diff --git a/tests/templates/test_state_preparations/test_mottonen_state_prep.py b/tests/templates/test_state_preparations/test_mottonen_state_prep.py index d1c0e194c4f..1a9f0aa6219 100644 --- a/tests/templates/test_state_preparations/test_mottonen_state_prep.py +++ b/tests/templates/test_state_preparations/test_mottonen_state_prep.py @@ -418,11 +418,11 @@ def circuit(state): @pytest.mark.jax @pytest.mark.parametrize("shots, atol", [(None, 0.005), (1000000, 0.05)]) -def test_jacobians_with_and_without_jit_match(shots, atol): +def test_jacobians_with_and_without_jit_match(shots, atol, seed): """Test that the Jacobian of the circuit is the same with and without jit.""" import jax - dev = qml.device("default.qubit", shots=shots, seed=7890234) + dev = qml.device("default.qubit", shots=shots, seed=seed) dev_no_shots = qml.device("default.qubit", shots=None) def circuit(coeffs): diff --git a/tests/templates/test_subroutines/test_amplitude_amplification.py b/tests/templates/test_subroutines/test_amplitude_amplification.py index 484e924f359..1c7ca4c2c97 100644 --- a/tests/templates/test_subroutines/test_amplitude_amplification.py +++ b/tests/templates/test_subroutines/test_amplitude_amplification.py @@ -172,14 +172,14 @@ def test_qnode_autograd(self, shots): @pytest.mark.jax @pytest.mark.parametrize("use_jit", [False, True]) @pytest.mark.parametrize("shots", [None, 50000]) - def test_qnode_jax(self, shots, use_jit): + def test_qnode_jax(self, shots, use_jit, seed): """Test that the QNode executes and is differentiable with JAX. The shots argument controls whether autodiff or parameter-shift gradients are used.""" import jax jax.config.update("jax_enable_x64", True) - dev = qml.device("default.qubit", shots=shots, seed=10) + dev = qml.device("default.qubit", shots=shots, seed=seed) diff_method = "backprop" if shots is None else "parameter-shift" qnode = qml.QNode(self.circuit, dev, interface="jax", diff_method=diff_method) @@ -198,12 +198,12 @@ def test_qnode_jax(self, shots, use_jit): @pytest.mark.torch @pytest.mark.parametrize("shots", [None, 50000]) - def test_qnode_torch(self, shots): + def test_qnode_torch(self, shots, seed): """Test that the QNode executes and is differentiable with Torch. The shots argument controls whether autodiff or parameter-shift gradients are used.""" import torch - dev = qml.device("default.qubit", shots=shots, seed=10) + dev = qml.device("default.qubit", shots=shots, seed=seed) diff_method = "backprop" if shots is None else "parameter-shift" qnode = qml.QNode(self.circuit, dev, interface="torch", diff_method=diff_method) @@ -216,12 +216,12 @@ def test_qnode_torch(self, shots): @pytest.mark.tf @pytest.mark.parametrize("shots", [None, 50000]) @pytest.mark.xfail(reason="tf gradient doesn't seem to be working, returns ()") - def test_qnode_tf(self, shots): + def test_qnode_tf(self, shots, seed): """Test that the QNode executes and is differentiable with TensorFlow. The shots argument controls whether autodiff or parameter-shift gradients are used.""" import tensorflow as tf - dev = qml.device("default.qubit", shots=shots, seed=10) + dev = qml.device("default.qubit", shots=shots, seed=seed) diff_method = "backprop" if shots is None else "parameter-shift" qnode = qml.QNode(self.circuit, dev, interface="tf", diff_method=diff_method) diff --git a/tests/templates/test_subroutines/test_controlled_sequence.py b/tests/templates/test_subroutines/test_controlled_sequence.py index d0401a0288c..494c521cdb0 100644 --- a/tests/templates/test_subroutines/test_controlled_sequence.py +++ b/tests/templates/test_subroutines/test_controlled_sequence.py @@ -212,7 +212,7 @@ def test_qnode_autograd(self, shots): @pytest.mark.jax @pytest.mark.parametrize("use_jit", [False, True]) @pytest.mark.parametrize("shots", [None, 50000]) - def test_qnode_jax(self, shots, use_jit): + def test_qnode_jax(self, shots, use_jit, seed): """Test that the QNode executes and is differentiable with JAX. The shots argument controls whether autodiff or parameter-shift gradients are used.""" @@ -220,7 +220,7 @@ def test_qnode_jax(self, shots, use_jit): jax.config.update("jax_enable_x64", True) - dev = qml.device("default.qubit", shots=shots, seed=10) + dev = qml.device("default.qubit", shots=shots, seed=seed) diff_method = "backprop" if shots is None else "parameter-shift" qnode = qml.QNode(self.circuit, dev, interface="jax", diff_method=diff_method) @@ -242,13 +242,13 @@ def test_qnode_jax(self, shots, use_jit): @pytest.mark.torch @pytest.mark.parametrize("shots", [None, 50000]) - def test_qnode_torch(self, shots): + def test_qnode_torch(self, shots, seed): """Test that the QNode executes and is differentiable with Torch. The shots argument controls whether autodiff or parameter-shift gradients are used.""" import torch - dev = qml.device("default.qubit", shots=shots, seed=10) + dev = qml.device("default.qubit", shots=shots, seed=seed) diff_method = "backprop" if shots is None else "parameter-shift" qnode = qml.QNode(self.circuit, dev, interface="torch", diff_method=diff_method) @@ -265,13 +265,13 @@ def test_qnode_torch(self, shots): @pytest.mark.tf @pytest.mark.parametrize("shots", [None, 10000]) @pytest.mark.xfail(reason="tf gradient doesn't seem to be working, returns ()") - def test_qnode_tf(self, shots): + def test_qnode_tf(self, shots, seed): """Test that the QNode executes and is differentiable with TensorFlow. The shots argument controls whether autodiff or parameter-shift gradients are used.""" import tensorflow as tf - dev = qml.device("default.qubit", shots=shots, seed=10) + dev = qml.device("default.qubit", shots=shots, seed=seed) diff_method = "backprop" if shots is None else "parameter-shift" qnode = qml.QNode(self.circuit, dev, interface="tf", diff_method=diff_method) diff --git a/tests/templates/test_subroutines/test_qdrift.py b/tests/templates/test_subroutines/test_qdrift.py index 2f0f25b9eb8..33c168dc7a1 100644 --- a/tests/templates/test_subroutines/test_qdrift.py +++ b/tests/templates/test_subroutines/test_qdrift.py @@ -55,7 +55,6 @@ def test_queuing(self): @pytest.mark.parametrize("n", (1, 2, 3)) @pytest.mark.parametrize("time", (0.5, 1, 2)) - @pytest.mark.parametrize("seed", (None, 1234, 42)) @pytest.mark.parametrize("coeffs, ops", test_hamiltonians) def test_init_correctly(self, coeffs, ops, time, n, seed): # pylint: disable=too-many-arguments """Test that all of the attributes are initialized correctly.""" @@ -77,7 +76,6 @@ def test_init_correctly(self, coeffs, ops, time, n, seed): # pylint: disable=to @pytest.mark.parametrize("n", (1, 2, 3)) @pytest.mark.parametrize("time", (0.5, 1, 2)) - @pytest.mark.parametrize("seed", (None, 1234, 42)) @pytest.mark.parametrize("coeffs, ops", test_hamiltonians) def test_copy(self, coeffs, ops, time, n, seed): # pylint: disable=too-many-arguments """Test that we can make copies of QDrift correctly.""" @@ -126,7 +124,6 @@ class TestDecomposition: @pytest.mark.parametrize("n", (1, 2, 3)) @pytest.mark.parametrize("time", (0.5, 1, 2)) - @pytest.mark.parametrize("seed", (None, 1234, 42)) @pytest.mark.parametrize("coeffs, ops", test_hamiltonians) def test_private_sample(self, coeffs, ops, time, seed, n): # pylint: disable=too-many-arguments """Test the private function which samples the decomposition""" @@ -146,15 +143,14 @@ def test_private_sample(self, coeffs, ops, time, seed, n): # pylint: disable=to assert term.coeff == (s * normalization * time * 1j / n) # with this exponent @pytest.mark.parametrize("coeffs", ([0.99, 0.01], [0.5 + 0.49j, -0.01j])) - def test_private_sample_statistics(self, coeffs): + def test_private_sample_statistics(self, coeffs, seed): """Test the private function samples from the right distribution""" ops = [qml.PauliX(0), qml.PauliZ(1)] - decomp = _sample_decomposition(coeffs, ops, 1.23, n=10, seed=1234) + decomp = _sample_decomposition(coeffs, ops, 1.23, n=10, seed=seed) # High probability we only sample PauliX! assert all(isinstance(op.base, qml.PauliX) for op in decomp) - @pytest.mark.parametrize("seed", (1234, 42)) def test_compute_decomposition(self, seed): """Test that the decomposition is computed and queues correctly.""" coeffs = [1, -0.5, 0.5] @@ -182,9 +178,9 @@ def test_compute_decomposition(self, seed): class TestIntegration: """Test that the QDrift template integrates well with the rest of PennyLane""" + @pytest.mark.local_salt(8) @pytest.mark.parametrize("n", (1, 2, 3)) @pytest.mark.parametrize("time", (0.5, 1, 2)) - @pytest.mark.parametrize("seed", (1234, 42)) @pytest.mark.parametrize("coeffs, ops", test_hamiltonians) def test_execution(self, coeffs, ops, time, n, seed): # pylint: disable=too-many-arguments """Test that the circuit executes as expected""" @@ -213,8 +209,8 @@ def circ(): assert allclose(expected_state, state) + @pytest.mark.local_salt(8) @pytest.mark.autograd - @pytest.mark.parametrize("seed", (1234, 42)) @pytest.mark.parametrize("coeffs, ops", test_hamiltonians) def test_execution_autograd(self, coeffs, ops, seed): """Test that the circuit executes as expected using autograd""" @@ -246,7 +242,6 @@ def circ(time): assert allclose(expected_state, state) @pytest.mark.torch - @pytest.mark.parametrize("seed", (1234, 42)) @pytest.mark.parametrize("coeffs, ops", test_hamiltonians) def test_execution_torch(self, coeffs, ops, seed): """Test that the circuit executes as expected using torch""" @@ -277,7 +272,6 @@ def circ(time): assert allclose(expected_state, state) @pytest.mark.tf - @pytest.mark.parametrize("seed", (1234, 42)) @pytest.mark.parametrize("coeffs, ops", test_hamiltonians) def test_execution_tf(self, coeffs, ops, seed): """Test that the circuit executes as expected using tensorflow""" @@ -308,7 +302,6 @@ def circ(time): assert allclose(expected_state, state) @pytest.mark.jax - @pytest.mark.parametrize("seed", (1234, 42)) @pytest.mark.parametrize("coeffs, ops", test_hamiltonians) def test_execution_jax(self, coeffs, ops, seed): """Test that the circuit executes as expected using jax""" @@ -339,7 +332,6 @@ def circ(time): assert allclose(expected_state, state) @pytest.mark.jax - @pytest.mark.parametrize("seed", (1234, 42)) @pytest.mark.parametrize("coeffs, ops", test_hamiltonians) def test_execution_jaxjit(self, coeffs, ops, seed): """Test that the circuit executes as expected using jax jit""" @@ -462,7 +454,6 @@ def circ(time, coeffs): @pytest.mark.autograd @pytest.mark.parametrize("n", (1, 5, 10)) - @pytest.mark.parametrize("seed", (1234, 42)) def test_autograd_gradient(self, n, seed): """Test that the gradient is computed correctly""" time = qnp.array(1.5) @@ -493,7 +484,6 @@ def reference_circ(time, coeffs): @pytest.mark.torch @pytest.mark.parametrize("n", (1, 5, 10)) - @pytest.mark.parametrize("seed", (1234, 42)) def test_torch_gradient(self, n, seed): """Test that the gradient is computed correctly using torch""" import torch @@ -534,7 +524,6 @@ def reference_circ(time, coeffs): @pytest.mark.tf @pytest.mark.parametrize("n", (1, 5, 10)) - @pytest.mark.parametrize("seed", (1234, 42)) def test_tf_gradient(self, n, seed): """Test that the gradient is computed correctly using tensorflow""" import tensorflow as tf @@ -573,7 +562,6 @@ def reference_circ(time, coeffs): @pytest.mark.jax @pytest.mark.parametrize("n", (1, 5, 10)) - @pytest.mark.parametrize("seed", (1234, 42)) def test_jax_gradient(self, n, seed): """Test that the gradient is computed correctly using jax""" import jax diff --git a/tests/templates/test_subroutines/test_qubitization.py b/tests/templates/test_subroutines/test_qubitization.py index 66e40066eca..7b8e439d77c 100644 --- a/tests/templates/test_subroutines/test_qubitization.py +++ b/tests/templates/test_subroutines/test_qubitization.py @@ -176,7 +176,7 @@ def test_qnode_autograd(self): @pytest.mark.jax @pytest.mark.parametrize("use_jit", (False, True)) @pytest.mark.parametrize("shots", (None, 50000)) - def test_qnode_jax(self, shots, use_jit): + def test_qnode_jax(self, shots, use_jit, seed): """ "Test that the QNode executes and is differentiable with JAX. The shots argument controls whether autodiff or parameter-shift gradients are used.""" import jax @@ -191,7 +191,7 @@ def test_qnode_jax(self, shots, use_jit): jax.config.update("jax_enable_x64", True) - dev = qml.device("default.qubit", shots=shots, seed=10) + dev = qml.device("default.qubit", shots=shots, seed=seed) diff_method = "backprop" if shots is None else "parameter-shift" qnode = qml.QNode(self.circuit, dev, interface="jax", diff_method=diff_method) @@ -210,7 +210,7 @@ def test_qnode_jax(self, shots, use_jit): @pytest.mark.torch @pytest.mark.parametrize("shots", [None, 50000]) - def test_qnode_torch(self, shots): + def test_qnode_torch(self, shots, seed): """ "Test that the QNode executes and is differentiable with Torch. The shots argument controls whether autodiff or parameter-shift gradients are used.""" import torch @@ -219,7 +219,7 @@ def test_qnode_torch(self, shots): if shots is not None: pytest.xfail() - dev = qml.device("default.qubit", shots=shots, seed=10) + dev = qml.device("default.qubit", shots=shots, seed=seed) diff_method = "backprop" if shots is None else "parameter-shift" qnode = qml.QNode(self.circuit, dev, interface="torch", diff_method=diff_method) @@ -231,12 +231,12 @@ def test_qnode_torch(self, shots): @pytest.mark.tf @pytest.mark.parametrize("shots", [None, 50000]) @pytest.mark.xfail(reason="tf gradient doesn't seem to be working, returns ()") - def test_qnode_tf(self, shots): + def test_qnode_tf(self, shots, seed): """ "Test that the QNode executes and is differentiable with TensorFlow. The shots argument controls whether autodiff or parameter-shift gradients are used.""" import tensorflow as tf - dev = qml.device("default.qubit", shots=shots, seed=10) + dev = qml.device("default.qubit", shots=shots, seed=seed) diff_method = "backprop" if shots is None else "parameter-shift" qnode = qml.QNode(self.circuit, dev, interface="tf", diff_method=diff_method) diff --git a/tests/templates/test_subroutines/test_reflection.py b/tests/templates/test_subroutines/test_reflection.py index d47a822efab..6123daed312 100644 --- a/tests/templates/test_subroutines/test_reflection.py +++ b/tests/templates/test_subroutines/test_reflection.py @@ -183,14 +183,14 @@ def test_qnode_autograd(self, shots): @pytest.mark.jax @pytest.mark.parametrize("use_jit", [False, True]) @pytest.mark.parametrize("shots", [None, 50000]) - def test_qnode_jax(self, shots, use_jit): + def test_qnode_jax(self, shots, use_jit, seed): """Test that the QNode executes and is differentiable with JAX. The shots argument controls whether autodiff or parameter-shift gradients are used.""" import jax jax.config.update("jax_enable_x64", True) - dev = qml.device("default.qubit", shots=shots, seed=10) + dev = qml.device("default.qubit", shots=shots, seed=seed) diff_method = "backprop" if shots is None else "parameter-shift" qnode = qml.QNode(self.circuit, dev, interface="jax", diff_method=diff_method) @@ -212,13 +212,13 @@ def test_qnode_jax(self, shots, use_jit): @pytest.mark.torch @pytest.mark.parametrize("shots", [None, 50000]) - def test_qnode_torch(self, shots): + def test_qnode_torch(self, shots, seed): """Test that the QNode executes and is differentiable with Torch. The shots argument controls whether autodiff or parameter-shift gradients are used.""" import torch - dev = qml.device("default.qubit", shots=shots, seed=10) + dev = qml.device("default.qubit", shots=shots, seed=seed) diff_method = "backprop" if shots is None else "parameter-shift" qnode = qml.QNode(self.circuit, dev, interface="torch", diff_method=diff_method) @@ -235,12 +235,12 @@ def test_qnode_torch(self, shots): @pytest.mark.tf @pytest.mark.parametrize("shots", [None, 50000]) @pytest.mark.xfail(reason="tf gradient doesn't seem to be working, returns ()") - def test_qnode_tf(self, shots): + def test_qnode_tf(self, shots, seed): """Test that the QNode executes and is differentiable with TensorFlow. The shots argument controls whether autodiff or parameter-shift gradients are used.""" import tensorflow as tf - dev = qml.device("default.qubit", shots=shots, seed=10) + dev = qml.device("default.qubit", shots=shots, seed=seed) diff_method = "backprop" if shots is None else "parameter-shift" qnode = qml.QNode(self.circuit, dev, interface="tf", diff_method=diff_method) diff --git a/tests/test_compiler.py b/tests/test_compiler.py index 6ceb2378614..8d8b18e499c 100644 --- a/tests/test_compiler.py +++ b/tests/test_compiler.py @@ -864,7 +864,7 @@ class TestCatalystMCMs: @pytest.mark.parametrize("measure_f", [qml.counts, qml.expval, qml.probs]) @pytest.mark.parametrize("meas_obj", [qml.PauliZ(0), [0], "mcm"]) # pylint: disable=too-many-arguments - def test_dynamic_one_shot_simple(self, measure_f, meas_obj): + def test_dynamic_one_shot_simple(self, measure_f, meas_obj, seed): """Tests that Catalyst yields the same results as PennyLane's DefaultQubit for a simple circuit with a mid-circuit measurement.""" if measure_f in (qml.counts, qml.probs, qml.sample) and ( @@ -879,7 +879,7 @@ def test_dynamic_one_shot_simple(self, measure_f, meas_obj): pytest.xfail("isa") shots = 8000 - dq = qml.device("default.qubit", shots=shots, seed=8237945) + dq = qml.device("default.qubit", shots=shots, seed=seed) @qml.defer_measurements @qml.qnode(dq) diff --git a/tests/test_debugging.py b/tests/test_debugging.py index e575017f5a1..99585f44e2c 100644 --- a/tests/test_debugging.py +++ b/tests/test_debugging.py @@ -364,6 +364,10 @@ def circuit(): @pytest.mark.parametrize("diff_method", [None, "parameter-shift"]) def test_default_qutrit_mixed_finite_shot(self, diff_method): """Test that multiple snapshots are returned correctly on the qutrit density-matrix simulator.""" + + # TODO: not sure what to do with this test so leaving this here for now. + np.random.seed(9872653) + dev = qml.device("default.qutrit.mixed", wires=2, shots=100) assert qml.debugging.snapshot._is_snapshot_compatible(dev) @@ -482,6 +486,10 @@ def qnode(params): def test_adjoint_circuit(self): """Test that snapshots are returned correctly when adjointed.""" + + # TODO: not sure what to do with this test so leaving this here for now. + np.random.seed(9872653) + dev = qml.device("default.qubit", wires=2) def circuit(params, wire): @@ -508,6 +516,10 @@ def qnode(params): def test_all_sample_measurement_snapshot(self): """Test that the correct measurement snapshots are returned for different measurement types.""" + + # TODO: The fact that this entire test depends on a global seed is not good + np.random.seed(9872653) + dev = qml.device("default.qubit", wires=1, shots=10) @qml.qnode(dev) diff --git a/tests/test_hermitian_edge_cases.py b/tests/test_hermitian_edge_cases.py index 41224a00b7c..4297b52a8ec 100644 --- a/tests/test_hermitian_edge_cases.py +++ b/tests/test_hermitian_edge_cases.py @@ -21,6 +21,8 @@ import pennylane as qml +# pylint:disable=too-many-arguments + THETA = np.linspace(0.11, 1, 3) PHI = np.linspace(0.32, 1, 3) @@ -87,9 +89,9 @@ def circuit(): @pytest.mark.parametrize("theta", THETA) @pytest.mark.parametrize("w1, w2", list(itertools.permutations(range(4), 2))) - def test_hermitian_two_wires_permuted(self, w1, w2, shots, theta): + def test_hermitian_two_wires_permuted(self, w1, w2, shots, theta, seed): """Test that an hermitian expectation with various wires permuted works""" - dev = qml.device("default.qubit", wires=4, shots=shots, seed=123545) + dev = qml.device("default.qubit", wires=4, shots=shots, seed=seed) theta = 0.543 A = np.array( diff --git a/tests/test_operation.py b/tests/test_operation.py index cdcdc48fb4d..f1c9c6bd5d4 100644 --- a/tests/test_operation.py +++ b/tests/test_operation.py @@ -511,11 +511,11 @@ class MyOp(qml.operation.Operator): assert MyOp.has_matrix is False assert MyOp(wires=0).has_matrix is False - def test_has_matrix_false_concrete_template(self): + def test_has_matrix_false_concrete_template(self, seed): """Test has_matrix with a concrete operation (StronglyEntanglingLayers) that does not have a matrix defined.""" - rng = qml.numpy.random.default_rng(seed=42) + rng = qml.numpy.random.default_rng(seed=seed) shape = qml.StronglyEntanglingLayers.shape(n_layers=2, n_wires=2) params = rng.random(shape) op = qml.StronglyEntanglingLayers(params, wires=range(2)) diff --git a/tests/test_qnode.py b/tests/test_qnode.py index 841bf4a8315..67b54593d9c 100644 --- a/tests/test_qnode.py +++ b/tests/test_qnode.py @@ -1723,7 +1723,7 @@ def f(x): @pytest.mark.jax @pytest.mark.parametrize("diff_method", [None, "best"]) - def test_defer_measurements_with_jit(self, diff_method, mocker): + def test_defer_measurements_with_jit(self, diff_method, mocker, seed): """Test that using mcm_method="deferred" defaults to behaviour like postselect_mode="fill-shots" when using jax jit.""" import jax # pylint: disable=import-outside-toplevel @@ -1734,7 +1734,7 @@ def test_defer_measurements_with_jit(self, diff_method, mocker): spy = mocker.spy(qml.defer_measurements, "_transform") spy_one_shot = mocker.spy(qml.dynamic_one_shot, "_transform") - dev = qml.device("default.qubit", wires=4, shots=shots, seed=jax.random.PRNGKey(123)) + dev = qml.device("default.qubit", wires=4, shots=shots, seed=jax.random.PRNGKey(seed)) @qml.qnode(dev, diff_method=diff_method, mcm_method="deferred") def f(x): @@ -1755,9 +1755,8 @@ def f(x): assert qml.math.allclose(res_jit, postselect) @pytest.mark.jax - # @pytest.mark.parametrize("diff_method", [None, "best"]) - @pytest.mark.parametrize("diff_method", ["best"]) - def test_deferred_hw_like_error_with_jit(self, diff_method): + @pytest.mark.parametrize("diff_method", [None, "best"]) + def test_deferred_hw_like_error_with_jit(self, diff_method, seed): """Test that an error is raised if attempting to use postselect_mode="hw-like" with jax jit with mcm_method="deferred".""" import jax # pylint: disable=import-outside-toplevel @@ -1766,7 +1765,7 @@ def test_deferred_hw_like_error_with_jit(self, diff_method): postselect = 1 param = jax.numpy.array(np.pi / 2) - dev = qml.device("default.qubit", wires=4, shots=shots, seed=jax.random.PRNGKey(123)) + dev = qml.device("default.qubit", wires=4, shots=shots, seed=jax.random.PRNGKey(seed)) @qml.qnode(dev, diff_method=diff_method, mcm_method="deferred", postselect_mode="hw-like") def f(x): diff --git a/tests/test_tensor_measurements.py b/tests/test_tensor_measurements.py index 543eba1618b..6e726b1a9cb 100644 --- a/tests/test_tensor_measurements.py +++ b/tests/test_tensor_measurements.py @@ -49,10 +49,10 @@ def tolerance(self, shots, tol): return {"atol": tol, "rtol": 0} - def test_tensor_product(self, shots, theta, phi, varphi, tolerance): + def test_tensor_product(self, shots, theta, phi, varphi, tolerance, seed): """Test that a tensor product ZxZ gives the same result as simply using an Hermitian matrix""" - dev = qml.device("default.qubit", wires=3, shots=shots, seed=1851) + dev = qml.device("default.qubit", wires=3, shots=shots, seed=seed) @qml.qnode(dev) def circuit1(a, b, c): @@ -69,10 +69,10 @@ def circuit2(a, b, c): assert np.allclose(res1, res2, **tolerance) - def test_combine_tensor_with_non_tensor(self, shots, theta, phi, varphi, tolerance): + def test_combine_tensor_with_non_tensor(self, shots, theta, phi, varphi, tolerance, seed): """Test that a tensor product along with a non-tensor product continues to function correctly""" - dev = qml.device("default.qubit", wires=3, shots=shots, seed=181) + dev = qml.device("default.qubit", wires=3, shots=shots, seed=seed) @qml.qnode(dev) def circuit1(a, b, c): @@ -94,9 +94,9 @@ def circuit3(a, b, c): assert np.allclose(res1, res2, **tolerance) - def test_paulix_tensor_pauliy(self, shots, theta, phi, varphi, tolerance): + def test_paulix_tensor_pauliy(self, shots, theta, phi, varphi, tolerance, seed): """Test that a tensor product involving PauliX and PauliY works correctly""" - dev = qml.device("default.qubit", wires=3, shots=shots, seed=164) + dev = qml.device("default.qubit", wires=3, shots=shots, seed=seed) @qml.qnode(dev) def circuit(a, b, c): @@ -109,9 +109,9 @@ def circuit(a, b, c): assert np.allclose(res, expected, **tolerance) @pytest.mark.autograd - def test_paulix_tensor_pauliy_gradient(self, shots, theta, phi, varphi, tolerance): + def test_paulix_tensor_pauliy_gradient(self, shots, theta, phi, varphi, tolerance, seed): """Test that a tensor product involving PauliX and PauliY works correctly""" - dev = qml.device("default.qubit", wires=3, shots=shots, seed=144) + dev = qml.device("default.qubit", wires=3, shots=shots, seed=seed) @qml.qnode(dev) def circuit(a, b, c): @@ -124,9 +124,9 @@ def circuit(a, b, c): assert np.allclose(res, expected, **tolerance) - def test_pauliz_tensor_identity(self, shots, theta, phi, varphi, tolerance): + def test_pauliz_tensor_identity(self, shots, theta, phi, varphi, tolerance, seed): """Test that a tensor product involving PauliZ and Identity works correctly""" - dev = qml.device("default.qubit", wires=3, shots=shots, seed=134) + dev = qml.device("default.qubit", wires=3, shots=shots, seed=seed) @qml.qnode(dev) def circuit(a, b, c): @@ -138,9 +138,9 @@ def circuit(a, b, c): assert np.allclose(res, expected, **tolerance) - def test_pauliz_tensor_hadamard(self, shots, theta, phi, varphi, tolerance): + def test_pauliz_tensor_hadamard(self, shots, theta, phi, varphi, tolerance, seed): """Test that a tensor product involving PauliZ and hadamard works correctly""" - dev = qml.device("default.qubit", wires=3, shots=shots, seed=324) + dev = qml.device("default.qubit", wires=3, shots=shots, seed=seed) @qml.qnode(dev) def circuit(a, b, c): @@ -152,9 +152,9 @@ def circuit(a, b, c): assert np.allclose(res, expected, **tolerance) - def test_hermitian(self, shots, theta, phi, varphi, tolerance): + def test_hermitian(self, shots, theta, phi, varphi, tolerance, seed): """Test that a tensor product involving an Hermitian matrix works correctly""" - dev = qml.device("default.qubit", wires=3, shots=shots, seed=125) + dev = qml.device("default.qubit", wires=3, shots=shots, seed=seed) A = np.array( [ @@ -180,9 +180,9 @@ def circuit(a, b, c): assert np.allclose(res, expected, **tolerance) - def test_hermitian_tensor_hermitian(self, shots, theta, phi, varphi, tolerance): + def test_hermitian_tensor_hermitian(self, shots, theta, phi, varphi, tolerance, seed): """Test that a tensor product involving two Hermitian matrices works correctly""" - dev = qml.device("default.qubit", wires=3, shots=shots, seed=224) + dev = qml.device("default.qubit", wires=3, shots=shots, seed=seed) A1 = np.array([[1, 2], [2, 4]]) @@ -220,9 +220,11 @@ def circuit(a, b, c): assert np.allclose(res, expected, **tolerance) - def test_hermitian_tensor_identity_expectation(self, shots, theta, phi, varphi, tolerance): + def test_hermitian_tensor_identity_expectation( + self, shots, theta, phi, varphi, tolerance, seed + ): """Test that a tensor product involving an Hermitian matrix and the identity works correctly""" - dev = qml.device("default.qubit", wires=2, shots=shots, seed=144) + dev = qml.device("default.qubit", wires=2, shots=shots, seed=seed) A = np.array( [[1.02789352, 1.61296440 - 0.3498192j], [1.61296440 + 0.3498192j, 1.23920938 + 0j]] @@ -258,9 +260,9 @@ def tolerance(self, shots, tol): return {"atol": tol, "rtol": 0} - def test_paulix_tensor_pauliy(self, shots, theta, phi, varphi, tolerance): + def test_paulix_tensor_pauliy(self, shots, theta, phi, varphi, tolerance, seed): """Test that a tensor product involving PauliX and PauliY works correctly""" - dev = qml.device("default.qubit", wires=3, shots=shots, seed=924) + dev = qml.device("default.qubit", wires=3, shots=shots, seed=seed) @qml.qnode(dev) def circuit(a, b, c): @@ -279,9 +281,9 @@ def circuit(a, b, c): assert np.allclose(res, expected, **tolerance) - def test_pauliz_tensor_hadamard(self, shots, theta, phi, varphi, tolerance): + def test_pauliz_tensor_hadamard(self, shots, theta, phi, varphi, tolerance, seed): """Test that a tensor product involving PauliZ and hadamard works correctly""" - dev = qml.device("default.qubit", wires=3, shots=shots, seed=167) + dev = qml.device("default.qubit", wires=3, shots=shots, seed=seed) @qml.qnode(dev) def circuit(a, b, c): @@ -298,9 +300,9 @@ def circuit(a, b, c): assert np.allclose(res, expected, **tolerance) - def test_tensor_hermitian(self, shots, theta, phi, varphi, tolerance): + def test_tensor_hermitian(self, shots, theta, phi, varphi, tolerance, seed): """Test that a tensor product involving qml.Hermitian works correctly""" - dev = qml.device("default.qubit", wires=3, shots=shots, seed=824) + dev = qml.device("default.qubit", wires=3, shots=shots, seed=seed) A = np.array( [ @@ -360,9 +362,9 @@ class TestTensorSample: """Tests for samples of tensor observables""" # pylint: disable=unused-argument - def test_paulix_tensor_pauliz(self, theta, phi, varphi, tol_stochastic): + def test_paulix_tensor_pauliz(self, theta, phi, varphi, tol_stochastic, seed): """Test that a tensor product involving PauliX and PauliZ works correctly""" - dev = qml.device("default.qubit", wires=2, shots=int(1e6), seed=524) + dev = qml.device("default.qubit", wires=2, shots=int(1e6), seed=seed) @qml.qnode(dev) def circuit(): @@ -374,9 +376,9 @@ def circuit(): # s1 should only contain 1 assert np.allclose(s1, 1, atol=tol_stochastic, rtol=0) - def test_paulix_tensor_pauliy(self, theta, phi, varphi, tol_stochastic): + def test_paulix_tensor_pauliy(self, theta, phi, varphi, tol_stochastic, seed): """Test that a tensor product involving PauliX and PauliY works correctly""" - dev = qml.device("default.qubit", wires=3, shots=int(1e6), seed=173) + dev = qml.device("default.qubit", wires=3, shots=int(1e6), seed=seed) @qml.qnode(dev, diff_method="parameter-shift") def circuit(a, b, c): @@ -388,9 +390,9 @@ def circuit(a, b, c): # s1 should only contain 1 and -1 assert np.allclose(s1**2, 1, atol=tol_stochastic, rtol=0) - def test_pauliz_tensor_hadamard(self, theta, phi, varphi, tol_stochastic): + def test_pauliz_tensor_hadamard(self, theta, phi, varphi, tol_stochastic, seed): """Test that a tensor product involving PauliZ and hadamard works correctly""" - dev = qml.device("default.qubit", wires=3, shots=int(1e6), seed=814) + dev = qml.device("default.qubit", wires=3, shots=int(1e6), seed=seed) @qml.qnode(dev, diff_method="parameter-shift") def circuit(a, b, c): @@ -402,9 +404,9 @@ def circuit(a, b, c): # s1 should only contain 1 and -1 assert np.allclose(s1**2, 1, atol=tol_stochastic, rtol=0) - def test_tensor_hermitian(self, theta, phi, varphi, tol_stochastic): + def test_tensor_hermitian(self, theta, phi, varphi, tol_stochastic, seed): """Test that a tensor product involving qml.Hermitian works correctly""" - dev = qml.device("default.qubit", wires=3, shots=int(1e6), seed=124) + dev = qml.device("default.qubit", wires=3, shots=int(1e6), seed=seed) A = np.array( [ diff --git a/tests/test_vqe.py b/tests/test_vqe.py index f4d3da8c034..60b16bd2fc1 100644 --- a/tests/test_vqe.py +++ b/tests/test_vqe.py @@ -280,7 +280,7 @@ def test_cost_expvals(self, coeffs, observables, expected): @pytest.mark.torch @pytest.mark.slow @pytest.mark.parametrize("shots", [None, [(8000, 5)], [(8000, 5), (9000, 4)]]) - def test_optimize_torch(self, shots): + def test_optimize_torch(self, shots, seed): """Test that a Hamiltonian cost function is the same with and without grouping optimization when using the Torch interface.""" @@ -306,7 +306,7 @@ def test_optimize_torch(self, shots): ) shape = qml.templates.StronglyEntanglingLayers.shape(n_layers=2, n_wires=4) - _rng = np.random.default_rng(1234) + _rng = np.random.default_rng(seed) w = _rng.random(shape) with qml.Tracker(dev) as tracker: @@ -328,7 +328,7 @@ def test_optimize_torch(self, shots): @pytest.mark.tf @pytest.mark.slow @pytest.mark.parametrize("shots", [None, [(8000, 5)], [(8000, 5), (9000, 4)]]) - def test_optimize_tf(self, shots): + def test_optimize_tf(self, shots, seed): """Test that a Hamiltonian cost function is the same with and without grouping optimization when using the TensorFlow interface.""" @@ -354,7 +354,7 @@ def test_optimize_tf(self, shots): ) shape = qml.templates.StronglyEntanglingLayers.shape(n_layers=2, n_wires=4) - _rng = np.random.default_rng(1234) + _rng = np.random.default_rng(seed) w = _rng.random(shape) with qml.Tracker(dev) as tracker: @@ -374,7 +374,7 @@ def test_optimize_tf(self, shots): @pytest.mark.autograd @pytest.mark.slow @pytest.mark.parametrize("shots", [None, [(8000, 5)], [(8000, 5), (9000, 4)]]) - def test_optimize_autograd(self, shots): + def test_optimize_autograd(self, shots, seed): """Test that a Hamiltonian cost function is the same with and without grouping optimization when using the autograd interface.""" @@ -400,7 +400,7 @@ def test_optimize_autograd(self, shots): ) shape = qml.templates.StronglyEntanglingLayers.shape(n_layers=2, n_wires=4) - _rng = np.random.default_rng(1234) + _rng = np.random.default_rng(seed) w = _rng.random(shape) with qml.Tracker(dev) as tracker: @@ -418,7 +418,7 @@ def test_optimize_autograd(self, shots): # pylint: disable=protected-access @pytest.mark.autograd - def test_optimize_multiple_terms_autograd(self): + def test_optimize_multiple_terms_autograd(self, seed): """Test that a Hamiltonian cost function is the same with and without grouping optimization when using the autograd interface, even when there are non-unique Hamiltonian terms.""" @@ -456,7 +456,7 @@ def test_optimize_multiple_terms_autograd(self): ) shape = qml.templates.StronglyEntanglingLayers.shape(n_layers=2, n_wires=5) - _rng = np.random.default_rng(1234) + _rng = np.random.default_rng(seed) w = _rng.random(shape) with qml.Tracker(dev) as tracker: @@ -474,7 +474,7 @@ def test_optimize_multiple_terms_autograd(self): # pylint: disable=protected-access @pytest.mark.torch - def test_optimize_multiple_terms_torch(self): + def test_optimize_multiple_terms_torch(self, seed): """Test that a Hamiltonian cost function is the same with and without grouping optimization when using the Torch interface, even when there are non-unique Hamiltonian terms.""" @@ -512,7 +512,7 @@ def test_optimize_multiple_terms_torch(self): ) shape = qml.templates.StronglyEntanglingLayers.shape(n_layers=2, n_wires=5) - _rng = np.random.default_rng(1234) + _rng = np.random.default_rng(seed) w = _rng.random(shape) with qml.Tracker(dev) as tracker: @@ -530,7 +530,7 @@ def test_optimize_multiple_terms_torch(self): # pylint: disable=protected-access @pytest.mark.tf - def test_optimize_multiple_terms_tf(self): + def test_optimize_multiple_terms_tf(self, seed): """Test that a Hamiltonian cost function is the same with and without grouping optimization when using the TensorFlow interface, even when there are non-unique Hamiltonian terms.""" @@ -568,7 +568,7 @@ def test_optimize_multiple_terms_tf(self): ) shape = qml.templates.StronglyEntanglingLayers.shape(n_layers=2, n_wires=5) - _rng = np.random.default_rng(1234) + _rng = np.random.default_rng(seed) w = _rng.random(shape) with qml.Tracker(dev) as tracker: @@ -610,6 +610,9 @@ def test_optimize_grad(self): ) shape = qml.templates.StronglyEntanglingLayers.shape(n_layers=2, n_wires=4) + # TODO: This is another case of a magic number in the sense that no other number allows + # this test to pass. This is likely because the expected `big_hamiltonian_grad` + # was calculated using this exact seed. This test needs to be revisited. _rng = pnp.random.default_rng(1967) w = _rng.uniform(low=0, high=2 * np.pi, size=shape, requires_grad=True) @@ -669,6 +672,9 @@ def test_optimize_grad_torch(self): ) shape = qml.templates.StronglyEntanglingLayers.shape(n_layers=2, n_wires=4) + # TODO: This is another case of a magic number in the sense that no other number allows + # this test to pass. This is likely because the expected `big_hamiltonian_grad` + # was calculated using this exact seed. This test needs to be revisited. _rng = np.random.default_rng(1967) w = _rng.uniform(low=0, high=2 * np.pi, size=shape) w = torch.tensor(w, requires_grad=True) @@ -696,6 +702,9 @@ def test_optimize_grad_tf(self): ) shape = qml.templates.StronglyEntanglingLayers.shape(n_layers=2, n_wires=4) + # TODO: This is another case of a magic number in the sense that no other number allows + # this test to pass. This is likely because the expected `big_hamiltonian_grad` + # was calculated using this exact seed. This test needs to be revisited. _rng = np.random.default_rng(1967) w = _rng.uniform(low=0, high=2 * np.pi, size=shape) w = tf.Variable(w) @@ -782,7 +791,7 @@ def circuit2(): @pytest.mark.jax @pytest.mark.parametrize("shots, dim", [([(1000, 2)], 2), ([30, 30], 2), ([2, 3, 4], 3)]) - def test_shot_distribution(self, shots, dim): + def test_shot_distribution(self, shots, dim, seed): """Tests that distributed shots work with the new VQE design.""" import jax @@ -798,7 +807,7 @@ def circuit(weights, coeffs): obs = [qml.PauliZ(0), qml.PauliX(0) @ qml.PauliZ(1)] coeffs = np.array([0.1, 0.2]) - key = jax.random.PRNGKey(42) + key = jax.random.PRNGKey(seed) weights = jax.random.uniform(key, [2, 2, 3]) res = circuit(weights, coeffs) diff --git a/tests/transforms/test_broadcast_expand.py b/tests/transforms/test_broadcast_expand.py index e1212626fc8..0e0d71bb6b8 100644 --- a/tests/transforms/test_broadcast_expand.py +++ b/tests/transforms/test_broadcast_expand.py @@ -23,7 +23,8 @@ from pennylane import numpy as pnp -def get_device(name="default.qubit", wires=2, seed=123): +def get_device(name="default.qubit", wires=2, seed=None): + assert seed is not None, "Please use the pytest-rng provided seed" return qml.device(name, wires=wires, seed=seed) @@ -90,7 +91,7 @@ class TestBroadcastExpand: @pytest.mark.parametrize("params, size", list(zip(parameters, sizes))) @pytest.mark.parametrize("obs, exp_fn", observables_and_exp_fns) - def test_expansion(self, params, size, obs, exp_fn): + def test_expansion(self, params, size, obs, exp_fn, seed): """Test that the expansion works as expected.""" ops = make_ops(*params) expvals = [qml.expval(ob) for ob in obs] @@ -101,18 +102,18 @@ def test_expansion(self, params, size, obs, exp_fn): assert len(tapes) == size assert all(_tape.batch_size is None for _tape in tapes) - result = fn(qml.execute(tapes, get_device(), None)) + result = fn(qml.execute(tapes, get_device(seed=seed), None)) expected = exp_fn(*params) assert qml.math.allclose(result, expected) @pytest.mark.parametrize("params", parameters) @pytest.mark.parametrize("obs, exp_fn", observables_and_exp_fns) - def test_expansion_qnode(self, params, obs, exp_fn): + def test_expansion_qnode(self, params, obs, exp_fn, seed): """Test that the transform integrates correctly with the transform program""" @qml.transforms.broadcast_expand - @qml.qnode(get_device()) + @qml.qnode(get_device(seed=seed)) def circuit(x, y, z, obs): qml.StatePrep(np.array([1, 0, 0, 0]), wires=[0, 1]) _ = make_ops(x, y, z) @@ -125,7 +126,7 @@ def circuit(x, y, z, obs): @pytest.mark.parametrize("params, size", list(zip(parameters, sizes))) @pytest.mark.parametrize("obs, exp_fn", observables_and_exp_fns) - def test_shot_vector_expval(self, params, size, obs, exp_fn, tol_stochastic): + def test_shot_vector_expval(self, params, size, obs, exp_fn, tol_stochastic, seed): """Test that expansion works as expected with shot vectors""" ops = make_ops(*params) expvals = [qml.expval(ob) for ob in obs] @@ -137,7 +138,7 @@ def test_shot_vector_expval(self, params, size, obs, exp_fn, tol_stochastic): assert len(tapes) == size assert all(_tape.batch_size is None for _tape in tapes) - result = fn(qml.execute(tapes, get_device(seed=1), None)) + result = fn(qml.execute(tapes, get_device(seed=seed), None)) expected = exp_fn(*params) assert len(result) == len(shots) @@ -153,7 +154,7 @@ def test_shot_vector_expval(self, params, size, obs, exp_fn, tol_stochastic): ([{"op": qml.PauliZ(0)}, {"wires": [0, 1]}], [2, 4]), ], ) - def test_shot_vector_probs(self, params, size, args, shapes): + def test_shot_vector_probs(self, params, size, args, shapes, seed): """Test that expansion works as expected with shot vectors""" ops = make_ops(*params) mps = [qml.probs(**a) for a in args] @@ -165,7 +166,7 @@ def test_shot_vector_probs(self, params, size, args, shapes): assert len(tapes) == size assert all(_tape.batch_size is None for _tape in tapes) - result = fn(qml.execute(tapes, get_device(), None)) + result = fn(qml.execute(tapes, get_device(seed=seed), None)) assert len(result) == len(shots) for r in result: for i, _r in enumerate(r): @@ -180,7 +181,7 @@ def test_shot_vector_probs(self, params, size, args, shapes): ([{"op": qml.PauliZ(0)}, {"wires": [0, 1]}], [(), (2,)]), ], ) - def test_shot_vector_sample(self, params, size, args, shapes): + def test_shot_vector_sample(self, params, size, args, shapes, seed): """Test that expansion works as expected with shot vectors""" ops = make_ops(*params) mps = [qml.sample(**a) for a in args] @@ -192,7 +193,7 @@ def test_shot_vector_sample(self, params, size, args, shapes): assert len(tapes) == size assert all(_tape.batch_size is None for _tape in tapes) - result = fn(qml.execute(tapes, get_device(), None)) + result = fn(qml.execute(tapes, get_device(seed=seed), None)) assert len(result) == len(shots) for i, r in enumerate(result): for j, _r in enumerate(r): @@ -211,7 +212,7 @@ def test_shot_vector_sample(self, params, size, args, shapes): [{"op": qml.PauliZ(0)}, {"wires": [0, 1]}], ], ) - def test_shot_vector_counts(self, params, size, args): + def test_shot_vector_counts(self, params, size, args, seed): """Test that expansion works as expected with shot vectors""" ops = make_ops(*params) mps = [qml.counts(**a) for a in args] @@ -223,7 +224,7 @@ def test_shot_vector_counts(self, params, size, args): assert len(tapes) == size assert all(_tape.batch_size is None for _tape in tapes) - result = fn(qml.execute(tapes, get_device(), None)) + result = fn(qml.execute(tapes, get_device(seed=seed), None)) assert len(result) == len(shots) for r in result: for _r in r: @@ -234,7 +235,7 @@ def test_shot_vector_counts(self, params, size, args): # TODO: Update broadcast_expand to unwrap counts dictionaries from 0-D numpy arrays assert isinstance(_r.item(), dict) - def test_state_prep(self): + def test_state_prep(self, seed): """Test that expansion works for state preparations""" ops = [qml.CNOT([0, 1])] meas = [qml.expval(qml.PauliZ(1))] @@ -245,7 +246,7 @@ def test_state_prep(self): assert len(tapes) == 4 assert all(t.batch_size is None for t in tapes) - result = fn(qml.execute(tapes, get_device(), None)) + result = fn(qml.execute(tapes, get_device(seed=seed), None)) expected = np.array([1, -1, -1, 1]) assert qml.math.allclose(result, expected) @@ -275,12 +276,12 @@ def test_not_copied(self): @pytest.mark.parametrize("params", parameters) @pytest.mark.parametrize("obs, exp_fn", observables_and_exp_fns) @pytest.mark.parametrize("diff_method", ["parameter-shift", "backprop"]) - def test_autograd(self, params, obs, exp_fn, diff_method): + def test_autograd(self, params, obs, exp_fn, diff_method, seed): """Test that the expansion works with autograd and is differentiable.""" params = tuple(pnp.array(p, requires_grad=True) for p in params) @qml.transforms.broadcast_expand - @qml.qnode(get_device(), interface="autograd", diff_method=diff_method) + @qml.qnode(get_device(seed=seed), interface="autograd", diff_method=diff_method) def cost(*params): make_ops(*params) return qml.math.stack([qml.expval(ob) for ob in obs]) @@ -299,7 +300,7 @@ def cost(*params): @pytest.mark.parametrize("obs, exp_fn", observables_and_exp_fns) @pytest.mark.parametrize("use_jit", [True, False]) @pytest.mark.parametrize("diff_method", ["parameter-shift", "backprop"]) - def test_jax(self, params, obs, exp_fn, use_jit, diff_method): + def test_jax(self, params, obs, exp_fn, use_jit, diff_method, seed): """Test that the expansion works with jax and is differentiable.""" # pylint: disable=too-many-arguments import jax @@ -309,7 +310,7 @@ def test_jax(self, params, obs, exp_fn, use_jit, diff_method): params = tuple(jax.numpy.array(p) for p in params) @qml.transforms.broadcast_expand - @qml.qnode(get_device(), interface="jax", diff_method=diff_method) + @qml.qnode(get_device(seed=seed), interface="jax", diff_method=diff_method) def cost(*params): make_ops(*params) return tuple(qml.expval(ob) for ob in obs) @@ -335,14 +336,14 @@ def cost(*params): @pytest.mark.tf @pytest.mark.parametrize("params", parameters) @pytest.mark.parametrize("obs, exp_fn", observables_and_exp_fns) - def test_tf(self, params, obs, exp_fn): + def test_tf(self, params, obs, exp_fn, seed): """Test that the expansion works with TensorFlow and is differentiable.""" import tensorflow as tf params = tuple(tf.Variable(p, dtype=tf.float64) for p in params) @qml.transforms.broadcast_expand - @qml.qnode(get_device(), interface="tensorflow") + @qml.qnode(get_device(seed=seed), interface="tensorflow") def cost(*params): make_ops(*params) return tuple(qml.expval(ob) for ob in obs) @@ -365,7 +366,7 @@ def cost(*params): @pytest.mark.parametrize("params", parameters) @pytest.mark.parametrize("obs, exp_fn", observables_and_exp_fns) @pytest.mark.parametrize("diff_method", ["parameter-shift", "backprop"]) - def test_torch(self, params, obs, exp_fn, diff_method): + def test_torch(self, params, obs, exp_fn, diff_method, seed): """Test that the expansion works with torch and is differentiable.""" import torch @@ -375,7 +376,7 @@ def test_torch(self, params, obs, exp_fn, diff_method): params = tuple(pnp.array(p, requires_grad=True) for p in params) @qml.transforms.broadcast_expand - @qml.qnode(get_device(), interface="torch", diff_method=diff_method) + @qml.qnode(get_device(seed=seed), interface="torch", diff_method=diff_method) def cost(*params): make_ops(*params) return tuple(qml.expval(ob) for ob in obs) diff --git a/tests/transforms/test_defer_measurements.py b/tests/transforms/test_defer_measurements.py index b85de016d3f..1c3b483c871 100644 --- a/tests/transforms/test_defer_measurements.py +++ b/tests/transforms/test_defer_measurements.py @@ -368,10 +368,12 @@ def circ1(phi): @pytest.mark.parametrize("reduce_postselected", [None, True, False]) @pytest.mark.parametrize("shots", [None, 1000]) @pytest.mark.parametrize("phi", np.linspace(np.pi / 2, 7 * np.pi / 2, 6)) - def test_some_postselection_qnode(self, phi, shots, reduce_postselected, tol, tol_stochastic): + def test_some_postselection_qnode( + self, phi, shots, reduce_postselected, tol, tol_stochastic, seed + ): """Test that a qnode with some mid-circuit measurements with postselection is transformed correctly by defer_measurements""" - dev = DefaultQubit(seed=822) + dev = DefaultQubit(seed=seed) dm_transform = qml.defer_measurements if reduce_postselected is not None: @@ -491,10 +493,10 @@ def circ2(): qml.assert_equal(op, expected_op) @pytest.mark.parametrize("shots", [None, 1000, [1000, 1000]]) - def test_measurement_statistics_single_wire(self, shots): + def test_measurement_statistics_single_wire(self, shots, seed): """Test that users can collect measurement statistics on a single mid-circuit measurement.""" - dev = DefaultQubit(seed=10) + dev = DefaultQubit(seed=seed) @qml.defer_measurements @qml.qnode(dev) @@ -503,7 +505,7 @@ def circ1(x): m0 = qml.measure(0) return qml.probs(op=m0) - dev = DefaultQubit(seed=10) + dev = DefaultQubit(seed=seed) @qml.qnode(dev) def circ2(x): @@ -548,11 +550,11 @@ def circ2(x): assert mp.mv.wires == qml.wires.Wires([1]) @pytest.mark.parametrize("shots", [None, 1000, [1000, 1000]]) - def test_terminal_measurements(self, shots): + def test_terminal_measurements(self, shots, seed): """Test that mid-circuit measurement statistics and terminal measurements can be made together.""" # Using DefaultQubit to allow non-commuting measurements - dev = DefaultQubit(seed=10) + dev = DefaultQubit(seed=seed) @qml.defer_measurements @qml.qnode(dev) @@ -562,7 +564,7 @@ def circ1(x, y): qml.RY(y, 1) return qml.expval(qml.PauliX(1)), qml.probs(op=m0) - dev = DefaultQubit(seed=10) + dev = DefaultQubit(seed=seed) @qml.qnode(dev) def circ2(x, y): diff --git a/tests/transforms/test_dynamic_one_shot.py b/tests/transforms/test_dynamic_one_shot.py index 903a54628f1..56d40256ce4 100644 --- a/tests/transforms/test_dynamic_one_shot.py +++ b/tests/transforms/test_dynamic_one_shot.py @@ -90,13 +90,13 @@ def f(x): @pytest.mark.jax @pytest.mark.parametrize("use_jit", [True, False]) @pytest.mark.parametrize("diff_method", [None, "best"]) -def test_hw_like_with_jax(use_jit, diff_method): +def test_hw_like_with_jax(use_jit, diff_method, seed): """Test that invalid shots are replaced with INTEGER_MIN_VAL if postselect_mode="hw-like" with JAX""" import jax # pylint: disable=import-outside-toplevel shots = 10 - dev = qml.device("default.qubit", shots=shots, seed=jax.random.PRNGKey(123)) + dev = qml.device("default.qubit", shots=shots, seed=jax.random.PRNGKey(seed)) @qml.qnode(dev, postselect_mode="hw-like", diff_method=diff_method) def f(x): @@ -280,15 +280,13 @@ class TestInterfaces: @pytest.mark.parametrize("shots", [1, 20, [20, 21]]) @pytest.mark.parametrize("n_mcms", [1, 3]) def test_interface_tape_results( - self, shots, n_mcms, measure_f, interface, use_interface_for_results + self, shots, n_mcms, measure_f, interface, use_interface_for_results, seed ): # pylint: disable=unused-argument """Test that the simulation results of a tape are correct with interface parameters""" if interface == "jax": from jax.random import PRNGKey - seed = PRNGKey(123) - else: - seed = 123 + seed = PRNGKey(seed) dev = qml.device("default.qubit", wires=4, shots=shots, seed=seed) param = qml.math.array(np.pi / 2, like=interface) diff --git a/tests/transforms/test_mitigate.py b/tests/transforms/test_mitigate.py index 05cd77b65da..6a05309d437 100644 --- a/tests/transforms/test_mitigate.py +++ b/tests/transforms/test_mitigate.py @@ -196,7 +196,7 @@ def test_reps_per_factor_not_1(self, mocker): assert args[0][0] == scale_factors assert np.allclose(args[0][1], np.mean(np.reshape(random_results, (3, 2)), axis=1)) - def test_broadcasting(self): + def test_broadcasting(self, seed): """Tests that mitigate_with_zne supports batch arguments""" batch_size = 2 @@ -214,7 +214,7 @@ def original_qnode(inputs): mitigated_qnode_expanded = qml.transforms.mitigate_with_zne( expanded_qnode, [1, 2, 3], fold_global, richardson_extrapolate ) - rng = np.random.default_rng(seed=18954959) + rng = np.random.default_rng(seed=seed) inputs = rng.uniform(0, 1, size=(batch_size, 2**2)) result_orig = mitigated_qnode_orig(inputs) result_expanded = mitigated_qnode_expanded(inputs) diff --git a/tests/transforms/test_qcut.py b/tests/transforms/test_qcut.py index e5af9c16aff..d0bc675ca81 100644 --- a/tests/transforms/test_qcut.py +++ b/tests/transforms/test_qcut.py @@ -28,7 +28,6 @@ import numpy as onp import pytest -from flaky import flaky from networkx import MultiDiGraph from networkx import __version__ as networkx_version from networkx import number_of_selfloops @@ -2513,14 +2512,13 @@ class TestCutCircuitMCTransform: Tests that the `cut_circuit_mc` transform gives the correct results. """ - @flaky(max_runs=3) - def test_cut_circuit_mc_expval(self, dev_fn): + def test_cut_circuit_mc_expval(self, dev_fn, seed): """ Tests that a circuit containing sampling measurements can be cut and recombined to give the correct expectation value """ - dev_sim = dev_fn(wires=3) + dev_sim = dev_fn(wires=3, seed=seed) @qml.qnode(dev_sim) def target_circuit(v): @@ -3878,9 +3876,8 @@ class TestCutCircuitTransform: Tests for the cut_circuit transform """ - @flaky(max_runs=3) @pytest.mark.parametrize("shots", [None, int(1e7)]) - def test_simple_cut_circuit(self, mocker, use_opt_einsum, shots): + def test_simple_cut_circuit(self, mocker, use_opt_einsum, shots, seed): """ Tests the full circuit cutting pipeline returns the correct value and gradient for a simple circuit using the `cut_circuit` transform. @@ -3888,7 +3885,7 @@ def test_simple_cut_circuit(self, mocker, use_opt_einsum, shots): if use_opt_einsum: pytest.importorskip("opt_einsum") - dev = qml.device("default.qubit", wires=2, shots=shots) + dev = qml.device("default.qubit", wires=2, shots=shots, seed=seed) @qml.qnode(dev) def circuit(x): @@ -4423,9 +4420,8 @@ def f(params): assert np.isclose(res, res_expected) assert np.allclose(grad, grad_expected) - @flaky(max_runs=3) @pytest.mark.parametrize("shots", [None, int(1e7)]) - def test_standard_circuit(self, mocker, use_opt_einsum, shots): + def test_standard_circuit(self, mocker, use_opt_einsum, shots, seed): """ Tests that the full circuit cutting pipeline returns the correct value for a typical scenario. The circuit is drawn below: @@ -4438,7 +4434,7 @@ def test_standard_circuit(self, mocker, use_opt_einsum, shots): if use_opt_einsum: pytest.importorskip("opt_einsum") - dev_original = qml.device("default.qubit", wires=4) + dev_original = qml.device("default.qubit", wires=4, seed=seed) # We need a 3-qubit device dev_cut = qml.device("default.qubit", wires=3, shots=shots) From ce0ab85224f966ae56c21998a3ebc465cff9e14f Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Tue, 29 Oct 2024 09:51:39 +0000 Subject: [PATCH 10/11] [no ci] bump nightly version --- pennylane/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/_version.py b/pennylane/_version.py index b797a93e02d..19d332966c9 100644 --- a/pennylane/_version.py +++ b/pennylane/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev0" +__version__ = "0.40.0-dev1" From 3324b8e102a8136e8dda91ab7cef81f0894d1562 Mon Sep 17 00:00:00 2001 From: Guillermo Alonso-Linaje <65235481+KetpuntoG@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:39:56 -0400 Subject: [PATCH 11/11] suggestion, pytrees --- pennylane/ops/op_math/adjoint.py | 8 ++------ pennylane/ops/op_math/controlled.py | 19 ++----------------- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/pennylane/ops/op_math/adjoint.py b/pennylane/ops/op_math/adjoint.py index c994ce14692..f0ebacfe5e2 100644 --- a/pennylane/ops/op_math/adjoint.py +++ b/pennylane/ops/op_math/adjoint.py @@ -22,7 +22,6 @@ from pennylane.compiler import compiler from pennylane.math import conj, moveaxis, transpose from pennylane.operation import Observable, Operation, Operator -from pennylane.ops.op_math.controlled import remove_from_queue_args_and_kwargs from pennylane.queuing import QueuingManager from pennylane.tape import make_qscript @@ -238,11 +237,8 @@ def _adjoint_transform(qfunc: Callable, lazy=True) -> Callable: def wrapper(*args, **kwargs): qscript = make_qscript(qfunc)(*args, **kwargs) - for arg in args: - remove_from_queue_args_and_kwargs(arg) - - for value in kwargs.values(): - remove_from_queue_args_and_kwargs(value) + leaves, _ = qml.pytrees.flatten((args, kwargs), lambda obj: isinstance(obj, Operator)) + _ = [qml.QueuingManager.remove(l) for l in leaves if isinstance(l, Operator)] if lazy: adjoint_ops = [Adjoint(op) for op in reversed(qscript.operations)] diff --git a/pennylane/ops/op_math/controlled.py b/pennylane/ops/op_math/controlled.py index 8a5ddc15af7..6ac000cefd5 100644 --- a/pennylane/ops/op_math/controlled.py +++ b/pennylane/ops/op_math/controlled.py @@ -199,28 +199,13 @@ def create_controlled_op(op, control, control_values=None, work_wires=None): return _ctrl_transform(op, control, control_values, work_wires) -def remove_from_queue_args_and_kwargs(item): - """function used to recursively remove operators that have been added to the queue in an argument or kwarg.""" - if isinstance(item, (list, tuple, set)): - for elem in item: - remove_from_queue_args_and_kwargs(elem) - elif isinstance(item, dict): - for value in item.values(): - remove_from_queue_args_and_kwargs(value) - elif isinstance(item, Operator): - qml.queuing.QueuingManager.remove(item) - - def _ctrl_transform(op, control, control_values, work_wires): @wraps(op) def wrapper(*args, **kwargs): qscript = qml.tape.make_qscript(op)(*args, **kwargs) - for arg in args: - remove_from_queue_args_and_kwargs(arg) - - for value in kwargs.values(): - remove_from_queue_args_and_kwargs(value) + leaves, _ = qml.pytrees.flatten((args, kwargs), lambda obj: isinstance(obj, Operator)) + _ = [qml.QueuingManager.remove(l) for l in leaves if isinstance(l, Operator)] # flip control_values == 0 wires here, so we don't have to do it for each individual op. flip_control_on_zero = (len(qscript) > 1) and (control_values is not None)