def test_merge_rotations_autograd(self): """Test QNode and gradient in autograd interface.""" original_qnode = qml.QNode(qfunc, dev) transformed_qnode = qml.QNode(transformed_qfunc, dev) input = np.array([0.1, 0.2, 0.3, 0.4], requires_grad=True) # Check that the numerical output is the same assert qml.math.allclose(original_qnode(input), transformed_qnode(input)) # Check that the gradient is the same assert qml.math.allclose( qml.grad(original_qnode)(input), qml.grad(transformed_qnode)(input)) # Check operation list ops = transformed_qnode.qtape.operations compare_operation_lists(ops, expected_op_list, expected_wires_list)
def test_controlled_rotation_second_derivative(self, G, tol): """Test that the controlled rotation gates return the correct second derivative if first decomposed.""" dev = qml.device("default.qubit", wires=2) init_state = qml.numpy.array([1.0, -1.0], requires_grad=False) / np.sqrt(2) @qml.qnode(dev) def circuit(b): qml.QubitStateVector(init_state, wires=0) G(b, wires=[0, 1]) return qml.expval(qml.PauliX(0)) b = 0.123 res = circuit(b) assert np.allclose(res, -np.cos(b / 2), atol=tol, rtol=0) grad = qml.grad(qml.grad(circuit))(b) expected = np.cos(b / 2) / 4 assert np.allclose(grad, expected, atol=tol, rtol=0)
def test_non_differentiable_gradient(self): """Test gradient computation with requires_grad=False raises an error""" def cost(x): return np.sum(np.sin(x)) grad_fn = qml.grad(cost, argnum=[0]) arr1 = np.array([0.0, 1.0, 2.0], requires_grad=False) with pytest.raises(np.NonDifferentiableError, match="non-differentiable"): grad_fn(arr1)
def test_tracker(self): """Tests the device tracker with batch execution.""" dev = qml.device('qiskit.aer', shots=100, wires=3) @qml.qnode(dev, diff_method="parameter-shift") def circuit(x): qml.RX(x, wires=0) return qml.expval(qml.PauliZ(0)) x = tensor(0.1, requires_grad=True) with qml.Tracker(dev) as tracker: qml.grad(circuit)(x) expected = {'executions': [1, 1, 1], 'shots': [100, 100, 100], 'batches': [1, 1], 'batch_len': [1, 2]} assert tracker.history == expected
def test_non_scalar_cost_gradient(self): """Test gradient computation with a non-scalar cost function raises an error""" def cost(x): return np.sin(x) grad_fn = qml.grad(cost, argnum=[0]) arr1 = np.array([0.0, 1.0, 2.0], requires_grad=True) with pytest.raises(TypeError, match="only applies to real scalar-output functions"): grad_fn(arr1)
def test_integration(): """Test that the execution of lightning.qubit is possible and agrees with default.qubit""" wires = 2 layers = 2 dev_l = qml.device("lightning.qubit", wires=wires) dev_d = qml.device("default.qubit", wires=wires) def circuit(weights): qml.templates.StronglyEntanglingLayers(weights, wires=range(wires)) return qml.expval(qml.PauliZ(0)) np.random.seed(1967) weights = np.random.random( qml.templates.StronglyEntanglingLayers.shape(layers, wires)) qn_l = qml.QNode(circuit, dev_l) qn_d = qml.QNode(circuit, dev_d) assert np.allclose(qn_l(weights), qn_d(weights)) assert np.allclose(qml.grad(qn_l)(weights), qml.grad(qn_d)(weights))
def test_gradient_autograd(self, mocker): """Test that caching works when calculating the gradient method using the autograd interface""" qnode = get_qnode(caching=10, interface="autograd") d_qnode = qml.grad(qnode) args = [0.1, 0.2] d_qnode(*args) spy = mocker.spy(DefaultQubitAutograd, "execute") d_qnode(*args) spy.assert_not_called()
def test_sin(self, tol): """Tests gradients with multivariate sin and cosine.""" multi_var = lambda x: np.sin(x[0]) + np.cos(x[1]) grad_multi_var = lambda x: np.array([np.cos(x[0]), -np.sin(x[1])]) x_vec = [1.5, -2.5] g = qml.grad(multi_var, 0) auto_grad = g(x_vec) correct_grad = grad_multi_var(x_vec) assert np.allclose(auto_grad, correct_grad, atol=tol, rtol=0)
def test_hamiltonian_dif_autograd(self): """Tests that the hamiltonian_expand tape transform is differentiable with the Autograd interface""" H = qml.Hamiltonian( [-0.2, 0.5, 1], [qml.PauliX(1), qml.PauliZ(1) @ qml.PauliY(2), qml.PauliZ(0)] ) var = [ np.array(0.1), np.array(0.67), np.array(0.3), np.array(0.4), np.array(-0.5), np.array(0.7), np.array(0.4), np.array(-0.5), np.array(0.7), ] output = 0.42294409781940356 output2 = [ 9.68883500e-02, -2.90832724e-01, -1.04448033e-01, -1.94289029e-09, 3.50307411e-01, -3.41123470e-01, 0.0, # these three are the Hamiltonian parameters 0.0, 0.0, ] with qml.tape.JacobianTape() as tape: for i in range(2): qml.RX(np.array(0), wires=0) qml.RX(np.array(0), wires=1) qml.RX(np.array(0), wires=2) qml.CNOT(wires=[0, 1]) qml.CNOT(wires=[1, 2]) qml.CNOT(wires=[2, 0]) qml.expval(H) AutogradInterface.apply(tape) def cost(x): tape.set_parameters(x, trainable_only=False) tapes, fn = qml.transforms.hamiltonian_expand(tape) res = [t.execute(dev) for t in tapes] return fn(res) assert np.isclose(cost(var), output) grad = qml.grad(cost)(var) for g, o in zip(grad, output2): assert np.allclose(g, o)
def test_agrees_with_autograd(self, tol): """Test that the grad function agrees with autograd""" def cost(x): return np.sum(np.sin(x) * x[0]**3) grad_fn = qml.grad(cost) params = np.array([0.5, 1.0, 2.0], requires_grad=True) res = grad_fn(params) expected = autograd.grad(cost)(params) assert np.allclose(res, expected, atol=tol, rtol=0)
def test_autodifferentiation(): """Test that autodifferentiation is preserved when writing a cost function that uses TensorBox method chaining""" x = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]) cost_fn = lambda a: (qml.proc.TensorBox(a).T**2).unbox()[0, 1] grad_fn = qml.grad(cost_fn) res = grad_fn(x)[0] expected = np.array([[0.0, 0.0, 0.0], [8.0, 0.0, 0.0]]) assert np.all(res == expected)
def test_hermitian_expectation(self, device, tol): """Test that arbitrary multi-mode Hermitian expectation values are correct""" n_wires = 2 dev = device(n_wires) dev_def = qml.device("default.qubit", wires=n_wires) if dev.shots is not None: pytest.skip("Device is in non-analytical mode.") if "Hermitian" not in dev.observables: pytest.skip("Device does not support the Hermitian observable.") if dev.name == dev_def.name: pytest.skip("Device is default.qubit.") theta = 0.432 phi = 0.123 A_ = np.array( [ [-6, 2 + 1j, -3, -5 + 2j], [2 - 1j, 0, 2 - 1j, -5 + 4j], [-3, 2 + 1j, 0, -4 + 3j], [-5 - 2j, -5 - 4j, -4 - 3j, -6], ] ) A_.requires_grad = False def circuit(theta, phi): qml.RX(theta, wires=[0]) qml.RX(phi, wires=[1]) qml.CNOT(wires=[0, 1]) return qml.expval(qml.Hermitian(A_, wires=[0, 1])) qnode_def = qml.QNode(circuit, dev_def) qnode = qml.QNode(circuit, dev) grad_def = qml.grad(qnode_def, argnum=[0, 1]) grad = qml.grad(qnode, argnum=[0, 1]) assert np.allclose(qnode(theta, phi), qnode_def(theta, phi), atol=tol(dev.shots)) assert np.allclose(grad(theta, phi), grad_def(theta, phi), atol=tol(dev.shots))
def test_parameter_shift_hessian(self, params, tol): """Tests that the output of the parameter-shift transform can be differentiated using autograd, yielding second derivatives.""" dev = qml.device("default.qubit.autograd", wires=2) params = np.array([0.543, -0.654], requires_grad=True) def cost_fn(x): with qml.tape.JacobianTape() as tape1: qml.RX(x[0], wires=[0]) qml.RY(x[1], wires=[1]) qml.CNOT(wires=[0, 1]) qml.var(qml.PauliZ(0) @ qml.PauliX(1)) with qml.tape.JacobianTape() as tape2: qml.RX(x[0], wires=0) qml.RY(x[0], wires=1) qml.CNOT(wires=[0, 1]) qml.probs(wires=1) result = execute([tape1, tape2], dev, gradient_fn=param_shift) return result[0] + result[1][0, 0] res = cost_fn(params) x, y = params expected = 0.5 * (3 + np.cos(x)**2 * np.cos(2 * y)) assert np.allclose(res, expected, atol=tol, rtol=0) res = qml.grad(cost_fn)(params) expected = np.array([ -np.cos(x) * np.cos(2 * y) * np.sin(x), -np.cos(x)**2 * np.sin(2 * y) ]) assert np.allclose(res, expected, atol=tol, rtol=0) res = qml.jacobian(qml.grad(cost_fn))(params) expected = np.array([ [-np.cos(2 * x) * np.cos(2 * y), np.sin(2 * x) * np.sin(2 * y)], [np.sin(2 * x) * np.sin(2 * y), -2 * np.cos(x)**2 * np.cos(2 * y)], ]) assert np.allclose(res, expected, atol=tol, rtol=0)
def test_differentiable_autograd(self, diff_method): """Test that a batch transform is differentiable when using autograd""" dev = qml.device("default.qubit", wires=2) qnode = qml.QNode(self.circuit, dev, interface="autograd", diff_method=diff_method) def cost(x, weights): return self.my_transform(qnode, weights)(x) weights = np.array([0.1, 0.2], requires_grad=True) x = np.array(0.543, requires_grad=True) res = cost(x, weights) assert np.allclose(res, self.expval(x, weights)) grad = qml.grad(cost)(x, weights) expected = qml.grad(self.expval)(x, weights) assert all(np.allclose(g, e) for g, e in zip(grad, expected))
def test_integration_qubit_grad(self, template, diffable, nondiffable, n_wires, interface, to_var): """Tests that gradient calculations of qubit templates execute without error.""" if template.__name__ in ["AmplitudeEmbedding"]: pytest.skip("Template cannot be differentiated") # Extract keys and items keys_diffable = [*diffable] diffable = list(diffable.values()) # Turn into correct format diffable = [to_var(i) for i in diffable] # Make qnode dev = qml.device('default.qubit', wires=n_wires) @qml.qnode(dev, interface=interface) def circuit(*diffable): # Turn diffables back into dictionaries dict = {key: item for key, item in zip(keys_diffable, diffable)} # Merge diffables and nondiffables dict.update(nondiffable) # Circuit template(**dict) return qml.expval(qml.Identity(0)) # Do gradient check for every differentiable argument for argnum in range(len(diffable)): # Check gradients in numpy interface if interface == 'autograd': grd = qml.grad(circuit, argnum=[argnum]) grd(*diffable) # Check gradients in torch interface if interface == 'torch': for i in range(len(diffable)): if i != argnum: diffable[i].requires_grad = False diffable[argnum].requires_grad = True res = circuit(*diffable) res.backward() diffable[argnum].grad.numpy() # Check gradients in tf interface if interface == 'tf': with tf.GradientTape() as tape: loss = circuit(*diffable) tape.gradient(loss, diffable[argnum])
def test_no_trainable_params_deprecation_grad(self, recwarn): """Tests that no deprecation warning arises when using qml.grad with qml.math.is_independent. """ def lin(x): return pnp.sum(x) x = pnp.array([0.2, 9.1, -3.2], requires_grad=True) jac = qml.grad(lin) assert qml.math.is_independent(jac, "autograd", (x, ), {}) assert len(recwarn) == 0
def test_decomposition_integration(self, angle, tol): """Test that the decompositon of PauliRot yields the same results.""" dev = qml.device("default.qubit", wires=2) @qml.qnode(dev) def circuit(theta): qml.PauliRot(theta, "XX", wires=[0, 1]) return qml.expval(qml.PauliZ(0)) @qml.qnode(dev) def decomp_circuit(theta): qml.PauliRot.decomposition(theta, "XX", wires=[0, 1]) return qml.expval(qml.PauliZ(0)) assert circuit(angle) == pytest.approx(decomp_circuit(angle), abs=tol) assert np.squeeze(qml.grad(circuit)(angle)) == pytest.approx( np.squeeze(qml.grad(decomp_circuit)(angle)), abs=tol )
def test__tape_qchem(tol): """The circit Ansatz with a QChem Hamiltonian produces correct results""" H, qubits = qml.qchem.molecular_hamiltonian( ["H", "H"], np.array([0.0, 0.1, 0.0, 0.0, -0.1, 0.0])) def circuit(params): circuit_ansatz(params, wires=range(4)) return qml.expval(H) params = np.arange(30) * 0.111 dev_lq = qml.device("lightning.qubit", wires=4) dev_dq = qml.device("default.qubit", wires=4) circuit_lq = qml.QNode(circuit, dev_lq, diff_method="adjoint") circuit_dq = qml.QNode(circuit, dev_lq, diff_method="parameter-shift") assert np.allclose( qml.grad(circuit_lq)(params), qml.grad(circuit_dq)(params), tol)
def test_linear(self, tol): """Tests gradients with multivariate multidimensional linear func.""" x_vec = np.random.uniform(-5, 5, size=(2)) x_vec_multidim = np.expand_dims(x_vec, axis=1) gradf = lambda x: np.array([[2 * x_[0]] for x_ in x]) f = lambda x: np.sum([x_[0]**2 for x_ in x]) g = qml.grad(f, 0) auto_grad = g(x_vec_multidim) correct_grad = gradf(x_vec_multidim) assert np.allclose(auto_grad, correct_grad, atol=tol, rtol=0)
def test_gradient_autograd(self, mocker): """Test that caching works when calculating the gradient using the autograd interface""" dev = qml.device("default.qubit", wires=2, cache=10) qn = QNode(qfunc, dev, interface="autograd") d_qnode = qml.grad(qn) args = [0.1, 0.2] d_qnode(*args) spy = mocker.spy(DefaultQubit, "apply") d_qnode(*args) spy.assert_not_called()
def test_hessian_vector_valued_postprocessing(self, dev_name, diff_method, mocker, tol): """Test hessian calculation of a vector valued QNode with post-processing""" if diff_method not in {"parameter-shift", "backprop"}: pytest.skip("Test only supports parameter-shift or backprop") dev = qml.device(dev_name, wires=1) @qnode(dev, diff_method=diff_method, interface="autograd") def circuit(x): qml.RX(x[0], wires=0) qml.RY(x[1], wires=0) return [qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(0))] def cost_fn(x): return x @ circuit(x) x = np.array([0.76, -0.87], requires_grad=True) res = cost_fn(x) a, b = x expected_res = x @ [np.cos(a) * np.cos(b), np.cos(a) * np.cos(b)] assert np.allclose(res, expected_res, atol=tol, rtol=0) grad_fn = qml.grad(cost_fn) g = grad_fn(x) expected_g = [ np.cos(b) * (np.cos(a) - (a + b) * np.sin(a)), np.cos(a) * (np.cos(b) - (a + b) * np.sin(b)), ] assert np.allclose(g, expected_g, atol=tol, rtol=0) spy = mocker.spy(JacobianTape, "hessian") hess = qml.jacobian(grad_fn)(x) if diff_method == "backprop": spy.assert_not_called() elif diff_method == "parameter-shift": spy.assert_called_once() expected_hess = [ [ -(np.cos(b) * ((a + b) * np.cos(a) + 2 * np.sin(a))), -(np.cos(b) * np.sin(a)) + (-np.cos(a) + (a + b) * np.sin(a)) * np.sin(b), ], [ -(np.cos(b) * np.sin(a)) + (-np.cos(a) + (a + b) * np.sin(a)) * np.sin(b), -(np.cos(a) * ((a + b) * np.cos(b) + 2 * np.sin(b))), ], ] assert np.allclose(hess, expected_hess, atol=tol, rtol=0)
def test_autograd(self, tol): """Tests the autograd interface.""" features = pnp.array([1.0, 1.0, 1.0], requires_grad=True) dev = qml.device("default.qubit", wires=3) circuit = qml.QNode(circuit_template, dev) circuit2 = qml.QNode(circuit_decomposed, dev) res = circuit(features) res2 = circuit2(features) assert qml.math.allclose(res, res2, atol=tol, rtol=0) grad_fn = qml.grad(circuit) grads = grad_fn(features) grad_fn2 = qml.grad(circuit2) grads2 = grad_fn2(features) assert np.allclose(grads[0], grads2[0], atol=tol, rtol=0)
def test_sin(self, tol): """Tests gradients with multivariate multidimensional sin and cos.""" x_vec = np.random.uniform(-5, 5, size=(2)) x_vec_multidim = np.expand_dims(x_vec, axis=1) gradf = lambda x: np.array([[np.cos(x[0, 0])], [-np.sin(x[[1]])]], dtype=np.float64) f = lambda x: np.sin(x[0, 0]) + np.cos(x[1, 0]) g = qml.grad(f, 0) auto_grad = g(x_vec_multidim) correct_grad = gradf(x_vec_multidim) assert np.allclose(auto_grad, correct_grad, atol=tol, rtol=0)
def test_gradient(self): """Test gradient computations continue to work""" def cost(x): return np.sum(np.sin(x)) grad_fn = qml.grad(cost, argnum=[0]) arr1 = np.array([0., 1., 2.]) res = grad_fn(arr1) expected = np.cos(arr1) assert np.all(res == expected)
def test_autograd(self, tol): """Tests the autograd interface.""" weights = pnp.array(np.random.random(size=(2, )), requires_grad=True) dev = qml.device("default.qubit", wires=4) circuit = qml.QNode(circuit_template, dev) circuit2 = qml.QNode(circuit_decomposed, dev) res = circuit(weights) res2 = circuit2(weights) assert qml.math.allclose(res, res2, atol=tol, rtol=0) grad_fn = qml.grad(circuit) grads = grad_fn(weights) grad_fn2 = qml.grad(circuit2) grads2 = grad_fn2(weights) assert np.allclose(grads, grads2, atol=tol, rtol=0)
def test_pauliz_expectation_analytic(self, device, tol): """Test that the tensor product of PauliZ expectation value is correct""" n_wires = 2 dev = device(n_wires) dev_def = qml.device("default.qubit", wires=n_wires) if dev.name == dev_def.name: pytest.skip("Device is default.qubit.") supports_tensor = ("supports_tensor_observables" in dev.capabilities() and dev.capabilities()["supports_tensor_observables"]) if not supports_tensor: pytest.skip("Device does not support tensor observables.") if not dev.analytic: pytest.skip("Device is in non-analytical mode.") theta = 0.432 phi = 0.123 def circuit(theta, phi): qml.RX(theta, wires=[0]) qml.RX(phi, wires=[1]) qml.CNOT(wires=[0, 1]) return qml.expval(qml.PauliZ(wires=0) @ qml.PauliZ(wires=1)) qnode_def = qml.QNode(circuit, dev_def) qnode = qml.QNode(circuit, dev) grad_def = qml.grad(qnode_def, argnum=[0, 1]) grad = qml.grad(qnode, argnum=[0, 1]) assert np.allclose(qnode(theta, phi), qnode_def(theta, phi), atol=tol(dev.analytic)) assert np.allclose(grad(theta, phi), grad_def(theta, phi), atol=tol(dev.analytic))
def test_random_circuit(self, device, tol, ret): """Test that the expectation value of a random circuit is correct""" n_wires = 2 dev = device(n_wires) dev_def = qml.device("default.qubit", wires=n_wires) if dev.name == dev_def.name: pytest.skip("Device is default.qubit.") supports_tensor = ("supports_tensor_observables" in dev.capabilities() and dev.capabilities()["supports_tensor_observables"]) if not supports_tensor: pytest.skip("Device does not support tensor observables.") if not dev.analytic: pytest.skip("Device is in non-analytical mode.") n_layers = np.random.randint(1, 5) weights = 2 * np.pi * np.random.rand(n_layers, 1) ret_type = getattr(qml, ret) def circuit(weights): RandomLayers(weights, wires=range(n_wires)) return ret_type(qml.PauliZ(wires=0) @ qml.PauliX(wires=1)) qnode_def = qml.QNode(circuit, dev_def) qnode = qml.QNode(circuit, dev) grad_def = qml.grad(qnode_def, argnum=0) grad = qml.grad(qnode, argnum=0) assert np.allclose(qnode(weights), qnode_def(weights), atol=tol(dev.analytic)) assert np.allclose(grad(weights), grad_def(weights), atol=tol(dev.analytic))
def test_autograd_conversion(self, qnode, tol): """Tests that the to_autograd() function ignores QNodes that already have the autograd interface.""" converted_qnode = to_autograd(qnode) assert converted_qnode is qnode x = 0.4 res = converted_qnode(x) assert np.allclose(res, np.cos(x), atol=tol, rtol=0) grad_fn = qml.grad(converted_qnode) res = grad_fn(x) assert np.allclose(res, -np.sin(x), atol=tol, rtol=0)
def test_grad_zero_hamiltonian(self, tol): """Tests the VQE gradient for a "zero" Hamiltonian.""" dev = qml.device("default.qubit", wires=4) H = qml.Hamiltonian([0], [qml.PauliX(0)]) w = pnp.array(PARAMS, requires_grad=True) @qml.qnode(dev, diff_method="parameter-shift") def circuit(w): qml.templates.StronglyEntanglingLayers(w, wires=range(4)) return qml.expval(H) dc = qml.grad(circuit)(w) assert np.allclose(dc, 0, atol=tol)
def test_grad_autograd(self, diff_method, tol): """Tests the VQE gradient in the autograd interface.""" dev = qml.device("default.qubit", wires=4) H = big_hamiltonian w = pnp.array(PARAMS, requires_grad=True) @qml.qnode(dev, diff_method=diff_method) def circuit(w): qml.templates.StronglyEntanglingLayers(w, wires=range(4)) return qml.expval(H) dc = qml.grad(circuit)(w) assert np.allclose(dc, big_hamiltonian_grad, atol=tol)