def test_returning_non_measurements(self): """Test that an exception is raised if a non-measurement is returned from the QNode.""" 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 5 qn = QNode(func, dev) with pytest.raises(qml.QuantumFunctionError, match="must return either a single measurement"): qn(5, 1) def func(x, y): qml.RX(x, wires=0) qml.RY(y, wires=1) qml.CNOT(wires=[0, 1]) return expval(qml.PauliZ(0)), 5 qn = QNode(func, dev) with pytest.raises(qml.QuantumFunctionError, match="must return either a single measurement"): qn(5, 1)
def test_gradient_repeated_gate_parameters(self, mocker, tol): """Tests that repeated use of a free parameter in a multi-parameter gate yield correct gradients.""" dev = qml.device("default.qubit", wires=1) 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 expval(qml.PauliX(0)) spy_numeric = mocker.spy(QuantumTape, "numeric_pd") spy_analytic = mocker.spy(ReversibleTape, "analytic_pd") cost = QNode(circuit, dev, diff_method="finite-diff") grad_fn = qml.grad(cost) grad_F = grad_fn(params) spy_numeric.assert_called() spy_analytic.assert_not_called() cost = QNode(circuit, dev, diff_method="reversible") grad_fn = qml.grad(cost) grad_A = grad_fn(params) spy_analytic.assert_called() # the different methods agree assert np.allclose(grad_A, grad_F, atol=tol, rtol=0)
def test_qnode(self, mocker, tol): """Test that specifying diff_method allows the reversible method to be selected""" args = np.array([0.54, 0.1, 0.5], requires_grad=True) dev = qml.device("default.qubit", wires=2) def circuit(x, y, z): qml.Hadamard(wires=0) qml.RX(0.543, wires=0) qml.CNOT(wires=[0, 1]) qml.Rot(x, y, z, wires=0) qml.Rot(1.3, -2.3, 0.5, wires=[0]) qml.RZ(-0.5, wires=0) qml.RY(0.5, wires=1) qml.CNOT(wires=[0, 1]) return expval(qml.PauliX(0) @ qml.PauliZ(1)) qnode1 = QNode(circuit, dev, diff_method="reversible") spy = mocker.spy(ReversibleTape, "analytic_pd") grad_fn = qml.grad(qnode1) grad_A = grad_fn(*args) spy.assert_called() assert isinstance(qnode1.qtape, ReversibleTape) qnode2 = QNode(circuit, dev, diff_method="finite-diff") grad_fn = qml.grad(qnode2) grad_F = grad_fn(*args) assert not isinstance(qnode2.qtape, ReversibleTape) assert np.allclose(grad_A, grad_F, atol=tol, rtol=0)
def test_invalid_interface(self): """Test that an exception is raised for an invalid interface""" dev = qml.device("default.qubit", wires=1) with pytest.raises(qml.QuantumFunctionError, match="Unknown interface"): QNode(None, dev, interface="something")
def test_basic_tape_construction(self, tol): """Test that a quantum tape is properly constructed""" 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 expval(qml.PauliZ(0)) qn = QNode(func, dev) x = 0.12 y = 0.54 res = qn(x, y) assert isinstance(qn.qtape, QuantumTape) assert len(qn.qtape.operations) == 3 assert len(qn.qtape.observables) == 1 assert qn.qtape.num_params == 2 expected = qn.qtape.execute(dev) assert np.allclose(res, expected, atol=tol, rtol=0) # when called, a new quantum tape is constructed old_tape = qn.qtape res2 = qn(x, y) assert np.allclose(res, res2, atol=tol, rtol=0) assert qn.qtape is not old_tape
def test_unknown_diff_method(self): """Test that an exception is raised for an unknown differentiation method""" dev = qml.device("default.qubit", wires=1) with pytest.raises( qml.QuantumFunctionError, match="Differentiation method hello not recognized"): QNode(None, dev, diff_method="hello")
def test_diff_method(self, mocker): """Test that a user-supplied diff-method correctly returns the right quantum tape, interface, and diff method.""" dev = qml.device("default.qubit", wires=1) mock_best = mocker.patch("pennylane.beta.tapes.QNode.get_best_method") mock_best.return_value = 1, 2, 3 mock_backprop = mocker.patch( "pennylane.beta.tapes.QNode._validate_backprop_method") mock_backprop.return_value = 4, 5, 6 mock_device = mocker.patch( "pennylane.beta.tapes.QNode._validate_device_method") mock_device.return_value = 7, 8, 9 qn = QNode(None, dev, diff_method="best") assert qn._tape == mock_best.return_value[0] assert qn.interface == mock_best.return_value[1] assert qn.diff_options["method"] == mock_best.return_value[2] qn = QNode(None, dev, diff_method="backprop") assert qn._tape == mock_backprop.return_value[0] assert qn.interface == mock_backprop.return_value[1] assert qn.diff_options["method"] == mock_backprop.return_value[2] mock_backprop.assert_called_once() qn = QNode(None, dev, diff_method="device") assert qn._tape == mock_device.return_value[0] assert qn.interface == mock_device.return_value[1] assert qn.diff_options["method"] == mock_device.return_value[2] mock_device.assert_called_once() qn = QNode(None, dev, diff_method="finite-diff") assert qn._tape == QuantumTape assert qn.diff_options["method"] == "numeric" qn = QNode(None, dev, diff_method="parameter-shift") assert qn._tape == QubitParamShiftTape assert qn.diff_options["method"] == "analytic" # check that get_best_method was only ever called once mock_best.assert_called_once()
def test_consistent_measurement_order(self): """Test evaluation exceeds as expected if measurements are returned in the same order to how they were queued on the tape""" dev = qml.device("default.qubit", wires=2) def func(x, y): global op1, op2, op3, m1, m2 op1 = qml.RX(x, wires=0) op2 = qml.RY(y, wires=1) op3 = qml.CNOT(wires=[0, 1]) m1 = expval(qml.PauliZ(0)) m2 = expval(qml.PauliX(1)) return [m1, m2] qn = QNode(func, dev) qn(5, 1) # evaluate the QNode assert qn.qtape.operations == [op1, op2, op3] assert qn.qtape.measurements == [m1, m2]
def test_inconsistent_measurement_order(self): """Test that an exception is raised if measurements are returned in an order different to how they were queued on the tape""" 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]) m = expval(qml.PauliZ(0)) return expval(qml.PauliX(1)), m qn = QNode(func, dev) with pytest.raises( qml.QuantumFunctionError, match= "measurements must be returned in the order they are measured", ): qn(5, 1)
def test_import_error(self, mocker): """Test that an exception is caught on import error""" mock = mocker.patch( "pennylane.beta.interfaces.torch.TorchInterface.apply") mock.side_effect = ImportError() def func(x, y): qml.RX(x, wires=0) qml.RY(y, wires=1) qml.CNOT(wires=[0, 1]) return expval(qml.PauliZ(0)) dev = qml.device("default.qubit", wires=2) qn = QNode(func, dev, interface="torch") with pytest.raises( qml.QuantumFunctionError, match= "PyTorch not found. Please install the latest version of PyTorch to enable the 'torch' interface", ): qn(0.1, 0.1)
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 probs(wires=0), probs(wires=1) qn = QNode(func, dev, h=1e-8, order=2) assert qn.diff_options["h"] == 1e-8 assert qn.diff_options["order"] == 2 x = 0.12 y = 0.54 res = qn(x, y) jac = qn.qtape.jacobian(dev, params=[0.45, 0.1]) assert jac.shape == (4, 2)
def test_invalid_device(self): """Test that an exception is raised for an invalid device""" with pytest.raises(qml.QuantumFunctionError, match="Invalid device"): QNode(None, None)