def test_expval_and_variance(self, tol): """Test that the qnode works for a combination of expectation values and variances""" dev = qml.device("default.qubit", wires=3) def circuit(a, b, c): qml.RX(a, wires=0) qml.RY(b, wires=1) qml.CNOT(wires=[1, 2]) qml.RX(c, wires=2) qml.CNOT(wires=[0, 1]) qml.RZ(c, wires=2) return qml.var(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)), qml.var( qml.PauliZ(2)) circuit = QubitQNode(circuit, dev) a = 0.54 b = -0.423 c = 0.123 var = circuit(a, b, c) expected = np.array([ np.sin(a)**2, np.cos(a) * np.cos(b), 0.25 * (3 - 2 * np.cos(b)**2 * np.cos(2 * c) - np.cos(2 * b)), ]) assert var == pytest.approx(expected, abs=tol) # # circuit jacobians gradA = circuit.jacobian([a, b, c], method="A") gradF = circuit.jacobian([a, b, c], method="F") expected = np.array([ [2 * np.cos(a) * np.sin(a), -np.cos(b) * np.sin(a), 0], [ 0, -np.cos(a) * np.sin(b), 0.5 * (2 * np.cos(b) * np.cos(2 * c) * np.sin(b) + np.sin(2 * b)), ], [0, 0, np.cos(b)**2 * np.sin(2 * c)], ]).T assert gradF == pytest.approx(expected, abs=tol) assert gradA == pytest.approx(expected, abs=tol)
def test_differentiate_second_positional(self, tol): """Tests that the second positional arguments are differentiated.""" def circuit3(a, b): qml.RX(b, wires=0) return qml.expval(qml.PauliZ(0)) dev = qml.device("default.qubit", wires=2) circuit3 = QubitQNode(circuit3, dev) a = 0.7418 b = -5.0 circuit_output = circuit3(a, b) expected_output = np.cos(b) assert circuit_output == pytest.approx(expected_output, abs=tol) # circuit jacobians circuit_jacobian = circuit3.jacobian([a, b]) expected_jacobian = np.array([[0, -np.sin(b)]]) assert circuit_jacobian == pytest.approx(expected_jacobian, abs=tol)
def test_involutory_variance(self, tol): """Tests qubit observable that are involutory""" def circuit(a): qml.RX(a, wires=0) return qml.var(qml.PauliZ(0)) dev = qml.device("default.qubit", wires=1) circuit = QubitQNode(circuit, dev) a = 0.54 var = circuit(a) expected = 1 - np.cos(a) ** 2 assert var == pytest.approx(expected, abs=tol) # circuit jacobians gradA = circuit.jacobian([a], method="A") gradF = circuit.jacobian([a], method="F") expected = 2 * np.sin(a) * np.cos(a) assert gradF == pytest.approx(expected, abs=tol) assert gradA == pytest.approx(expected, abs=tol)
def test_differentiate_all_positional(self, tol): """Tests that all positional arguments are differentiated.""" def circuit1(a, b, c): qml.RX(a, wires=0) qml.RX(b, wires=1) qml.RX(c, wires=2) return tuple(qml.expval(qml.PauliZ(idx)) for idx in range(3)) dev = qml.device("default.qubit", wires=3) circuit1 = QubitQNode(circuit1, dev) vals = np.array([np.pi, np.pi / 2, np.pi / 3]) circuit_output = circuit1(*vals) expected_output = np.cos(vals) assert circuit_output == pytest.approx(expected_output, abs=tol) # circuit jacobians circuit_jacobian = circuit1.jacobian(vals) expected_jacobian = -np.diag(np.sin(vals)) assert circuit_jacobian == pytest.approx(expected_jacobian, abs=tol)
def test_parameter_multipliers(self, mult, tol): """Test that various types and values of scalar multipliers for differentiable qfunc parameters yield the correct gradients.""" def circuit(x): qml.RY(mult * x, wires=[0]) return qml.expval(qml.PauliX(0)) dev = qml.device("default.qubit", wires=1) q = QubitQNode(circuit, dev) par = [0.1] # gradients exact = mult * np.cos(mult * np.array([par])) grad_F = q.jacobian(par, method="F") grad_A = q.jacobian(par, method="A") # different methods must agree assert grad_F == pytest.approx(exact, abs=tol) assert grad_A == pytest.approx(exact, abs=tol)
def test_fanout(self, tol): """Tests qubit observable with repeated parameters""" def circuit(a): qml.RX(a, wires=0) qml.RY(a, wires=0) return qml.var(qml.PauliZ(0)) dev = qml.device("default.qubit", wires=2) circuit = QubitQNode(circuit, dev) a = 0.54 var = circuit(a) expected = 0.5 * np.sin(a) ** 2 * (np.cos(2 * a) + 3) assert var == pytest.approx(expected, abs=tol) # circuit jacobians gradA = circuit.jacobian([a], method="A") gradF = circuit.jacobian([a], method="F") expected = 4 * np.sin(a) * np.cos(a) ** 3 assert gradA == pytest.approx(expected, abs=tol) assert gradF == pytest.approx(expected, abs=tol)
def test_Rot_gradient(self, theta, tol): """Tests that the automatic gradient of a arbitrary Euler-angle-parameterized gate is correct.""" def circuit(x, y, z): qml.Rot(x, y, z, wires=[0]) return qml.expval(qml.PauliZ(0)) dev = qml.device("default.qubit", wires=1) circuit = QubitQNode(circuit, dev) eye = np.eye(3) angle_inputs = np.array([theta, theta**3, np.sqrt(2) * theta]) autograd_val = circuit.jacobian(angle_inputs) manualgrad_val = np.zeros((1, 3)) for idx in range(3): onehot_idx = eye[idx] param1 = angle_inputs + np.pi / 2 * onehot_idx param2 = angle_inputs - np.pi / 2 * onehot_idx manualgrad_val[0, idx] = (circuit(*param1) - circuit(*param2)) / 2 assert autograd_val == pytest.approx(manualgrad_val, abs=tol)
def test_non_involutory_variance(self, tol): """Tests a qubit Hermitian observable that is not involutory""" A = np.array([[4, -1 + 6j], [-1 - 6j, 2]]) def circuit(a): qml.RX(a, wires=0) return qml.var(qml.Hermitian(A, 0)) dev = qml.device("default.qubit", wires=1) circuit = QubitQNode(circuit, dev) a = 0.54 var = circuit(a) expected = (39 / 2) - 6 * np.sin(2 * a) + (35 / 2) * np.cos(2 * a) assert var == pytest.approx(expected, abs=tol) # circuit jacobians gradA = circuit.jacobian([a], method="A") gradF = circuit.jacobian([a], method="F") expected = -35 * np.sin(2 * a) - 12 * np.cos(2 * a) assert gradA == pytest.approx(expected, abs=tol) assert gradF == pytest.approx(expected, abs=tol)
def test_differentiate_second_third_positional(self, tol): """Tests that the second and third positional arguments are differentiated.""" def circuit4(a, b, c): qml.RX(b, wires=0) qml.RX(c, wires=1) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)) dev = qml.device("default.qubit", wires=2) circuit4 = QubitQNode(circuit4, dev) a = 0.7418 b = -5.0 c = np.pi / 7 circuit_output = circuit4(a, b, c) expected_output = np.array([np.cos(b), np.cos(c)]) assert circuit_output == pytest.approx(expected_output, abs=tol) # circuit jacobians circuit_jacobian = circuit4.jacobian([a, b, c]) expected_jacobian = np.array([[0.0, -np.sin(b), 0.0], [0.0, 0.0, -np.sin(c)]]) assert circuit_jacobian == pytest.approx(expected_jacobian, abs=tol)
def test_gradient_gate_with_multiple_parameters(self, tol): """Tests that gates with multiple free parameters yield correct gradients.""" par = [0.5, 0.3, -0.7] def qf(x, y, z): qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) return qml.expval(qml.PauliZ(0)) dev = qml.device("default.qubit", wires=1) q = QubitQNode(qf, dev) value = q(*par) grad_A = q.jacobian(par, method="A") grad_F = q.jacobian(par, method="F") # analytic method works for every parameter assert q.par_to_grad_method == {0: "A", 1: "A", 2: "A"} # gradient has the correct shape and every element is nonzero assert grad_A.shape == (1, 3) assert np.count_nonzero(grad_A) == 3 # the different methods agree assert grad_A == pytest.approx(grad_F, abs=tol)