def test_keywordarg_not_differentiated(self): "Tests that qnodes do not differentiate w.r.t. keyword arguments." self.logTestName() a, b = 0.5, 0.54 def circuit1(weights, x=0.3): qml.QubitStateVector(np.array([1, 0, 1, 1]) / np.sqrt(3), [0, 1]) qml.Rot(weights[0], weights[1], x, 0) qml.CNOT([0, 1]) return qml.expval.PauliZ(0), qml.expval.PauliY(1) circuit1 = qml.QNode(circuit1, self.dev2) def circuit2(weights): qml.QubitStateVector(np.array([1, 0, 1, 1]) / np.sqrt(3), [0, 1]) qml.Rot(weights[0], weights[1], 0.3, 0) qml.CNOT([0, 1]) return qml.expval.PauliZ(0), qml.expval.PauliY(1) circuit2 = qml.QNode(circuit2, self.dev2) res1 = circuit1.jacobian([np.array([a, b])]) res2 = circuit2.jacobian([np.array([a, b])]) self.assertAllAlmostEqual(res1, res2, delta=self.tol)
def test_keywordarg_not_differentiated(self, tol): """Tests that qnodes do not differentiate w.r.t. keyword arguments.""" a, b = 0.5, 0.54 def circuit1(weights, x=0.3): qml.QubitStateVector(np.array([1, 0, 1, 1]) / np.sqrt(3), wires=[0, 1]) qml.Rot(weights[0], weights[1], x, wires=0) qml.CNOT(wires=[0, 1]) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliY(1)) dev = qml.device('default.qubit', wires=2) circuit1 = qml.QNode(circuit1, dev) def circuit2(weights): qml.QubitStateVector(np.array([1, 0, 1, 1]) / np.sqrt(3), wires=[0, 1]) qml.Rot(weights[0], weights[1], 0.3, wires=0) qml.CNOT(wires=[0, 1]) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliY(1)) circuit2 = qml.QNode(circuit2, dev) res1 = circuit1.jacobian([np.array([a, b])]) res2 = circuit2.jacobian([np.array([a, b])]) assert np.allclose(res1, res2, atol=tol, rtol=0)
def test_simple_circuits(self): default_qubit = qml.device('default.qubit', wires=4) for dev in self.devices: gates = [ qml.PauliX(wires=0), qml.PauliY(wires=1), qml.PauliZ(wires=2), qml.S(wires=3), qml.T(wires=0), qml.RX(2.3, wires=1), qml.RY(1.3, wires=2), qml.RZ(3.3, wires=3), qml.Hadamard(wires=0), qml.Rot(0.1, 0.2, 0.3, wires=1), qml.CRot(0.1, 0.2, 0.3, wires=[2, 3]), qml.Toffoli(wires=[0, 1, 2]), qml.SWAP(wires=[1, 2]), qml.CSWAP(wires=[1, 2, 3]), qml.U1(1.0, wires=0), qml.U2(1.0, 2.0, wires=2), qml.U3(1.0, 2.0, 3.0, wires=3), qml.CRX(0.1, wires=[1, 2]), qml.CRY(0.2, wires=[2, 3]), qml.CRZ(0.3, wires=[3, 1]), qml.CZ(wires=[2, 3]), qml.QubitUnitary(np.array([[1, 0], [0, 1]]), wires=2), ] layers = 3 np.random.seed(1967) gates_per_layers = [ np.random.permutation(gates).numpy() for _ in range(layers) ] for obs in { qml.PauliX(wires=0), qml.PauliY(wires=0), qml.PauliZ(wires=0), qml.Identity(wires=0), qml.Hadamard(wires=0) }: if obs.name in dev.observables: def circuit(): """4-qubit circuit with layers of randomly selected gates and random connections for multi-qubit gates.""" qml.BasisState(np.array([1, 0, 0, 0]), wires=[0, 1, 2, 3]) for gates in gates_per_layers: for gate in gates: if gate.name in dev.operations: qml.apply(gate) return qml.expval(obs) qnode_default = qml.QNode(circuit, default_qubit) qnode = qml.QNode(circuit, dev) assert np.allclose(qnode(), qnode_default(), atol=1e-3)
def test_caching(self): """Test that the circuit structure does not change on subsequent evalutions with caching turned on """ dev = qml.device('default.qubit', wires=2) def circuit(x, c=None): qml.RX(x, wires=0) for i in range(c.val): qml.RX(x, wires=i) return qml.expval(qml.PauliZ(0)) circuit = qml.QNode(circuit, dev, cache=True) # first evaluation circuit(0, c=0) # check structure assert len(circuit.queue) == 1 # second evaluation circuit(0, c=1) # check structure assert len(circuit.queue) == 1
def test_projectq_ops(self): results = [-1.0, -1.0] for i, dev in enumerate(self.devices[1:3]): gates = [ qml.PauliX(wires=0), qml.PauliY(wires=1), qml.PauliZ(wires=2), SqrtX(wires=0), SqrtSwap(wires=[3, 0]), ] layers = 3 np.random.seed(1967) gates_per_layers = [ np.random.permutation(gates).numpy() for _ in range(layers) ] def circuit(): """4-qubit circuit with layers of randomly selected gates.""" for gates in gates_per_layers: for gate in gates: if gate.name in dev.operations: qml.apply(gate) return qml.expval(qml.PauliZ(0)) qnode = qml.QNode(circuit, dev) assert np.allclose(qnode(), results[i], atol=1e-3)
def test_two_mode_rect_overparameterised(self): """Test that a two mode interferometer using the rectangular mesh correctly gives a beamsplitter+2 rotation gates when requested""" N = 2 wires = range(N) dev = qml.device('default.gaussian', wires=N) theta = [0.321] phi = [0.234] varphi = [0.42342, 0.543] def circuit(varphi): qml.template.Interferometer(theta, phi, varphi, wires=wires) return [qml.expval.MeanPhoton(w) for w in wires] qnode = qml.QNode(circuit, dev) self.assertAllAlmostEqual(qnode(varphi), [0, 0], delta=self.tol) queue = qnode.queue self.assertEqual(len(queue), 3) self.assertTrue(isinstance(qnode.queue[0], qml.Beamsplitter)) self.assertAllEqual(qnode.queue[0].parameters, theta + phi) self.assertTrue(isinstance(qnode.queue[1], qml.Rotation)) self.assertAllEqual(qnode.queue[1].parameters, [varphi[0]]) self.assertTrue(isinstance(qnode.queue[2], qml.Rotation)) self.assertAllEqual(qnode.queue[2].parameters, [varphi[1]])
def test_four_mode_triangular(self): """Test that a 4 mode interferometer using triangular mesh gives the correct gates""" N = 4 wires = range(N) dev = qml.device('default.gaussian', wires=N) theta = [0.321, 0.4523, 0.21321, 0.123, 0.5234, 1.23] phi = [0.234, 0.324, 0.234, 1.453, 1.42341, -0.534] varphi = [0.42342, 0.234, 0.4523] def circuit_tria(varphi): qml.template.Interferometer(theta, phi, varphi, mesh='triangular', wires=wires) return [qml.expval.MeanPhoton(w) for w in wires] qnode = qml.QNode(circuit_tria, dev) self.assertAllAlmostEqual(qnode(varphi), [0] * N, delta=self.tol) queue = qnode.queue self.assertEqual(len(queue), 9) expected_bs_wires = [[2, 3], [1, 2], [0, 1], [2, 3], [1, 2], [2, 3]] for idx, op in enumerate(qnode.queue[:6]): self.assertTrue(isinstance(op, qml.Beamsplitter)) self.assertAllEqual(op.parameters, [theta[idx], phi[idx]]) self.assertAllEqual(op.wires, expected_bs_wires[idx]) for idx, op in enumerate(qnode.queue[6:]): self.assertTrue(isinstance(op, qml.Rotation)) self.assertAllEqual(op.parameters, [varphi[idx]]) self.assertAllEqual(op.wires, [idx])
def test_op_successors(self): "Tests QNode._op_successors()." self.logTestName() def qf(x): qml.RX(x, wires=[0]) qml.CNOT(wires=[0, 1]) qml.RY(0.4, wires=[0]) qml.RZ(-0.2, wires=[1]) return qml.expval(qml.PauliX(0)), qml.expval(qml.PauliZ(1)) q = qml.QNode(qf, self.dev2) q.construct([1.0]) # the six operations in qf should appear in q.ops in the same order they appear above self.assertTrue(q.ops[0].name == 'RX') self.assertTrue(q.ops[1].name == 'CNOT') self.assertTrue(q.ops[2].name == 'RY') self.assertTrue(q.ops[3].name == 'RZ') self.assertTrue(q.ops[4].name == 'PauliX') self.assertTrue(q.ops[5].name == 'PauliZ') # only gates gate_successors = q._op_successors(0, only='G') self.assertTrue(q.ops[0] not in gate_successors) self.assertTrue(q.ops[1] in gate_successors) self.assertTrue(q.ops[4] not in gate_successors) # only evs ev_sucessors = q._op_successors(0, only='E') self.assertTrue(q.ops[0] not in ev_sucessors) self.assertTrue(q.ops[1] not in ev_sucessors) self.assertTrue(q.ops[4] in ev_sucessors) # both successors = q._op_successors(0, only=None) self.assertTrue(q.ops[0] not in successors) self.assertTrue(q.ops[1] in successors) self.assertTrue(q.ops[4] in successors)
def test_cv_gradients_multiple_gate_parameters(self): "Tests that gates with multiple free parameters yield correct gradients." self.logTestName() par = [0.4, -0.3, -0.7, 0.2] def qf(r0, phi0, r1, phi1): qml.Squeezing(r0, phi0, wires=[0]) qml.Squeezing(r1, phi1, wires=[0]) return qml.expval(qml.NumberOperator(0)) q = qml.QNode(qf, self.gaussian_dev) grad_F = q.jacobian(par, method='F') grad_A = q.jacobian(par, method='A') grad_A2 = q.jacobian(par, method='A', force_order2=True) # analytic method works for every parameter self.assertTrue(q.grad_method_for_par == {i:'A' for i in range(4)}) # the different methods agree self.assertAllAlmostEqual(grad_A, grad_F, delta=self.tol) self.assertAllAlmostEqual(grad_A2, grad_F, delta=self.tol) # check against the known analytic formula r0, phi0, r1, phi1 = par dn = np.zeros([4]) dn[0] = np.cosh(2 * r1) * np.sinh(2 * r0) + np.cos(phi0 - phi1) * np.cosh(2 * r0) * np.sinh(2 * r1) dn[1] = -0.5 * np.sin(phi0 - phi1) * np.sinh(2 * r0) * np.sinh(2 * r1) dn[2] = np.cos(phi0 - phi1) * np.cosh(2 * r1) * np.sinh(2 * r0) + np.cosh(2 * r0) * np.sinh(2 * r1) dn[3] = 0.5 * np.sin(phi0 - phi1) * np.sinh(2 * r0) * np.sinh(2 * r1) self.assertAllAlmostEqual(grad_A, dn, delta=self.tol)
def test_keywordarg_gradient(self): "Tests that qnodes' keyword arguments work with gradients" self.logTestName() def circuit(x, y, input_state=np.array([0, 0])): qml.BasisState(input_state, wires=[0, 1]) qml.RX(x, wires=[0]) qml.RY(y, wires=[0]) return qml.expval.PauliZ(0) circuit = qml.QNode(circuit, self.dev2).to_torch() x = 0.543 y = 0.45632 x_t = torch.autograd.Variable(torch.tensor(x), requires_grad=True) y_t = torch.autograd.Variable(torch.tensor(y), requires_grad=True) c = circuit(x_t, y_t, input_state=np.array([0, 0])) c.backward() self.assertAllAlmostEqual(x_t.grad.numpy(), [-np.sin(x)*np.cos(y)], delta=self.tol) self.assertAllAlmostEqual(y_t.grad.numpy(), [-np.sin(y)*np.cos(x)], delta=self.tol) x_t = torch.autograd.Variable(torch.tensor(x), requires_grad=True) y_t = torch.autograd.Variable(torch.tensor(y), requires_grad=True) c = circuit(x_t, y_t, input_state=np.array([1, 0])) c.backward() self.assertAllAlmostEqual(x_t.grad.numpy(), [np.sin(x)*np.cos(y)], delta=self.tol) self.assertAllAlmostEqual(y_t.grad.numpy(), [np.sin(y)*np.cos(x)], delta=self.tol) x_t = torch.autograd.Variable(torch.tensor(x), requires_grad=True) y_t = torch.autograd.Variable(torch.tensor(y), requires_grad=True) c = circuit(x_t, y_t) c.backward() self.assertAllAlmostEqual(x_t.grad.numpy(), [-np.sin(x)*np.cos(y)], delta=self.tol) self.assertAllAlmostEqual(y_t.grad.numpy(), [-np.sin(y)*np.cos(x)], delta=self.tol)
def test_multidim_array(self, s, tol): """Tests that arguments which are multidimensional arrays are properly evaluated and differentiated in QNodes.""" multidim_array = np.reshape(b, s) def circuit(w): qml.RX(w[np.unravel_index(0, s)], wires=0) # b[0] qml.RX(w[np.unravel_index(1, s)], wires=1) # b[1] qml.RX(w[np.unravel_index(2, s)], wires=2) # ... qml.RX(w[np.unravel_index(3, s)], wires=3) qml.RX(w[np.unravel_index(4, s)], wires=4) qml.RX(w[np.unravel_index(5, s)], wires=5) qml.RX(w[np.unravel_index(6, s)], wires=6) qml.RX(w[np.unravel_index(7, s)], wires=7) return tuple(qml.expval(qml.PauliZ(idx)) for idx in range(len(b))) dev = qml.device('default.qubit', wires=8) circuit = qml.QNode(circuit, dev) # circuit evaluations circuit_output = circuit(multidim_array) expected_output = np.cos(b) assert np.allclose(circuit_output, expected_output, atol=tol, rtol=0) # circuit jacobians circuit_jacobian = circuit.jacobian([multidim_array]) expected_jacobian = -np.diag(np.sin(b)) assert np.allclose(circuit_jacobian, expected_jacobian, atol=tol, rtol=0)
def test_multiple_expectation_jacobian_array(self, tol): """Tests that qnodes using an array argument return correct gradients for multiple expectation values.""" a, b, c = 0.5, 0.54, 0.3 def circuit(weights): qml.QubitStateVector(np.array([1, 0, 1, 1]) / np.sqrt(3), wires=[0, 1]) qml.Rot(weights[0], weights[1], weights[2], wires=0) qml.CNOT(wires=[0, 1]) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliY(1)) dev = qml.device('default.qubit', wires=2) circuit = qml.QNode(circuit, dev) res = circuit.jacobian([np.array([a, b, c])]) assert np.allclose(self.expected_jacobian(a, b, c), res, atol=tol, rtol=0) jac = qml.jacobian(circuit, 0) res = jac(np.array([a, b, c])) assert np.allclose(self.expected_jacobian(a, b, c), res, atol=tol, rtol=0)
def test_qnode_gradient_fanout(self): "Tests that the correct gradient is computed for qnodes which use the same parameter in multiple gates." self.logTestName() extra_param = 0.31 def circuit(reused_param, other_param): qml.RX(extra_param, wires=[0]) qml.RY(reused_param, wires=[0]) qml.RZ(other_param, wires=[0]) qml.RX(reused_param, wires=[0]) return qml.expval.PauliZ(0) f = qml.QNode(circuit, self.qubit_dev1) zero_state = np.array([1., 0.]) for reused_p in thetas: reused_p = reused_p**3 / 19 for other_p in thetas: other_p = other_p**2 / 11 # autograd gradient grad = autograd.grad(f) grad_eval = grad(reused_p, other_p) # manual gradient grad_true0 = (expZ(Rx(reused_p) @ Rz(other_p) @ Ry(reused_p + np.pi / 2) @ Rx(extra_param) @ zero_state) \ -expZ(Rx(reused_p) @ Rz(other_p) @ Ry(reused_p - np.pi / 2) @ Rx(extra_param) @ zero_state)) / 2 grad_true1 = (expZ(Rx(reused_p + np.pi / 2) @ Rz(other_p) @ Ry(reused_p) @ Rx(extra_param) @ zero_state) \ -expZ(Rx(reused_p - np.pi / 2) @ Rz(other_p) @ Ry(reused_p) @ Rx(extra_param) @ zero_state)) / 2 grad_true = grad_true0 + grad_true1 # product rule self.assertAlmostEqual(grad_eval, grad_true, delta=self.tol)
def check_methods(qf, d): q = qml.QNode(qf, self.dev2) q.construct( par ) # NOTE: the default plugin is a discrete (qubit) simulator, it cannot execute CV gates, but the QNode can be constructed #print(q.grad_method_for_par) self.assertTrue(q.grad_method_for_par == d)
def test_multiple_expectation_jacobian_positional(self): "Tests that qnodes using positional arguments return correct gradients for multiple expectation values." self.logTestName() a, b, c = 0.5, 0.54, 0.3 def circuit(x, y, z): qml.QubitStateVector(np.array([1, 0, 1, 1]) / np.sqrt(3), [0, 1]) qml.Rot(x, y, z, 0) qml.CNOT([0, 1]) return qml.expval.PauliZ(0), qml.expval.PauliY(1) circuit = qml.QNode(circuit, self.dev2) # compare our manual Jacobian computation to theoretical result # Note: circuit.jacobian actually returns a full jacobian in this case res = circuit.jacobian(np.array([a, b, c])) self.assertAllAlmostEqual(self.expected_jacobian(a, b, c), res, delta=self.tol) # compare our manual Jacobian computation to autograd # not sure if this is the intended usage of jacobian jac0 = qml.jacobian(circuit, 0) jac1 = qml.jacobian(circuit, 1) jac2 = qml.jacobian(circuit, 2) res = np.stack([jac0(a, b, c), jac1(a, b, c), jac2(a, b, c)]).T self.assertAllAlmostEqual(self.expected_jacobian(a, b, c), res, delta=self.tol)
def test_qfunc_gradients(self): "Tests that the various ways of computing the gradient of a qfunc all agree." self.logTestName() def circuit(x, y, z): qml.RX(x, wires=[0]) qml.CNOT(wires=[0, 1]) qml.RY(-1.6, wires=[0]) qml.RY(y, wires=[1]) qml.CNOT(wires=[1, 0]) qml.RX(z, wires=[0]) qml.CNOT(wires=[0, 1]) return qml.expval.PauliZ(0) qnode = qml.QNode(circuit, self.qubit_dev2) params = np.array([0.1, -1.6, np.pi / 5]) # manual gradients grad_fd1 = qnode.jacobian(params, method='F', order=1) grad_fd2 = qnode.jacobian(params, method='F', order=2) grad_angle = qnode.jacobian(params, method='A') # automatic gradient grad_fn = autograd.grad(qnode.evaluate) grad_auto = grad_fn(params) # gradients computed with different methods must agree self.assertAllAlmostEqual(grad_fd1, grad_fd2, self.tol) self.assertAllAlmostEqual(grad_fd1, grad_angle, self.tol) self.assertAllAlmostEqual(grad_fd1, grad_auto, self.tol)
def test_multidim_array(self): "Tests that arguments which are multidimensional arrays are properly evaluated and differentiated in QNodes." self.logTestName() for s in b_shapes: multidim_array = np.reshape(b, s) def circuit(w): qml.RX(w[np.unravel_index(0, s)], 0) # b[0] qml.RX(w[np.unravel_index(1, s)], 1) # b[1] qml.RX(w[np.unravel_index(2, s)], 2) # ... qml.RX(w[np.unravel_index(3, s)], 3) qml.RX(w[np.unravel_index(4, s)], 4) qml.RX(w[np.unravel_index(5, s)], 5) qml.RX(w[np.unravel_index(6, s)], 6) qml.RX(w[np.unravel_index(7, s)], 7) return tuple(qml.expval.PauliZ(idx) for idx in range(len(b))) circuit = qml.QNode(circuit, self.dev8) # circuit evaluations circuit_output = circuit(multidim_array) expected_output = np.cos(b) self.assertAllAlmostEqual(circuit_output, expected_output, delta=self.tol) # circuit jacobians circuit_jacobian = circuit.jacobian(multidim_array) expected_jacobian = -np.diag(np.sin(b)) self.assertAllAlmostEqual(circuit_jacobian, expected_jacobian, delta=self.tol)
def test_multiple_expectation_jacobian_positional(self, tol): """Tests that qnodes using positional arguments return correct gradients for multiple expectation values.""" a, b, c = 0.5, 0.54, 0.3 def circuit(x, y, z): qml.QubitStateVector(np.array([1, 0, 1, 1]) / np.sqrt(3), wires=[0, 1]) qml.Rot(x, y, z, wires=0) qml.CNOT(wires=[0, 1]) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliY(1)) dev = qml.device('default.qubit', wires=2) circuit = qml.QNode(circuit, dev) # compare our manual Jacobian computation to theoretical result # Note: circuit.jacobian actually returns a full jacobian in this case res = circuit.jacobian(np.array([a, b, c])) assert np.allclose(self.expected_jacobian(a, b, c), res, atol=tol, rtol=0) # compare our manual Jacobian computation to autograd # not sure if this is the intended usage of jacobian jac0 = qml.jacobian(circuit, 0) jac1 = qml.jacobian(circuit, 1) jac2 = qml.jacobian(circuit, 2) res = np.stack([jac0(a, b, c), jac1(a, b, c), jac2(a, b, c)]).T assert np.allclose(self.expected_jacobian(a, b, c), res, atol=tol, rtol=0)
def test_differentiate_positional_multidim(self, tol): """Tests that all positional arguments are differentiated when they are multidimensional.""" def circuit(a, b): qml.RX(a[0], wires=0) qml.RX(a[1], wires=1) qml.RX(b[2, 1], wires=2) return qml.expval(qml.PauliZ(0)), qml.expval( qml.PauliZ(1)), qml.expval(qml.PauliZ(2)) dev = qml.device('default.qubit', wires=3) circuit = qml.QNode(circuit, dev) a = np.array([-np.sqrt(2), -0.54]) b = np.array([np.pi / 7] * 6).reshape([3, 2]) circuit_output = circuit(a, b) expected_output = np.cos(np.array([[a[0], a[1], b[-1, 0]]])) assert np.allclose(circuit_output, expected_output, atol=tol, rtol=0) # circuit jacobians circuit_jacobian = circuit.jacobian([a, b]) expected_jacobian = np.array([ [-np.sin(a[0])] + [0.] * 7, # expval 0 [0., -np.sin(a[1])] + [0.] * 6, # expval 1 [0.] * 2 + [0.] * 5 + [-np.sin(b[2, 1])] ]) # expval 2 assert np.allclose(circuit_jacobian, expected_jacobian, atol=tol, rtol=0)
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 = qml.QNode(circuit4, dev) a = 0.7418 b = -5. c = np.pi / 7 circuit_output = circuit4(a, b, c) expected_output = np.array([[np.cos(b), np.cos(c)]]) assert np.allclose(circuit_output, expected_output, atol=tol, rtol=0) # circuit jacobians circuit_jacobian = circuit4.jacobian([a, b, c]) expected_jacobian = np.array([[0., -np.sin(b), 0.], [0., 0., -np.sin(c)]]) assert np.allclose(circuit_jacobian, expected_jacobian, atol=tol, rtol=0)
def test_multiple_expectation_jacobian_array(self): "Tests that qnodes using an array argument return correct gradients for multiple expectation values." self.logTestName() a, b, c = 0.5, 0.54, 0.3 def circuit(weights): qml.QubitStateVector(np.array([1, 0, 1, 1]) / np.sqrt(3), wires=[0, 1]) qml.Rot(weights[0], weights[1], weights[2], wires=0) qml.CNOT(wires=[0, 1]) return qml.expval.PauliZ(0), qml.expval.PauliY(1) circuit = qml.QNode(circuit, self.dev2) res = circuit.jacobian([np.array([a, b, c])]) self.assertAllAlmostEqual(self.expected_jacobian(a, b, c), res, delta=self.tol) jac = qml.jacobian(circuit, 0) res = jac(np.array([a, b, c])) self.assertAllAlmostEqual(self.expected_jacobian(a, b, c), res, delta=self.tol)
def test_differentiate_positional_multidim(self): "Tests that all positional arguments are differentiated when they are multidimensional." self.logTestName() def circuit(a, b): qml.RX(a[0], 0) qml.RX(a[1], 1) qml.RX(b[2, 1], 2) return qml.expval.PauliZ(0), qml.expval.PauliZ( 1), qml.expval.PauliZ(2) circuit = qml.QNode(circuit, self.dev3) a = np.array([-np.sqrt(2), -0.54]) b = np.array([np.pi / 7] * 6).reshape([3, 2]) circuit_output = circuit(a, b) expected_output = np.cos(np.array([[a[0], a[1], b[-1, 0]]])) self.assertAllAlmostEqual(circuit_output, expected_output, delta=self.tol) # circuit jacobians circuit_jacobian = circuit.jacobian([a, b]) expected_jacobian = np.array([ [-np.sin(a[0])] + [0.] * 7, # expval 0 [0., -np.sin(a[1])] + [0.] * 6, # expval 1 [0.] * 2 + [0.] * 5 + [-np.sin(b[2, 1])] ]) # expval 2 self.assertAllAlmostEqual(circuit_jacobian, expected_jacobian, delta=self.tol)
def test_array_parameters_autograd(self, tol): """Test that gradients of array parameters give same results as positional arguments.""" a, b, c = 0.5, 0.54, 0.3 def ansatz(x, y, z): qml.QubitStateVector(np.array([1, 0, 1, 1]) / np.sqrt(3), wires=[0, 1]) qml.Rot(x, y, z, wires=0) qml.CNOT(wires=[0, 1]) return qml.expval(qml.PauliZ(0)) def circuit1(x, y, z): return ansatz(x, y, z) def circuit2(x, array): return ansatz(x, array[0], array[1]) def circuit3(array): return ansatz(*array) dev = qml.device('default.qubit', wires=2) circuit1 = qml.QNode(circuit1, dev) grad1 = qml.grad(circuit1, argnum=[0, 1, 2]) positional_grad = circuit1.jacobian([a, b, c]) positional_autograd = grad1(a, b, c) assert np.allclose(positional_grad, positional_autograd, atol=tol, rtol=0) circuit2 = qml.QNode(circuit2, dev) grad2 = qml.grad(circuit2, argnum=[0, 1]) circuit3 = qml.QNode(circuit3, dev) grad3 = qml.grad(circuit3, argnum=0) array_grad = circuit3.jacobian([np.array([a, b, c])]) array_autograd = grad3(np.array([a, b, c])) assert np.allclose(array_grad, array_autograd, atol=tol, rtol=0)
def test_cv_gradients_gaussian_circuit(self): """Tests that the gradients of circuits of gaussian gates match between the finite difference and analytic methods.""" self.logTestName() class PolyN(qml.ops.PolyXP): "Mimics NumberOperator using the arbitrary 2nd order observable interface. Results should be identical." def __init__(self, wires): hbar = 2 q = np.diag([-0.5, 0.5/hbar, 0.5/hbar]) super().__init__(q, wires=wires) self.name = 'PolyXP' gates = [] for name in qml.ops._cv__ops__: cls = getattr(qml.ops, name) if cls.supports_analytic: gates.append(cls) obs = [qml.ops.X, qml.ops.NumberOperator, PolyN] par = [0.4] for G in reversed(gates): log.debug('Testing gate %s...', G.__name__[0]) for O in obs: log.debug('Testing observable %s...', O.__name__[0]) def circuit(x): args = [0.3] * G.num_params args[0] = x qml.Displacement(0.5, 0, wires=0) G(*args, wires=range(G.num_wires)) qml.Beamsplitter(1.3, -2.3, wires=[0, 1]) qml.Displacement(-0.5, 0, wires=0) qml.Squeezing(0.5, -1.5, wires=0) qml.Rotation(-1.1, wires=0) return qml.expval(O(wires=0)) q = qml.QNode(circuit, self.gaussian_dev) val = q.evaluate(par) # log.info(' value:', val) grad_F = q.jacobian(par, method='F') grad_A2 = q.jacobian(par, method='A', force_order2=True) # log.info(' grad_F: ', grad_F) # log.info(' grad_A2: ', grad_A2) if O.ev_order == 1: grad_A = q.jacobian(par, method='A') # log.info(' grad_A: ', grad_A) # the different methods agree self.assertAllAlmostEqual(grad_A, grad_F, delta=self.tol) # analytic method works for every parameter self.assertTrue(q.grad_method_for_par == {0:'A'}) # the different methods agree self.assertAllAlmostEqual(grad_A2, grad_F, delta=self.tol)
def test_keywordargs_used(self): "Tests that qnodes use keyword arguments." self.logTestName() def circuit(w, x=None): qml.RX(x, [0]) return qml.expval.PauliZ(0) circuit = qml.QNode(circuit, self.dev1) c = circuit(1., x=np.pi) self.assertAlmostEqual(c, -1., delta=self.tol)
def test_keywordargs_used(self): "Tests that qnodes use keyword arguments." self.logTestName() def circuit(w, x=None): qml.RX(x, wires=[0]) return qml.expval(qml.PauliZ(0)) circuit = qml.QNode(circuit, self.dev1).to_torch() c = circuit(torch.tensor(1.), x=np.pi) self.assertAlmostEqual(c.numpy(), -1., delta=self.tol)
def test_array_parameters_autograd(self): "Test that gradients of array parameters give same results as positional arguments." self.logTestName() a, b, c = 0.5, 0.54, 0.3 def ansatz(x, y, z): qml.QubitStateVector(np.array([1, 0, 1, 1]) / np.sqrt(3), wires=[0, 1]) qml.Rot(x, y, z, wires=0) qml.CNOT(wires=[0, 1]) return qml.expval.PauliZ(0) def circuit1(x, y, z): return ansatz(x, y, z) def circuit2(x, array): return ansatz(x, array[0], array[1]) def circuit3(array): return ansatz(*array) circuit1 = qml.QNode(circuit1, self.dev2) grad1 = qml.grad(circuit1, argnum=[0, 1, 2]) positional_grad = circuit1.jacobian([a, b, c]) positional_autograd = grad1(a, b, c) self.assertAllAlmostEqual(positional_grad, positional_autograd, delta=self.tol) circuit2 = qml.QNode(circuit2, self.dev2) grad2 = qml.grad(circuit2, argnum=[0, 1]) circuit3 = qml.QNode(circuit3, self.dev2) grad3 = qml.grad(circuit3, argnum=0) array_grad = circuit3.jacobian([np.array([a, b, c])]) array_autograd = grad3(np.array([a, b, c])) self.assertAllAlmostEqual(array_grad, array_autograd, delta=self.tol)
def test_multiple_keywordargs_used(self): "Tests that qnodes use multiple keyword arguments." self.logTestName() def circuit(w, x=None, y=None): qml.RX(x, wires=[0]) qml.RX(y, wires=[1]) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)) circuit = qml.QNode(circuit, self.dev2).to_tfe() c = circuit(tf.constant(1.), x=np.pi, y=np.pi) self.assertAllAlmostEqual(c.numpy(), [-1., -1.], delta=self.tol)
def test_multidimensional_keywordargs_used(self): "Tests that qnodes use multi-dimensional keyword arguments." self.logTestName() def circuit(w, x=None): qml.RX(x[0], [0]) qml.RX(x[1], [1]) return qml.expval.PauliZ(0), qml.expval.PauliZ(1) circuit = qml.QNode(circuit, self.dev2) c = circuit(1., x=[np.pi, np.pi]) self.assertAllAlmostEqual(c, [-1., -1.], delta=self.tol)
def test_multidimensional_keywordargs_used(self): "Tests that qnodes use multi-dimensional keyword arguments." self.logTestName() def circuit(w, x=None): qml.RX(x[0], wires=[0]) qml.RX(x[1], wires=[1]) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)) circuit = qml.QNode(circuit, self.dev2).to_torch() c = circuit(torch.tensor(1.), x=[np.pi, np.pi]) self.assertAllAlmostEqual(c.numpy(), [-1., -1.], delta=self.tol)