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..1e798c14440 --- /dev/null +++ b/doc/releases/changelog-dev.md @@ -0,0 +1,24 @@ +:orphan: + +# Release 0.40.0-dev (development release) + +

New features since last release

+ +

Improvements 🛠

+ +

Breaking changes 💔

+ +

Deprecations 👋

+ +

Documentation 📝

+ +

Bug fixes 🐛

+ +* 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): + +Guillermo Alonso diff --git a/pennylane/_version.py b/pennylane/_version.py index 04c54fed9c2..55aacf482da 100644 --- a/pennylane/_version.py +++ b/pennylane/_version.py @@ -16,4 +16,5 @@ Version number (major.minor.patch[-label]) """ + __version__ = "0.39.0" 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/pennylane/ops/op_math/adjoint.py b/pennylane/ops/op_math/adjoint.py index e09ea4b007d..f0ebacfe5e2 100644 --- a/pennylane/ops/op_math/adjoint.py +++ b/pennylane/ops/op_math/adjoint.py @@ -236,6 +236,10 @@ def _adjoint_transform(qfunc: Callable, lazy=True) -> Callable: @wraps(qfunc) def wrapper(*args, **kwargs): qscript = make_qscript(qfunc)(*args, **kwargs) + + 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)] else: diff --git a/pennylane/ops/op_math/controlled.py b/pennylane/ops/op_math/controlled.py index 6448f1dc061..6ac000cefd5 100644 --- a/pennylane/ops/op_math/controlled.py +++ b/pennylane/ops/op_math/controlled.py @@ -204,6 +204,9 @@ def _ctrl_transform(op, control, control_values, work_wires): def wrapper(*args, **kwargs): qscript = qml.tape.make_qscript(op)(*args, **kwargs) + 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) op_control_values = None if flip_control_on_zero else control_values 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..6017b16bdf9 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) @@ -868,6 +868,7 @@ def test_single_op_defined_outside_queue_eager(self): assert len(q) == 1 assert q.queue[0] is out + @pytest.mark.usefixtures("legacy_opmath_only") 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 1cc3c5241f9..03ce3c0bb00 100644 --- a/tests/ops/op_math/test_controlled.py +++ b/tests/ops/op_math/test_controlled.py @@ -1872,6 +1872,23 @@ 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.""" + + def func(dic): + for gate in dic.values(): + qml.apply(gate) + + dev = qml.device("default.qubit") + + @qml.qnode(dev) + def circuit(): + qml.ctrl(func, control=0)({1: qml.X(1), 2: qml.Z(1)}) + return qml.state() + + circuit() + assert len(circuit.tape.operations) == 2 + class _Rot(Operation): """A rotation operation that is not an instance of Rot 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/op_math/test_prod.py b/tests/ops/op_math/test_prod.py index bebc7112f93..1d1c6756e53 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.""" 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)