def test_variance(self): """If the variance of the observable is first order, then parameter-shift is supported. If the observable is second order, however, only finite-differences is supported.""" with qml.tape.JacobianTape() as tape: qml.Rotation(1.0, wires=[0]) qml.var(qml.P(0)) # first order assert _grad_method(tape, 0) == "A" with qml.tape.JacobianTape() as tape: qml.Rotation(1.0, wires=[0]) qml.var(qml.NumberOperator(0)) # second order assert _grad_method(tape, 0) == "F" with qml.tape.JacobianTape() as tape: qml.Rotation(1.0, wires=[0]) qml.Rotation(1.0, wires=[1]) qml.Beamsplitter(0.5, 0.0, wires=[0, 1]) qml.var(qml.NumberOperator(0)) # fourth order qml.expval(qml.NumberOperator(1)) assert _grad_method(tape, 0) == "F" assert _grad_method(tape, 1) == "F" assert _grad_method(tape, 2) == "F" assert _grad_method(tape, 3) == "F"
def circuit(x=None): qml.DisplacementEmbedding(features=x, wires=range(n_wires), method="phase", c=1.0) qml.Beamsplitter(np.pi / 2, 0, wires=[0, 1]) qml.DisplacementEmbedding(features=[0, 0], wires=range(n_wires), method="phase", c=1.0) return [ qml.expval(qml.NumberOperator(wires=0)), qml.expval(qml.NumberOperator(wires=1)), ]
def circuit(x=None): qml.templates.DisplacementEmbedding( features=x, wires=range(n_wires), method="amplitude", c=1.0 ) return [ qml.expval(qml.NumberOperator(wires=0)), qml.expval(qml.NumberOperator(wires=1)), ]
def circuit(x=None): SqueezingEmbedding(features=x, wires=range(n_wires), method='amplitude', c=1) return [ qml.expval(qml.NumberOperator(wires=0)), qml.expval(qml.NumberOperator(wires=1)) ]
def circuit(x=None): SqueezingEmbedding(features=x, wires=range(n_wires), method='phase', c=1) Beamsplitter(pi / 2, 0, wires=[0, 1]) SqueezingEmbedding(features=[0, 0], wires=range(n_wires), method='phase', c=1) return [ qml.expval(qml.NumberOperator(wires=0)), qml.expval(qml.NumberOperator(wires=1)) ]
def circuit(varphi, mesh=None): qml.Interferometer(theta=[0.21], phi=[0.53], varphi=varphi, mesh=mesh, wires=[0, 1]) return qml.expval(qml.NumberOperator(0))
def test_device_wire_expansion(self, tol): """Test that the transformation works correctly for the case where the transformation applies to more wires than the observable.""" # create a 3-mode symmetric transformation wires = qml.wires.Wires([0, "a", 2]) ndim = 1 + 2 * len(wires) Z = np.arange(ndim**2).reshape(ndim, ndim) Z = Z.T + Z obs = qml.NumberOperator(0) res = CVParamShiftTape._transform_observable(obs, Z, device_wires=wires) # The Heisenberg representation of the number operator # is (X^2 + P^2) / (2*hbar) - 1/2. We use the ordering # I, X0, Xa, X2, P0, Pa, P2. A = np.diag([-0.5, 0.25, 0.25, 0, 0, 0, 0]) expected = A @ Z + Z @ A assert isinstance(res, qml.PolyXP) assert res.wires == wires assert np.allclose(res.data[0], expected, atol=tol, rtol=0)
def circuit(varphi, bs=None): Interferometer(theta=[], phi=[], varphi=varphi, beamsplitter=bs, wires=0) return qml.expval(qml.NumberOperator(0))
def circuit(varphi, mesh): Interferometer(theta=None, phi=None, varphi=varphi, mesh=mesh, wires=0) return qml.expval(qml.NumberOperator(0))
def circuit(params): for i in range(num_wires): qml.Squeezing(params[i], 0, wires=i) return [ qml.expval(qml.NumberOperator(wires=i)) for i in range(num_wires) ]
def test_observable_with_no_eigvals(self): """An observable with no eigenvalues defined should cause the eigvals property on the associated measurement process to be None""" obs = qml.NumberOperator(wires=0) m = MeasurementProcess(Expectation, obs=obs) assert m.eigvals is None
def circuit(weights): qml.Squeezing(r, 0, wires=0) qml.Squeezing(r, 0, wires=1) qml.Squeezing(r, 0, wires=2) template(*weights, wires=[0, 1, 2]) fn(template, *weights, wires=[0, 1, 2]) return qml.expval(qml.NumberOperator(0))
def test_multiple_squeezing_gradient(self, mocker, tol): """Test that the gradient of a circuit with two squeeze gates is correct.""" dev = qml.device("default.gaussian", wires=2, hbar=hbar) r0, phi0, r1, phi1 = [0.4, -0.3, -0.7, 0.2] with qml.tape.JacobianTape() as tape: qml.Squeezing(r0, phi0, wires=[0]) qml.Squeezing(r1, phi1, wires=[0]) qml.expval(qml.NumberOperator(0)) # second order spy2 = mocker.spy(qml.gradients.parameter_shift_cv, "second_order_param_shift") tapes, fn = param_shift_cv(tape, dev, force_order2=True) grad_A2 = fn(dev.batch_execute(tapes)) spy2.assert_called() # check against the known analytic formula expected = np.zeros([4]) expected[0] = np.cosh(2 * r1) * np.sinh( 2 * r0) + np.cos(phi0 - phi1) * np.cosh(2 * r0) * np.sinh(2 * r1) expected[1] = -0.5 * np.sin(phi0 - phi1) * np.sinh(2 * r0) * np.sinh( 2 * r1) expected[2] = np.cos(phi0 - phi1) * np.cosh(2 * r1) * np.sinh( 2 * r0) + np.cosh(2 * r0) * np.sinh(2 * r1) expected[3] = 0.5 * np.sin(phi0 - phi1) * np.sinh(2 * r0) * np.sinh( 2 * r1) assert np.allclose(grad_A2, expected, atol=tol, rtol=0)
def test_multiple_squeezing_gradient(self, mocker, tol): """Test that the gradient of a circuit with two squeeze gates is correct.""" dev = qml.device("default.gaussian", wires=2, hbar=hbar) r0, phi0, r1, phi1 = [0.4, -0.3, -0.7, 0.2] with CVParamShiftTape() as tape: qml.Squeezing(r0, phi0, wires=[0]) qml.Squeezing(r1, phi1, wires=[0]) expval(qml.NumberOperator(0)) # second order tape._update_gradient_info() spy2 = mocker.spy(CVParamShiftTape, "parameter_shift_second_order") grad_A2 = tape.jacobian(dev, method="analytic", force_order2=True) spy2.assert_called() # check against the known analytic formula expected = np.zeros([4]) expected[0] = np.cosh(2 * r1) * np.sinh(2 * r0) + np.cos(phi0 - phi1) * np.cosh( 2 * r0 ) * np.sinh(2 * r1) expected[1] = -0.5 * np.sin(phi0 - phi1) * np.sinh(2 * r0) * np.sinh(2 * r1) expected[2] = np.cos(phi0 - phi1) * np.cosh(2 * r1) * np.sinh(2 * r0) + np.cosh( 2 * r0 ) * np.sinh(2 * r1) expected[3] = 0.5 * np.sin(phi0 - phi1) * np.sinh(2 * r0) * np.sinh(2 * r1) assert np.allclose(grad_A2, expected, atol=tol, rtol=0)
def test_no_poly_xp_support(self, mocker, monkeypatch, caplog): """Test that if a device does not support PolyXP and the second-order parameter-shift rule is required, we fallback to finite differences.""" dev = qml.device("default.gaussian", wires=1) monkeypatch.delitem(dev._observable_map, "PolyXP") with CVParamShiftTape() as tape: qml.Rotation(1.0, wires=[0]) expval(qml.NumberOperator(0)) tape.trainable_params = {0} assert tape.analytic_pd == tape.parameter_shift spy1 = mocker.spy(tape, "parameter_shift_first_order") spy2 = mocker.spy(tape, "parameter_shift_second_order") spy_transform = mocker.spy(qml.operation.CVOperation, "heisenberg_tr") spy_numeric = mocker.spy(tape, "numeric_pd") with pytest.warns(UserWarning, match="does not support the PolyXP observable"): tape.jacobian(dev, method="analytic") spy1.assert_not_called() spy2.assert_called() spy_transform.assert_not_called() spy_numeric.assert_called()
def circuit_tria(varphi): Interferometer(theta, phi, varphi, mesh='triangular', beamsplitter='clements', wires=wires) return [qml.expval(qml.NumberOperator(w)) for w in wires]
def qf(x, y): qml.Displacement(x, 0, wires=[0]) # followed by nongaussian observable qml.Beamsplitter(0.2, 1.7, wires=[0, 1]) qml.Displacement(y, 0, wires=[1]) # followed by order-2 observable return qml.expval(qml.FockStateProjector(np.array([2]), 0)), qml.expval( qml.NumberOperator(1))
def circuit(*args): args = prep_par(args, op) op(*args, wires=wires) if issubclass(op, qml.operation.CV): return qml.expval(qml.NumberOperator(0)) else: return qml.expval(qml.PauliZ(0))
def circuit(varphi, bs=None): qml.templates.Interferometer(theta=[0.21], phi=[0.53], varphi=varphi, beamsplitter=bs, mesh=mesh, wires=[0, 1]) return qml.expval(qml.NumberOperator(0))
def circuit(theta, phi, varphi): for w in wires: qml.Squeezing(sq[w][0], sq[w][1], wires=w) qml.Interferometer(theta=theta, phi=phi, varphi=varphi, wires=wires) return [qml.expval(qml.NumberOperator(w)) for w in wires]
def test_second_order_expectation(self): """Test that the expectation of a second-order observable forces the gradient method to use the second-order parameter-shift rule""" with qml.tape.JacobianTape() as tape: qml.Rotation(1.0, wires=[0]) qml.expval(qml.NumberOperator(0)) # second order assert _grad_method(tape, 0) == "A2"
def test_multiple_output_values(self, tol): """Tests correct output shape and evaluation for a tape with multiple outputs""" dev = qml.device("default.gaussian", wires=2) n = 0.543 a = -0.654 with JacobianTape() as tape: qml.ThermalState(n, wires=0) qml.Displacement(a, 0, wires=0) qml.expval(qml.NumberOperator(1)) qml.var(qml.NumberOperator(0)) tape.trainable_params = {0, 1} res = tape.jacobian(dev) assert res.shape == (2, 2) expected = np.array([[0, 0], [2 * a ** 2 + 2 * n + 1, 2 * a * (2 * n + 1)]]) assert np.allclose(res, expected, atol=tol, rtol=0)
def test_multiple_second_order_observables(self, mocker, tol): """Test that the gradient of a circuit with multiple second order observables is correct""" dev = qml.device("default.gaussian", wires=2, hbar=hbar) r = [0.4, -0.7, 0.1, 0.2] p = [0.1, 0.2, 0.3, 0.4] with qml.tape.JacobianTape() as tape: qml.Squeezing(r[0], p[0], wires=[0]) qml.Squeezing(r[1], p[1], wires=[0]) qml.Squeezing(r[2], p[2], wires=[1]) qml.Squeezing(r[3], p[3], wires=[1]) qml.expval(qml.NumberOperator(0)) # second order qml.expval(qml.NumberOperator(1)) # second order spy2 = mocker.spy(qml.gradients.parameter_shift_cv, "second_order_param_shift") tapes, fn = param_shift_cv(tape, dev) grad_A2 = fn(dev.batch_execute(tapes)) spy2.assert_called() # check against the known analytic formula def expected_grad(r, p): return np.array([ np.cosh(2 * r[1]) * np.sinh(2 * r[0]) + np.cos(p[0] - p[1]) * np.cosh(2 * r[0]) * np.sinh(2 * r[1]), -0.5 * np.sin(p[0] - p[1]) * np.sinh(2 * r[0]) * np.sinh(2 * r[1]), np.cos(p[0] - p[1]) * np.cosh(2 * r[1]) * np.sinh(2 * r[0]) + np.cosh(2 * r[0]) * np.sinh(2 * r[1]), 0.5 * np.sin(p[0] - p[1]) * np.sinh(2 * r[0]) * np.sinh(2 * r[1]), ]) expected = np.zeros([2, 8]) expected[0, :4] = expected_grad(r[:2], p[:2]) expected[1, 4:] = expected_grad(r[2:], p[2:]) assert np.allclose(grad_A2, expected, atol=tol, rtol=0)
def test_multiple_second_order_observables(self, mocker, tol): """Test that the gradient of a circuit with multiple second order observables is correct""" dev = qml.device("default.gaussian", wires=2, hbar=hbar) r = [0.4, -0.7, 0.1, 0.2] p = [0.1, 0.2, 0.3, 0.4] with CVParamShiftTape() as tape: qml.Squeezing(r[0], p[0], wires=[0]) qml.Squeezing(r[1], p[1], wires=[0]) qml.Squeezing(r[2], p[2], wires=[1]) qml.Squeezing(r[3], p[3], wires=[1]) qml.expval(qml.NumberOperator(0)) # second order qml.expval(qml.NumberOperator(1)) # second order tape._update_gradient_info() spy2 = mocker.spy(CVParamShiftTape, "parameter_shift_second_order") grad_A2 = tape.jacobian(dev, method="analytic", force_order2=True) spy2.assert_called() # check against the known analytic formula def expected_grad(r, p): return np.array([ np.cosh(2 * r[1]) * np.sinh(2 * r[0]) + np.cos(p[0] - p[1]) * np.cosh(2 * r[0]) * np.sinh(2 * r[1]), -0.5 * np.sin(p[0] - p[1]) * np.sinh(2 * r[0]) * np.sinh(2 * r[1]), np.cos(p[0] - p[1]) * np.cosh(2 * r[1]) * np.sinh(2 * r[0]) + np.cosh(2 * r[0]) * np.sinh(2 * r[1]), 0.5 * np.sin(p[0] - p[1]) * np.sinh(2 * r[0]) * np.sinh(2 * r[1]), ]) expected = np.zeros([2, 8]) expected[0, :4] = expected_grad(r[:2], p[:2]) expected[1, 4:] = expected_grad(r[2:], p[2:]) assert np.allclose(grad_A2, expected, atol=tol, rtol=0)
def quantum_neural_net(var, x): wires = list(range(len(x))) # Encode input x into a sequence of quantum fock states for i in wires: qml.FockState(x[i], wires=i) # "layer" subcircuits for i, v in enumerate(var): layer(v[:len(v) // 2], v[len(v) // 2:], wires) return [qml.expval(qml.NumberOperator(w)) for w in wires]
def test_error_analytic_second_order(self): """Test exception raised if attempting to use a second order observable to compute the variance derivative analytically""" dev = qml.device("default.gaussian", wires=1) with CVParamShiftTape() as tape: qml.Displacement(1.0, 0, wires=0) var(qml.NumberOperator(0)) tape.trainable_params = {0} with pytest.raises(ValueError, match=r"cannot be used with the argument\(s\) \{0\}"): tape.jacobian(dev, method="analytic")
def circuit(var): """Variational circuit. Args: var (array[float]): array containing the variables Returns: mean photon number of mode 0 """ qml.FockState(1, wires=0) qml.Beamsplitter(var[0], var[1], wires=[0, 1]) return qml.expval(qml.NumberOperator(0))
def test_second_order_transform(self, tol): """Test that a second order observable is transformed correctly""" # create a symmetric transformation Z = np.arange(3**2).reshape(3, 3) Z = Z.T + Z obs = qml.NumberOperator(0) res = _transform_observable(obs, Z, device_wires=[0]) # The Heisenberg representation of the number operator # is (X^2 + P^2) / (2*hbar) - 1/2 A = np.array([[-0.5, 0, 0], [0, 0.25, 0], [0, 0, 0.25]]) expected = A @ Z + Z @ A assert isinstance(res, qml.PolyXP) assert res.wires.labels == (0, ) assert np.allclose(res.data[0], expected, atol=tol, rtol=0)
def test_single_output_value(self, tol): """Tests correct execution and output shape for a CV tape with a single expval output""" dev = qml.device("default.gaussian", wires=2) x = 0.543 y = -0.654 with QuantumTape() as tape: qml.Displacement(x, 0, wires=[0]) qml.Squeezing(y, 0, wires=[1]) qml.Beamsplitter(np.pi / 4, 0, wires=[0, 1]) qml.expval(qml.NumberOperator(0)) assert tape.output_dim == 1 res = tape.execute(dev) assert res.shape == (1, )
def test_single_output_value(self, tol): """Tests correct Jacobian and output shape for a CV tape with a single output""" dev = qml.device("default.gaussian", wires=2) n = 0.543 a = -0.654 with JacobianTape() as tape: qml.ThermalState(n, wires=0) qml.Displacement(a, 0, wires=0) qml.var(qml.NumberOperator(0)) tape.trainable_params = {0, 1} res = tape.jacobian(dev) assert res.shape == (1, 2) expected = np.array([2 * a**2 + 2 * n + 1, 2 * a * (2 * n + 1)]) assert np.allclose(res, expected, atol=tol, rtol=0)