def test_interface_jax(self, dev): """Test if the gradients agree between adjoint and finite-difference methods in the jax interface""" jax = pytest.importorskip("jax") def f(params1, params2): qml.RX(0.4, wires=[0]) qml.RZ(params1 * jax.numpy.sqrt(params2), wires=[0]) qml.RY(jax.numpy.cos(params2), wires=[0]) return qml.expval(qml.PauliZ(0)) params1 = jax.numpy.array(0.3) params2 = jax.numpy.array(0.4) h = 2e-3 if dev.R_DTYPE == np.float32 else 1e-7 tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 qnode_adjoint = QNode(f, dev, interface="jax", diff_method="adjoint") qnode_fd = QNode(f, dev, interface="jax", diff_method="finite-diff", h=h) grad_adjoint = jax.grad(qnode_adjoint)(params1, params2) grad_fd = jax.grad(qnode_fd)(params1, params2) assert np.allclose(grad_adjoint, grad_fd, atol=tol)
def test_gradient_repeated_gate_parameters(self, mocker, dev): """Tests that repeated use of a free parameter in a multi-parameter gate yields correct gradients.""" params = np.array([0.8, 1.3], requires_grad=True) def circuit(params): qml.RX(np.array(np.pi / 4, requires_grad=False), wires=[0]) qml.Rot(params[1], params[0], 2 * params[0], wires=[0]) return qml.expval(qml.PauliX(0)) spy_analytic = mocker.spy(dev, "adjoint_jacobian") h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 cost = QNode(circuit, dev, diff_method="finite-diff", h=h) grad_fn = qml.grad(cost) grad_F = grad_fn(params) spy_analytic.assert_not_called() cost = QNode(circuit, dev, diff_method="adjoint") grad_fn = qml.grad(cost) grad_D = grad_fn(params) spy_analytic.assert_called_once() # the different methods agree assert np.allclose(grad_D, grad_F, atol=tol, rtol=0)
def test_validate_backprop_child_method_wrong_interface(self, monkeypatch): """Test that the method for validating the backprop diff method tape raises an error if a child device supports backprop but using a different interface""" dev = qml.device("default.qubit", wires=1) test_interface = "something" orig_capabilities = dev.capabilities().copy() orig_capabilities["passthru_devices"] = {test_interface: "default.gaussian"} monkeypatch.setattr(dev, "capabilities", lambda: orig_capabilities) with pytest.raises(qml.QuantumFunctionError, match=r"when using the \['something'\] interface"): QNode._validate_backprop_method(dev, "another_interface")
def test_backprop_error(self): """Test if an error is raised when caching is used with the backprop diff_method""" dev = qml.device("default.qubit", wires=2, cache=10) with pytest.raises( qml.QuantumFunctionError, match="Device caching is incompatible with the backprop"): QNode(qfunc, dev, diff_method="backprop")
def test_validate_device_method(self, monkeypatch): """Test that the method for validating the device diff method tape works as expected""" dev = qml.device("default.qubit", wires=1) with pytest.raises( qml.QuantumFunctionError, match="does not provide a native method for computing the jacobian", ): QNode._validate_device_method(dev) monkeypatch.setitem(dev._capabilities, "provides_jacobian", True) method, diff_options, device = QNode._validate_device_method(dev) assert method == "device" assert device is dev
def test_no_shots_per_call_if_user_has_shots_qfunc_arg(self): """Tests that the per-call shots overwriting is suspended if user has a shots argument, but a warning is raised.""" # Todo: use standard creation of qnode below for both asserts once we do not parse args to tensors any more dev = qml.device("default.qubit", wires=[qml.numpy.array(0), qml.numpy.array(1)], shots=10) def circuit(a, shots): qml.RX(a, wires=shots) return qml.sample(qml.PauliZ(wires=qml.numpy.array(0))) # assert that warning is still raised with pytest.warns( UserWarning, match="The 'shots' argument name is reserved for overriding" ): circuit = QNode(circuit, dev) assert len(circuit(0.8, 1)) == 10 assert circuit.qtape.operations[0].wires.labels == (1,) dev = qml.device("default.qubit", wires=2, shots=10) with pytest.warns( UserWarning, match="The 'shots' argument name is reserved for overriding" ): @qnode(dev) def circuit(a, shots): qml.RX(a, wires=shots) return qml.sample(qml.PauliZ(wires=0)) assert len(circuit(0.8, shots=0)) == 10 assert circuit.qtape.operations[0].wires.labels == (0,)
def test_jacobian(self, tol): """Test the jacobian computation""" dev = qml.device("default.qubit", wires=2) def func(x, y): qml.RX(x, wires=0) qml.RY(y, wires=1) qml.CNOT(wires=[0, 1]) return qml.probs(wires=0), qml.probs(wires=1) qn = QNode(func, dev, diff_method="finite-diff", h=1e-8, approx_order=2) assert qn.gradient_kwargs["h"] == 1e-8 assert qn.gradient_kwargs["approx_order"] == 2 jac = qn.gradient_fn(qn)(0.45, 0.1) assert jac.shape == (2, 2, 2)
def test_operator_all_wires(self, monkeypatch, tol): """Test that an operator that must act on all wires does, or raises an error.""" monkeypatch.setattr(qml.RX, "num_wires", qml.operation.AllWires) def circuit(x): qml.RX(x, wires=0) return qml.expval(qml.PauliZ(0)) dev = qml.device("default.qubit", wires=2) qnode = QNode(circuit, dev) with pytest.raises(qml.QuantumFunctionError, match="Operator RX must act on all wires"): qnode(0.5) dev = qml.device("default.qubit", wires=1) qnode = QNode(circuit, dev) assert np.allclose(qnode(0.5), np.cos(0.5), atol=tol, rtol=0)