def test_two_mode_squeezing_heisenberg(self, phi, mag): """ops: Tests the Heisenberg representation of the Beamsplitter gate.""" r = mag matrix = cv.TwoModeSqueezing._heisenberg_rep([r, phi]) true_matrix = np.array([ [1, 0, 0, 0, 0], [ 0, np.cosh(r), 0, np.cos(phi) * np.sinh(r), np.sin(phi) * np.sinh(r) ], [ 0, 0, np.cosh(r), np.sin(phi) * np.sinh(r), -np.cos(phi) * np.sinh(r) ], [ 0, np.cos(phi) * np.sinh(r), np.sin(phi) * np.sinh(r), np.cosh(r), 0 ], [ 0, np.sin(phi) * np.sinh(r), -np.cos(phi) * np.sinh(r), 0, np.cosh(r) ], ]) assert np.allclose(matrix, true_matrix)
def test_finite_diff_squeezed(self, tol): """Test that the jacobian of the probability for a squeezed states is approximated well with finite differences""" cutoff = 5 dev = qml.device("strawberryfields.gaussian", wires=1, cutoff_dim=cutoff) @qml.qnode(dev) def circuit(r, phi): qml.Squeezing(r, phi, wires=0) return qml.probs(wires=[0]) r = 0.4 phi = -0.12 n = np.arange(cutoff) # differentiate with respect to parameter r res_F = circuit.jacobian([r, phi], wrt={0}, method="F").flatten() assert res_F.shape == (cutoff, ) expected_gradient = ( np.abs(np.tanh(r))**n * (1 + 2 * n - np.cosh(2 * r)) * fac(n) / (2**(n + 1) * np.cosh(r)**2 * np.sinh(r) * fac(n / 2)**2)) expected_gradient[n % 2 != 0] = 0 assert np.allclose(res_F, expected_gradient, atol=tol, rtol=0) # differentiate with respect to parameter phi res_F = circuit.jacobian([r, phi], wrt={1}, method="F").flat expected_gradient = 0 assert np.allclose(res_F, expected_gradient, atol=tol, rtol=0)
def test_two_mode_squeezing_heisenberg(self): """Tests the Heisenberg representation of the Beamsplitter gate.""" self.logTestName() for r in mags: for phi in phis: matrix = cv.TwoModeSqueezing._heisenberg_rep([r, phi]) true_matrix = np.array([[1, 0, 0, 0, 0], [ 0, np.cosh(r), 0, np.cos(phi) * np.sinh(r), np.sin(phi) * np.sinh(r) ], [ 0, 0, np.cosh(r), np.sin(phi) * np.sinh(r), -np.cos(phi) * np.sinh(r) ], [ 0, np.cos(phi) * np.sinh(r), np.sin(phi) * np.sinh(r), np.cosh(r), 0 ], [ 0, np.sin(phi) * np.sinh(r), -np.cos(phi) * np.sinh(r), 0, np.cosh(r) ]]) self.assertAllAlmostEqual(matrix, true_matrix, delta=self.tol)
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 squared_term(a, r, phi): """Analytic expression for <N^2>""" magnitude_squared = np.abs(a)**2 squared_term = ( -magnitude_squared + magnitude_squared**2 + 2 * magnitude_squared * np.cosh(2 * r) - np.exp(-1j * phi) * a**2 * np.cosh(r) * np.sinh(r) - np.exp(1j * phi) * np.conj(a)**2 * np.cosh(r) * np.sinh(r) + np.sinh(r)**4 + np.cosh(r) * np.sinh(r) * np.sinh(2 * r)) return squared_term
def test_jax(self, tol): """Tests that the output of the parameter-shift CV transform can be differentiated using JAX, yielding second derivatives.""" jax = pytest.importorskip("jax") from jax import numpy as jnp from jax.config import config config.update("jax_enable_x64", True) dev = qml.device("default.gaussian", wires=2) params = jnp.array([0.543, -0.654]) def cost_fn(x): with qml.tape.JacobianTape() as tape: qml.Squeezing(params[0], 0, wires=0) qml.Rotation(params[1], wires=0) qml.var(qml.X(wires=[0])) tape.trainable_params = {0, 2} tapes, fn = qml.gradients.param_shift_cv(tape, dev) jac = fn( qml.execute(tapes, dev, param_shift_cv, gradient_kwargs={"dev": dev}, interface="jax")) return jac r, phi = params res = cost_fn(params) expected = np.array([ 2 * np.exp(2 * r) * np.sin(phi)**2 - 2 * np.exp(-2 * r) * np.cos(phi)**2, 2 * np.sinh(2 * r) * np.sin(2 * phi), ]) assert np.allclose(res, expected, atol=tol, rtol=0) pytest.xfail( "The CV Operation methods have not been updated to support autodiff" ) res = jax.jacobian(cost_fn)(params) expected = np.array([ [ 4 * np.exp(-2 * r) * (np.cos(phi)**2 + np.exp(4 * r) * np.sin(phi)**2), 4 * np.cosh(2 * r) * np.sin(2 * phi), ], [ 4 * np.cosh(2 * r) * np.sin(2 * phi), 4 * np.cos(2 * phi) * np.sinh(2 * r) ], ]) assert np.allclose(res, expected, atol=tol, rtol=0)
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]), ])
def setUp(self): self.fnames = ['test_function_1', 'test_function_2', 'test_function_3'] self.univariate_funcs = [ np.sin, lambda x: np.exp(x / 10.), lambda x: x**2 ] self.grad_uni_fns = [ np.cos, lambda x: np.exp(x / 10.) / 10., lambda x: 2 * x ] self.multivariate_funcs = [ lambda x: np.sin(x[0]) + np.cos(x[1]), lambda x: np.exp(x[0] / 3) * np.tanh(x[1]), lambda x: np.sum(x_**2 for x_ in x) ] self.grad_multi_funcs = [ lambda x: np.array([np.cos(x[0]), -np.sin(x[1])]), lambda x: np.array([ np.exp(x[0] / 3) / 3 * np.tanh(x[1]), np.exp(x[0] / 3) * (1 - np.tanh(x[1])**2) ]), lambda x: np.array([2 * x_ for x_ in x]) ] self.mvar_mdim_funcs = [ lambda x: np.sin(x[0, 0]) + np.cos(x[1, 0]), lambda x: np.exp(x[0, 0] / 3) * np.tanh(x[1, 0]), lambda x: np.sum(x_[0]**2 for x_ in x) ] self.grad_mvar_mdim_funcs = [ lambda x: np.array([[np.cos(x[0, 0])], [-np.sin(x[[1]])]]), lambda x: np.array([[np.exp(x[0, 0] / 3) / 3 * np.tanh(x[ 1, 0])], [np.exp(x[0, 0] / 3) * (1 - np.tanh(x[1, 0])**2)]]), lambda x: np.array([[2 * x_[0]] for x_ in x]) ] self.margs_fns = [ lambda x, y: np.sin(x) + np.cos(y), lambda x, y: np.exp(x / 3) * np.tanh(y), lambda x, y: np.sum(x_**2 for x_ in [x, y]) ] self.grad_margs_funcs = [ lambda x, y: (np.cos(x), -np.sin(y)), lambda x, y: (np.exp(x / 3) / 3 * np.tanh(y), np.exp(x / 3) * (1 - np.tanh(y)**2)), lambda x, y: (2 * x, 2 * y) ] self.margs_mdim_fns = [ lambda x, y: (np.sin(x), np.cos(y)), lambda x, y: (np.exp(x / 3) * np.tanh(y), np.sinh(x * y)), lambda x, y: (x**2 + y**2, x * y) ] self.grad_margs_mdim_funcs = [ lambda x, y: np.diag([np.cos(x), -np.sin(y)]), lambda x, y: np.array([[ np.exp(x / 3) / 3 * np.tanh(y), np.exp(x / 3) * np.sech(y)**2 ], [np.cosh(x * y) * y, np.cosh(x * y) * x]]), lambda x, y: np.array([[2 * x, 2 * y], [y, x]]) ]
def test_squeezing_heisenberg(phi, mag): """ops: Tests the Heisenberg representation of the Squeezing gate.""" r = mag matrix = cv.Squeezing._heisenberg_rep([r, phi]) true_matrix = np.array([ [1, 0, 0], [0, np.cosh(r) - np.cos(phi) * np.sinh(r), -np.sin(phi) * np.sinh(r)], [0, -np.sin(phi) * np.sinh(r), np.cosh(r) + np.cos(phi) * np.sinh(r)], ]) assert np.allclose(matrix, true_matrix)
def test_fock_state(self): """Test that FockStateProjector works as expected""" self.logTestName() cutoff_dim = 12 a = 0.54321 r = 0.123 hbar = 2 dev = qml.device('strawberryfields.fock', wires=2, hbar=hbar, cutoff_dim=cutoff_dim) # test correct number state expectation |<n|a>|^2 @qml.qnode(dev) def circuit(x): qml.Displacement(x, 0, wires=0) return qml.expval(qml.FockStateProjector(np.array([2]), wires=0)) expected = np.abs(np.exp(-np.abs(a)**2 / 2) * a**2 / np.sqrt(2))**2 self.assertAlmostEqual(circuit(a), expected) # test correct number state expectation |<n|S(r)>|^2 @qml.qnode(dev) def circuit(x): qml.Squeezing(x, 0, wires=0) return qml.expval( qml.FockStateProjector(np.array([2, 0]), wires=[0, 1])) expected = np.abs( np.sqrt(2) / (2) * (-np.tanh(r)) / np.sqrt(np.cosh(r)))**2 self.assertAlmostEqual(circuit(r), expected)
def test_autograd_gradient(self, tol): """Tests that the output of the parameter-shift CV transform can be differentiated using autograd, yielding second derivatives.""" dev = qml.device("default.gaussian", wires=1) r = 0.12 phi = 0.105 def cost_fn(x): with qml.tape.JacobianTape() as tape: qml.Squeezing(x[0], 0, wires=0) qml.Rotation(x[1], wires=0) qml.var(qml.X(wires=[0])) tapes, fn = param_shift_cv(tape, dev) jac = fn( qml.execute(tapes, dev, param_shift_cv, gradient_kwargs={"dev": dev})) return jac[0, 2] params = np.array([r, phi], requires_grad=True) grad = qml.jacobian(cost_fn)(params) expected = np.array([ 4 * np.cosh(2 * r) * np.sin(2 * phi), 4 * np.cos(2 * phi) * np.sinh(2 * r) ]) assert np.allclose(grad, expected, atol=tol, rtol=0)
def test_controlled_addition(self): """Test the CX symplectic transform.""" self.logTestName() s = 0.543 S = controlled_addition(s) # test that S = B(theta+pi/2, 0) [S(z) x S(-z)] B(theta, 0) r = np.arcsinh(-s / 2) theta = 0.5 * np.arctan2(-1 / np.cosh(r), -np.tanh(r)) Sz = block_diag(squeezing(r, 0), squeezing(-r, 0))[:, [0, 2, 1, 3]][[0, 2, 1, 3]] expected = beamsplitter(theta + np.pi / 2, 0) @ Sz @ beamsplitter( theta, 0) self.assertAllAlmostEqual(S, expected, delta=self.tol) # test that S[x1, x2, p1, p2] -> [x1, x2+sx1, p1-sp2, p2] x1 = 0.5432 x2 = -0.453 p1 = 0.154 p2 = -0.123 out = S @ np.array([x1, x2, p1, p2]) * np.sqrt(2 * hbar) expected = np.array([x1, x2 + s * x1, p1 - s * p2, p2]) * np.sqrt( 2 * hbar) self.assertAllAlmostEqual(out, expected, delta=self.tol)
def test_two_mode_squeezing(self): """Test the two mode squeezing symplectic transform.""" self.logTestName() r = 0.543 phi = 0.123 S = two_mode_squeezing(r, phi) # test that S = B^\dagger(pi/4, 0) [S(z) x S(-z)] B(pi/4) B = beamsplitter(np.pi / 4, 0) Sz = block_diag(squeezing(r, phi), squeezing(-r, phi))[:, [0, 2, 1, 3]][[0, 2, 1, 3]] expected = B.conj().T @ Sz @ B self.assertAllAlmostEqual(S, expected, delta=self.tol) # test that S |a1, a2> = |ta1+ra2, ta2+ra1> a1 = 0.23 + 0.12j a2 = 0.23 + 0.12j out = S @ np.array([a1.real, a2.real, a1.imag, a2.imag]) * np.sqrt( 2 * hbar) T = np.cosh(r) R = np.exp(1j * phi) * np.sinh(r) a1out = T * a1 + R * np.conj(a2) a2out = T * a2 + R * np.conj(a1) expected = np.array([a1out.real, a2out.real, a1out.imag, a2out.imag ]) * np.sqrt(2 * hbar) self.assertAllAlmostEqual(out, expected, delta=self.tol)
def test_tf(self, tol): """Tests that the output of the parameter-shift CV transform can be executed using TF""" tf = pytest.importorskip("tensorflow") from pennylane.interfaces.tf import TFInterface dev = qml.device("default.gaussian", wires=1) params = tf.Variable([0.543, -0.654], dtype=tf.float64) with tf.GradientTape() as t: with TFInterface.apply(qml.tape.CVParamShiftTape()) as tape: qml.Squeezing(params[0], 0, wires=0) qml.Rotation(params[1], wires=0) qml.var(qml.X(wires=[0])) tapes, fn = qml.gradients.param_shift_cv(tape, dev) jac = fn([tp.execute(dev) for tp in tapes]) res = jac[0, 1] r, phi = 1.0 * params expected = np.array([ 2 * np.exp(2 * r) * np.sin(phi)**2 - 2 * np.exp(-2 * r) * np.cos(phi)**2, 2 * np.sinh(2 * r) * np.sin(2 * phi), ]) assert np.allclose(jac, expected, atol=tol, rtol=0) grad = t.jacobian(res, params) expected = np.array([ 4 * np.cosh(2 * r) * np.sin(2 * phi), 4 * np.cos(2 * phi) * np.sinh(2 * r) ]) assert np.allclose(grad, expected, atol=tol, rtol=0)
def test_number_state(self): """Test that NumberState works as expected""" self.logTestName() a = 0.54321 r = 0.123 hbar = 2 dev = qml.device('strawberryfields.gaussian', wires=2, hbar=hbar) # test correct number state expectation |<n|a>|^2 @qml.qnode(dev) def circuit(x): qml.Displacement(x, 0, 0) return qml.expval.NumberState(np.array([2]), wires=0) expected = np.abs(np.exp(-np.abs(a)**2 / 2) * a**2 / np.sqrt(2))**2 self.assertAlmostEqual(circuit(a), expected) # test correct number state expectation |<n|S(r)>|^2 @qml.qnode(dev) def circuit(x): qml.Squeezing(x, 0, 0) return qml.expval.NumberState(np.array([2, 0]), wires=[0, 1]) expected = np.abs( np.sqrt(2) / (2) * (-np.tanh(r)) / np.sqrt(np.cosh(r)))**2 self.assertAlmostEqual(circuit(r), expected)
def test_squeezed_number_state_gradient(self, mocker, tol): """Test the numerical gradient of the squeeze gate with with number state expectation is correct""" dev = qml.device("default.gaussian", wires=2, hbar=hbar) r = 0.23354 with qml.tape.JacobianTape() as tape: qml.Squeezing(r, 0.0, wires=[0]) # the fock state projector is a 'non-Gaussian' observable qml.expval(qml.FockStateProjector(np.array([2, 0]), wires=[0, 1])) tape.trainable_params = {0} spy = mocker.spy(qml.gradients.parameter_shift_cv, "second_order_param_shift") tapes, fn = param_shift_cv(tape, dev) grad = fn(dev.batch_execute(tapes)) assert tape._par_info[0]["grad_method"] == "F" spy.assert_not_called() # (d/dr) |<2|S(r)>|^2 = 0.5 tanh(r)^3 (2 csch(r)^2 - 1) sech(r) expected = 0.5 * np.tanh(r)**3 * (2 / (np.sinh(r)**2) - 1) / np.cosh(r) assert np.allclose(grad, expected, atol=tol, rtol=0)
def test_autograd_gradient(self, tol): """Tests that the output of the parameter-shift CV transform can be differentiated using autograd, yielding second derivatives.""" dev = qml.device("default.gaussian", wires=1) from pennylane.interfaces.autograd import AutogradInterface r = 0.12 phi = 0.105 def cost_fn(x): with AutogradInterface.apply(qml.tape.CVParamShiftTape()) as tape: qml.Squeezing(x[0], 0, wires=0) qml.Rotation(x[1], wires=0) qml.var(qml.X(wires=[0])) tapes, fn = param_shift_cv(tape, dev) return fn([t.execute(dev) for t in tapes])[0, 1] params = np.array([r, phi], requires_grad=True) grad = qml.jacobian(cost_fn)(params) expected = np.array([ 4 * np.cosh(2 * r) * np.sin(2 * phi), 4 * np.cos(2 * phi) * np.sinh(2 * r) ]) assert np.allclose(grad, expected, atol=tol, rtol=0)
def test_fock_state_projector(self, tol): """Test that FockStateProjector works as expected""" cutoff_dim = 12 a = 0.54321 r = 0.123 hbar = 2 dev = qml.device("strawberryfields.fock", wires=2, hbar=hbar, cutoff_dim=cutoff_dim) # test correct number state expectation |<n|a>|^2 @qml.qnode(dev) def circuit(x): qml.Displacement(x, 0, wires=0) return qml.expval(qml.FockStateProjector(np.array([2]), wires=0)) expected = np.abs(np.exp(-np.abs(a) ** 2 / 2) * a**2 / np.sqrt(2)) ** 2 assert np.allclose(circuit(a), expected, atol=tol, rtol=0) # test correct number state expectation |<n|S(r)>|^2 @qml.qnode(dev) def circuit(x): qml.Squeezing(x, 0, wires=0) return qml.expval(qml.FockStateProjector(np.array([2, 0]), wires=[0, 1])) expected = np.abs(np.sqrt(2) / (2) * (-np.tanh(r)) / np.sqrt(np.cosh(r))) ** 2 assert np.allclose(circuit(r), expected, atol=tol, rtol=0)
def test_finite_diff_squeezed(self, tol): """Test that the jacobian of the probability for a squeezed states is approximated well with finite differences""" cutoff = 5 dev = qml.device("strawberryfields.fock", wires=1, cutoff_dim=cutoff) @qml.qnode(dev) def circuit(r, phi): qml.Squeezing(r, phi, wires=0) return qml.probs(wires=[0]) r = 0.4 phi = -0.12 n = np.arange(cutoff) # construct tape circuit.construct([r, phi], {}) # differentiate with respect to parameter a circuit.qtape.trainable_params = {0} tapes, fn = qml.gradients.finite_diff(circuit.qtape) res_F = fn(dev.batch_execute(tapes)).flatten() assert res_F.shape == (cutoff,) expected_gradient = ( np.abs(np.tanh(r)) ** n * (1 + 2 * n - np.cosh(2 * r)) * fac(n) / (2 ** (n + 1) * np.cosh(r) ** 2 * np.sinh(r) * fac(n / 2) ** 2) ) expected_gradient[n % 2 != 0] = 0 assert np.allclose(res_F, expected_gradient, atol=tol, rtol=0) # re-construct tape to reset trainable_params circuit.construct([r, phi], {}) # differentiate with respect to parameter phi circuit.qtape.trainable_params = {1} tapes, fn = qml.gradients.finite_diff(circuit.qtape) res_F = fn(dev.batch_execute(tapes)).flatten() expected_gradient = 0 assert np.allclose(res_F, expected_gradient, atol=tol, rtol=0)
def pd_sr(rs0, phis0, rd0, phid0, rs1, phis1, rd1, phid1): """Analytic expression for the partial derivative with respect to the r argument of the first squeezing operation (rs0)""" return ( ( 0.25 + rd0 ** 2 * (-0.25 - 2 * rd1 ** 2 + 2 * rd1 ** 4) + (-(rd1 ** 2) + rd0 ** 2 * (-1 + 6 * rd1 ** 2)) * np.cosh(2 * rs1) + (-0.25 + 1.25 * rd0 ** 2) * np.cosh(4 * rs1) ) * np.sinh(2 * rs0) + ( -(rd1 ** 2) + rd1 ** 4 + (-0.5 + 2.5 * rd1 ** 2) * np.cosh(2 * rs1) + 0.5 * np.cosh(4 * rs1) ) * np.sinh(4 * rs0) + rd1 ** 2 * np.cos(2 * phid1 - phis1) * ((1 - 4 * rd0 ** 2) * np.sinh(2 * rs0) - 1.5 * np.sinh(4 * rs0)) * np.sinh(2 * rs1) + rd0 ** 2 * np.cos(2 * phid0 - phis0) * np.cosh(2 * rs0) * ( -0.25 + 2 * rd1 ** 2 - 2 * rd1 ** 4 + (1 - 4 * rd1 ** 2) * np.cosh(2 * rs1) - 0.75 * np.cosh(4 * rs1) + 2 * rd1 ** 2 * np.cos(2 * phid1 - phis1) * np.sinh(2 * rs1) ) )
def pd_dr(rs0, phis0, rd0, phid0, rs1, phis1, rd1, phid1): """Analytic expression for the partial derivative with respect to the r argument of the first displacement operation (rd0)""" return rd0 * ( 0.5 - rd0 ** 2 + (-2 + 4 * rd0 ** 2) * rd1 ** 2 * np.cosh(2 * rs1) + (-0.5 + rd0 ** 2) * np.cosh(4 * rs1) + (2 - 4 * rd0 ** 2) * rd1 ** 2 * np.cos(2 * phid1 - phis1) * np.sinh(2 * rs1) + np.cosh(2 * rs0) * ( -0.25 - 2 * rd1 ** 2 + 2 * rd1 ** 4 + (-1 + 6 * rd1 ** 2) * np.cosh(2 * rs1) + 1.25 * np.cosh(4 * rs1) - 4 * rd1 ** 2 * np.cos(2 * phid1 - phis1) * np.sinh(2 * rs1) ) + np.cos(2 * phid0 - phis0) * np.sinh(2 * rs0) * ( -0.25 + 2 * rd1 ** 2 - 2 * rd1 ** 4 + (1 - 4 * rd1 ** 2) * np.cosh(2 * rs1) - 0.75 * np.cosh(4 * rs1) + 2 * rd1 ** 2 * np.cos(2 * phid1 - phis1) * np.sinh(2 * rs1) ) )
def test_expectation(self): """Test that expectation values are calculated correctly""" self.logTestName() dev = qml.device('default.gaussian', wires=1, hbar=hbar) # test correct mean and variance for <n> of a displaced thermal state nbar = 0.5431 alpha = 0.324 - 0.59j dev.apply('ThermalState', wires=[0], par=[nbar]) dev.apply('Displacement', wires=[0], par=[alpha, 0]) mean = dev.expval('MeanPhoton', [0], []) self.assertAlmostEqual(mean, np.abs(alpha)**2 + nbar, delta=self.tol) # self.assertAlmostEqual(var, nbar**2+nbar+np.abs(alpha)**2*(1+2*nbar), delta=self.tol) # test correct mean and variance for Homodyne P measurement alpha = 0.324 - 0.59j dev.apply('CoherentState', wires=[0], par=[alpha]) mean = dev.expval('P', [0], []) self.assertAlmostEqual(mean, alpha.imag * np.sqrt(2 * hbar), delta=self.tol) # self.assertAlmostEqual(var, hbar/2, delta=self.tol) # test correct mean and variance for Homodyne measurement mean = dev.expval('Homodyne', [0], [np.pi / 2]) self.assertAlmostEqual(mean, alpha.imag * np.sqrt(2 * hbar), delta=self.tol) # self.assertAlmostEqual(var, hbar/2, delta=self.tol) # test correct mean and variance for number state expectation |<n|alpha>|^2 # on a coherent state for n in range(3): mean = dev.expval('NumberState', [0], [np.array([n])]) expected = np.abs( np.exp(-np.abs(alpha)**2 / 2) * alpha**n / np.sqrt(fac(n)))**2 self.assertAlmostEqual(mean, expected, delta=self.tol) # test correct mean and variance for number state expectation |<n|S(r)>|^2 # on a squeezed state n = 1 r = 0.4523 dev.apply('SqueezedState', wires=[0], par=[r, 0]) mean = dev.expval('NumberState', [0], [np.array([2 * n])]) expected = np.abs( np.sqrt(fac(2 * n)) / (2**n * fac(n)) * (-np.tanh(r))**n / np.sqrt(np.cosh(r)))**2 self.assertAlmostEqual(mean, expected, delta=self.tol)
def test_variance_squeezed_numberstate(self): """test correct variance for number state expectation |<n|S(r)>|^2 on a squeezed state """ self.logTestName() dev = qml.device('default.gaussian', wires=1, hbar=hbar) n = 1 r = 0.4523 dev.apply('SqueezedState', wires=[0], par=[r, 0]) var = dev.var('FockStateProjector', [0], [np.array([2 * n])]) mean = np.abs( np.sqrt(fac(2 * n)) / (2**n * fac(n)) * (-np.tanh(r))**n / np.sqrt(np.cosh(r)))**2 self.assertAlmostEqual(var, mean * (1 - mean), delta=self.tol)
def test_torch(self, tol): """Tests that the output of the parameter-shift CV transform can be executed using Torch.""" torch = pytest.importorskip("torch") dev = qml.device("default.gaussian", wires=1) params = torch.tensor([0.543, -0.654], dtype=torch.float64, requires_grad=True) with qml.tape.JacobianTape() as tape: qml.Squeezing(params[0], 0, wires=0) qml.Rotation(params[1], wires=0) qml.var(qml.X(wires=[0])) tape.trainable_params = {0, 2} tapes, fn = qml.gradients.param_shift_cv(tape, dev) jac = fn( qml.execute(tapes, dev, param_shift_cv, gradient_kwargs={"dev": dev}, interface="torch")) r, phi = params.detach().numpy() expected = np.array([ 2 * np.exp(2 * r) * np.sin(phi)**2 - 2 * np.exp(-2 * r) * np.cos(phi)**2, 2 * np.sinh(2 * r) * np.sin(2 * phi), ]) assert np.allclose(jac.detach().numpy(), expected, atol=tol, rtol=0) cost = jac[0, 1] cost.backward() hess = params.grad expected = np.array([ 4 * np.cosh(2 * r) * np.sin(2 * phi), 4 * np.cos(2 * phi) * np.sinh(2 * r) ]) assert np.allclose(hess.detach().numpy(), expected, atol=0.1, rtol=0)
def test_tf(self, tol): """Tests that the output of the parameter-shift CV transform can be executed using TF""" tf = pytest.importorskip("tensorflow") dev = qml.device("default.gaussian", wires=1) params = tf.Variable([0.543, -0.654], dtype=tf.float64) with tf.GradientTape() as t: with qml.tape.JacobianTape() as tape: qml.Squeezing(params[0], 0, wires=0) qml.Rotation(params[1], wires=0) qml.var(qml.X(wires=[0])) tape.trainable_params = {0, 2} tapes, fn = param_shift_cv(tape, dev) jac = fn( qml.execute(tapes, dev, param_shift_cv, gradient_kwargs={"dev": dev}, interface="tf")) res = jac[0, 1] r, phi = 1.0 * params expected = np.array([ 2 * np.exp(2 * r) * np.sin(phi)**2 - 2 * np.exp(-2 * r) * np.cos(phi)**2, 2 * np.sinh(2 * r) * np.sin(2 * phi), ]) assert np.allclose(jac, expected, atol=tol, rtol=0) grad = t.jacobian(res, params) expected = np.array([ 4 * np.cosh(2 * r) * np.sin(2 * phi), 4 * np.cos(2 * phi) * np.sinh(2 * r) ]) assert np.allclose(grad, expected, atol=tol, rtol=0)
def test_torch(self, tol): """Tests that the output of the parameter-shift CV transform can be executed using Torch.""" torch = pytest.importorskip("torch") from pennylane.interfaces.torch import TorchInterface dev = qml.device("default.gaussian", wires=1) params = torch.tensor([0.543, -0.654], dtype=torch.float64, requires_grad=True) with TorchInterface.apply(qml.tape.CVParamShiftTape()) as tape: qml.Squeezing(params[0], 0, wires=0) qml.Rotation(params[1], wires=0) qml.var(qml.X(wires=[0])) tapes, fn = qml.gradients.param_shift_cv(tape, dev) jac = fn([t.execute(dev) for t in tapes]) r, phi = params.detach().numpy() expected = np.array([ 2 * np.exp(2 * r) * np.sin(phi)**2 - 2 * np.exp(-2 * r) * np.cos(phi)**2, 2 * np.sinh(2 * r) * np.sin(2 * phi), ]) assert np.allclose(jac.detach().numpy(), expected, atol=tol, rtol=0) cost = jac[0, 1] cost.backward() hess = params.grad expected = np.array([ 4 * np.cosh(2 * r) * np.sin(2 * phi), 4 * np.cos(2 * phi) * np.sinh(2 * r) ]) assert np.allclose(hess.detach().numpy(), expected, atol=0.1, rtol=0)