def circuit_decomposed(features): qml.Displacement(features[0], 0.1, wires=0) qml.Displacement(features[1], 0.1, wires=1) qml.Displacement(features[2], 0.1, wires=2) qml.Beamsplitter(0.5, 0, wires=[2, 1]) qml.Beamsplitter(0.5, 0, wires=[1, 0]) return qml.expval(qml.X(0))
def layer(v): """ Single layer of the quantum neural net. Args: v (array[float]): array of variables for one layer """ # Matrix multiplication of input layer qml.Rotation(v[0], wires=0) qml.Squeezing(v[1], 0., wires=0) qml.Rotation(v[2], wires=0) # Bias qml.Displacement(v[3], 0., wires=0) # Element-wise nonlinear transformation qml.Kerr(v[4], wires=0) #================================================ qml.Rotation(v[5], wires=1) qml.Squeezing(v[6], 0., wires=1) qml.Rotation(v[7], wires=1) # Bias qml.Displacement(v[8], 0., wires=1) # Element-wise nonlinear transformation qml.Kerr(v[9], wires=1) qml.Beamsplitter(v[10],v[11], wires=[0,1]) qml.Beamsplitter(v[12],v[13], wires=[0,1])
def qfunc(a, b, c, d, e, f): qml.ThermalState(3, wires=[1]) qml.GaussianState(np.array([1, 1, 1, 2, 2, 3, 3, 3]), 2 * np.eye(8), wires=[0, 1, 2, 3]) qml.Rotation(a, wires=0) qml.Rotation(b, wires=1) qml.Beamsplitter(d, 1, wires=[0, 1]) qml.Beamsplitter(e, 1, wires=[1, 2]) qml.Displacement(f, 0, wires=[3]) qml.Squeezing(2.3, 0, wires=[0]) qml.Squeezing(2.3, 0, wires=[2]) qml.Beamsplitter(d, 1, wires=[1, 2]) qml.Beamsplitter(e, 1, wires=[2, 3]) qml.TwoModeSqueezing(2, 2, wires=[3, 1]) qml.ControlledPhase(2.3, wires=[2, 1]) qml.ControlledAddition(2, wires=[0, 3]) qml.QuadraticPhase(4, wires=[0]) return [ qml.expval(qml.ops.PolyXP(np.array([0, 1, 2]), wires=0)), qml.expval(qml.ops.QuadOperator(4, wires=1)), qml.expval( qml.ops.FockStateProjector(np.array([1, 5]), wires=[2, 3])), ]
def test_expval_and_variance(self, tol): """Test that the gradient works for a combination of CV expectation values and variances""" dev = qml.device("default.gaussian", wires=3) a, b = [0.54, -0.423] with qml.tape.JacobianTape() as tape: qml.Displacement(0.5, 0, wires=0) qml.Squeezing(a, 0, wires=0) qml.Squeezing(b, 0, wires=1) qml.Beamsplitter(0.6, -0.3, wires=[0, 1]) qml.Squeezing(-0.3, 0, wires=2) qml.Beamsplitter(1.4, 0.5, wires=[1, 2]) qml.var(qml.X(0)) qml.expval(qml.X(1)) qml.var(qml.X(2)) tape.trainable_params = {2, 4} # jacobians must match tapes, fn = qml.gradients.finite_diff(tape) grad_F = fn(dev.batch_execute(tapes)) tapes, fn = param_shift_cv(tape, dev) grad_A = fn(dev.batch_execute(tapes)) assert np.allclose(grad_A, grad_F, atol=tol, rtol=0)
def parameterized_cv_tape(): """A parametrized CV circuit.""" a, b, c, d, e, f = 0.1, 0.2, 0.3, 47 / 17, 0.5, 0.6 with qml.tape.QuantumTape() as tape: qml.ThermalState(3, wires=[1]) qml.GaussianState(2 * np.eye(8), np.array([1, 1, 1, 2, 2, 3, 3, 3]), wires=[0, 1, 2, 3]) qml.Rotation(a, wires=0) qml.Rotation(b, wires=1) qml.Beamsplitter(d, 1, wires=[0, 1]) qml.Beamsplitter(e, 1, wires=[1, 2]) qml.Displacement(f, 0, wires=[3]) qml.Squeezing(2.3, 0, wires=[0]) qml.Squeezing(2.3, 0, wires=[2]) qml.Beamsplitter(d, 1, wires=[1, 2]) qml.Beamsplitter(e, 1, wires=[2, 3]) qml.TwoModeSqueezing(2, 2, wires=[3, 1]) qml.ControlledPhase(2.3, wires=[2, 1]) qml.ControlledAddition(2, wires=[0, 3]) qml.QuadraticPhase(4, wires=[0]) qml.expval(qml.ops.PolyXP(np.array([0, 1, 2]), wires=0)) qml.expval(qml.ops.QuadOperator(4, wires=1)) qml.expval(qml.ops.FockStateProjector(np.array([1, 5]), wires=[2, 3])) return tape
def circuit(a, b): qml.Displacement(0.5, 0, wires=0) qml.Squeezing(a, 0, wires=0) qml.Squeezing(b, 0, wires=1) qml.Beamsplitter(0.6, -0.3, wires=[0, 1]) qml.Squeezing(-0.3, 0, wires=2) qml.Beamsplitter(1.4, 0.5, wires=[1, 2]) return qml.var(qml.X(0)), qml.expval(qml.X(1)), qml.var(qml.X(2))
def qfunc(): qml.GaussianState( 2 * np.eye(16), np.array([(2 * i + 2) // 2 for i in range(16)]), wires=list(range(8)) ) [qml.Beamsplitter(0.4, 0, wires=[2 * i, 2 * i + 1]) for i in range(4)] [qml.Beamsplitter(0.25475, 0.2312344, wires=[i, i + 4]) for i in range(4)] return [ qml.expval(qml.FockStateProjector(np.array([1, 1]), wires=[i, i + 4])) for i in range(4) ]
def wide_cv_tape(): """A wide unparametrized CV circuit.""" with qml.tape.QuantumTape() as tape: qml.GaussianState( 2 * np.eye(16), np.array([(2 * i + 2) // 2 for i in range(16)]), wires=list(range(8)) ) [qml.Beamsplitter(0.4, 0, wires=[2 * i, 2 * i + 1]) for i in range(4)] [qml.Beamsplitter(0.25475, 0.2312344, wires=[i, i + 4]) for i in range(4)] [qml.expval(qml.FockStateProjector(np.array([1, 1]), wires=[i, i + 4])) for i in range(4)] return tape
def test_non_differentiable(self): """Test that a non-differentiable parameter is correctly marked""" with qml.tape.JacobianTape() as tape: qml.FockState(1, wires=0) qml.Displacement(0.543, 0, wires=[1]) qml.Beamsplitter(0, 0, wires=[0, 1]) qml.expval(qml.X(wires=[0])) assert _grad_method(tape, 0) is None assert _grad_method(tape, 1) == "A" assert _grad_method(tape, 2) == "A" assert _grad_method(tape, 3) == "A" assert _grad_method(tape, 4) == "A" _gradient_analysis(tape) assert tape._par_info[0]["grad_method"] is None assert tape._par_info[1]["grad_method"] == "A" assert tape._par_info[2]["grad_method"] == "A" assert tape._par_info[3]["grad_method"] == "A" assert tape._par_info[4]["grad_method"] == "A" _gradient_analysis(tape)
def test_gradients_gaussian_circuit(self, op, obs, mocker, tol): """Tests that the gradients of circuits of gaussian gates match between the finite difference and analytic methods.""" tol = 1e-2 args = np.linspace(0.2, 0.5, op.num_params) with CVParamShiftTape() as tape: qml.Displacement(0.5, 0, wires=0) op(*args, wires=range(op.num_wires)) qml.Beamsplitter(1.3, -2.3, wires=[0, 1]) qml.Displacement(-0.5, 0.1, wires=0) qml.Squeezing(0.5, -1.5, wires=0) qml.Rotation(-1.1, wires=0) var(obs(wires=0)) dev = qml.device("default.gaussian", wires=2) res = tape.execute(dev) tape._update_gradient_info() tape.trainable_params = set(range(2, 2 + op.num_params)) # check that every parameter is analytic for i in range(op.num_params): assert tape._par_info[2 + i]["grad_method"][0] == "A" spy = mocker.spy(CVParamShiftTape, "parameter_shift_first_order") grad_F = tape.jacobian(dev, method="numeric") grad_A = tape.jacobian(dev, method="analytic") grad_A2 = tape.jacobian(dev, method="analytic", force_order2=True) assert np.allclose(grad_A2, grad_F, atol=tol, rtol=0) assert np.allclose(grad_A, grad_F, atol=tol, rtol=0)
def test_non_gaussian_operation(self): """Test that a non-Gaussian operation succeeding a differentiable Gaussian operation results in numeric differentiation.""" with qml.tape.JacobianTape() as tape: qml.Rotation(1.0, wires=[0]) qml.Rotation(1.0, wires=[1]) # Non-Gaussian qml.Kerr(1.0, wires=[1]) qml.expval(qml.P(0)) qml.expval(qml.X(1)) # First rotation gate has no succeeding non-Gaussian operation assert _grad_method(tape, 0) == "A" # Second rotation gate does no succeeding non-Gaussian operation assert _grad_method(tape, 1) == "F" # Kerr gate does not support the parameter-shift rule assert _grad_method(tape, 2) == "F" with qml.tape.JacobianTape() as tape: qml.Rotation(1.0, wires=[0]) qml.Rotation(1.0, wires=[1]) # entangle the modes qml.Beamsplitter(1.0, 0.0, wires=[0, 1]) # Non-Gaussian qml.Kerr(1.0, wires=[1]) qml.expval(qml.P(0)) qml.expval(qml.X(1)) # After entangling the modes, the Kerr gate now succeeds # both initial rotations assert _grad_method(tape, 0) == "F" assert _grad_method(tape, 1) == "F" assert _grad_method(tape, 2) == "F"
def test_beamsplitter_gradient(self, mocker, tol): """Test the gradient of the beamsplitter gate""" dev = qml.device("default.gaussian", wires=2, hbar=hbar) alpha = 0.5643 theta = 0.23354 with CVParamShiftTape() as tape: qml.Displacement(alpha, 0.0, wires=[0]) qml.Beamsplitter(theta, 0.0, wires=[0, 1]) expval(qml.X(0)) tape._update_gradient_info() tape.trainable_params = {2} spy1 = mocker.spy(CVParamShiftTape, "parameter_shift_first_order") spy2 = mocker.spy(CVParamShiftTape, "parameter_shift_second_order") grad_A = tape.jacobian(dev, method="analytic") spy1.assert_called() spy2.assert_not_called() grad_A2 = tape.jacobian(dev, method="analytic", force_order2=True) spy2.assert_called() expected = -hbar * alpha * np.sin(theta) assert np.allclose(grad_A, expected, atol=tol, rtol=0) assert np.allclose(grad_A2, expected, atol=tol, rtol=0)
def interferometer_inv(params, q): N = len(q) theta, phi, rphi = split_interferometer_params(params, N) if N == 1: # the interferometer is a single rotation qml.Rotation(-rphi[0], wires=q[0]) return # apply the final local phase shifts to all modes except the last one for i in range(max(1, N - 1)): qml.Rotation(-rphi[i], wires=q[i]) pairs = list() # Apply the rectangular beamsplitter array # The array depth is N for l in range(N): for k, (q1, q2) in enumerate(zip(q[:-1], q[1:])): # skip even or odd pairs depending on layer if (l + k) % 2 != 1: pairs.append((q1, q2)) for i in range(len(theta) - 1, -1, -1): qml.Beamsplitter(-theta[i], phi[i], wires=pairs[i])
def test_gradients_gaussian_circuit(self, op, obs, tol): """Tests that the gradients of circuits of gaussian gates match between the finite difference and analytic methods.""" tol = 1e-2 with qml.tape.JacobianTape() as tape: qml.Displacement(0.5, 0, wires=0) qml.apply(op) qml.Beamsplitter(1.3, -2.3, wires=[0, 1]) qml.Displacement(-0.5, 0.1, wires=0) qml.Squeezing(0.5, -1.5, wires=0) qml.Rotation(-1.1, wires=0) qml.expval(obs(wires=0)) dev = qml.device("default.gaussian", wires=2) res = tape.execute(dev) tape.trainable_params = set(range(2, 2 + op.num_params)) tapes, fn = qml.gradients.finite_diff(tape) grad_F = fn(dev.batch_execute(tapes)) tapes, fn = param_shift_cv(tape, dev, force_order2=True) grad_A2 = fn(dev.batch_execute(tapes)) # check that every parameter is analytic for i in range(op.num_params): assert tape._par_info[2 + i]["grad_method"][0] == "A" assert np.allclose(grad_A2, grad_F, atol=tol, rtol=0) if obs.ev_order == 1: tapes, fn = param_shift_cv(tape, dev) grad_A = fn(dev.batch_execute(tapes)) assert np.allclose(grad_A, grad_F, atol=tol, rtol=0)
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 test_beamsplitter_gradient(self, mocker, tol): """Test the gradient of the beamsplitter gate""" dev = qml.device("default.gaussian", wires=2, hbar=hbar) alpha = 0.5643 theta = 0.23354 with qml.tape.JacobianTape() as tape: qml.Displacement(alpha, 0.0, wires=[0]) qml.Beamsplitter(theta, 0.0, wires=[0, 1]) qml.expval(qml.X(0)) tape.trainable_params = {2} spy2 = mocker.spy(qml.gradients.parameter_shift_cv, "second_order_param_shift") tapes, fn = param_shift_cv(tape, dev) grad_A = fn(dev.batch_execute(tapes)) spy2.assert_not_called() tapes, fn = param_shift_cv(tape, dev, force_order2=True) grad_A2 = fn(dev.batch_execute(tapes)) spy2.assert_called() expected = -hbar * alpha * np.sin(theta) assert np.allclose(grad_A, expected, atol=tol, rtol=0) assert np.allclose(grad_A2, expected, atol=tol, rtol=0)
def qf(x, y): qml.Displacement(x, 0, wires=[0]) qml.Displacement(1.2, y, wires=[1]) qml.Beamsplitter(0.2, 1.7, wires=[0, 1]) qml.Rotation(1.9, wires=[0]) qml.Kerr(0.3, wires=[1]) # nongaussian succeeding both x and y due to the beamsplitter return qml.expval(qml.X(0)), qml.expval(qml.X(1))
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(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): qml.Displacement(0.5, 0, wires=0) make_gate(x) qml.Beamsplitter(1.3, -2.3, wires=[0, 1]) qml.Displacement(-0.5, 0.1, wires=0) qml.Squeezing(0.5, -1.5, wires=0) qml.Rotation(-1.1, wires=0) return qml.expval(O(wires=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.1, wires=0) qml.Squeezing(0.5, -1.5, wires=0) qml.Rotation(-1.1, wires=0) return qml.expval(O(wires=0))
def circuit_decomposed(*weights): # Interferometer (replace with operation once this template is refactored) qml.Beamsplitter(weights[0][0, 0], weights[1][0, 0], wires=[0, 1]) qml.Rotation(weights[2][0, 0], wires=0) qml.Rotation(weights[2][0, 1], wires=1) qml.Squeezing(weights[3][0, 0], weights[4][0, 0], wires=0) qml.Squeezing(weights[3][0, 1], weights[4][0, 1], wires=1) # Interferometer qml.Beamsplitter(weights[5][0, 0], weights[6][0, 0], wires=[0, 1]) qml.Rotation(weights[7][0, 0], wires=0) qml.Rotation(weights[7][0, 1], wires=1) qml.Displacement(weights[8][0, 0], weights[9][0, 0], wires=0) qml.Displacement(weights[8][0, 1], weights[9][0, 1], wires=1) qml.Kerr(weights[10][0, 0], wires=0) qml.Kerr(weights[10][0, 1], wires=1) return qml.expval(qml.X(0))
def variational_circuit(parameter): # Encode input x into quantum state qml.Displacement(1.0, 0.0, wires=0) qml.Displacement(0.5, 0.0, wires=1) # the main circuit qml.Beamsplitter(parameter, 0.0, wires=[0, 1]) qml.Rotation(0.1, wires=0) qml.Rotation(0.2, wires=1) return [qml.expval(qml.X(0)), qml.expval(qml.X(1))]
def layer(v): qml.Beamsplitter(v[0], v[1], wires=[0, 1]) qml.Beamsplitter(v[2], v[3], wires=[0, 2]) qml.Beamsplitter(v[4], v[5], wires=[0, 3]) qml.Beamsplitter(v[6], v[7], wires=[1, 2]) qml.Beamsplitter(v[8], v[9], wires=[1, 3]) qml.Beamsplitter(v[10], v[11], wires=[2, 3]) qml.Displacement(v[12], v[13], wires=0) qml.Displacement(v[14], v[15], wires=1) qml.Displacement(v[16], v[17], wires=2) qml.Displacement(v[18], v[19], wires=3) qml.Squeezing(v[20], v[21], wires=0) qml.Squeezing(v[22], v[23], wires=1) qml.Squeezing(v[24], v[25], wires=2) qml.Squeezing(v[26], v[27], wires=3) """qml.CubicPhase(v[28],wires=0) qml.CubicPhase(v[29],wires=1) qml.CubicPhase(v[30],wires=2) qml.CubicPhase(v[31],wires=3)""" qml.Kerr(v[28], wires=0) qml.Kerr(v[29], wires=1) qml.Kerr(v[30], wires=2) qml.Kerr(v[31], wires=3) """qml.Beamsplitter(v[32], v[33], wires=[0,1])
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.MeanPhoton(0)
def qfunc(a, b, c, d, e, f): qml.Rotation(a, wires=wires[0]), qml.Rotation(b, wires=wires[1]), qml.Rotation(c, wires=wires[2]), qml.Beamsplitter(d, 1, wires=[wires[0], wires[1]]) qml.Rotation(1, wires=wires[0]), qml.Rotation(e, wires=wires[1]), qml.Rotation(f, wires=wires[2]), return [ qml.expval(qml.ops.NumberOperator(wires=wires[0])), qml.expval(qml.ops.NumberOperator(wires=wires[1])), qml.expval(qml.ops.NumberOperator(wires=wires[2])), ]
def qfunc(a, b, c, d, e, f): qml.Rotation(a, wires=0), qml.Rotation(b, wires=1), qml.Rotation(c, wires=2), qml.Beamsplitter(d, 1, wires=[0, 1]) qml.Rotation(1, wires=0), qml.Rotation(e, wires=1), qml.Rotation(f, wires=2), return [ qml.expval(qml.ops.NumberOperator(wires=0)), qml.expval(qml.ops.NumberOperator(wires=1)), qml.expval(qml.ops.NumberOperator(wires=2)), ]
def test_expval_and_variance(self, tol): """Test that the gradient works for a combination of CV expectation values and variances""" dev = qml.device("default.gaussian", wires=3) a, b = [0.54, -0.423] with CVParamShiftTape() as tape: qml.Displacement(0.5, 0, wires=0) qml.Squeezing(a, 0, wires=0) qml.Squeezing(b, 0, wires=1) qml.Beamsplitter(0.6, -0.3, wires=[0, 1]) qml.Squeezing(-0.3, 0, wires=2) qml.Beamsplitter(1.4, 0.5, wires=[1, 2]) var(qml.X(0)) expval(qml.X(1)) var(qml.X(2)) tape.trainable_params = {2, 4} # jacobians must match grad_F = tape.jacobian(dev, method="numeric") grad_A = tape.jacobian(dev, method="analytic") assert np.allclose(grad_A, grad_F, 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_squeezed_gradient(self, mocker, tol): """Test the gradient of the squeezed gate. We also ensure that the gradient is correct even when an operation with no Heisenberg representation is a descendent.""" dev = qml.device("default.gaussian", wires=2, hbar=hbar) class Rotation(qml.operation.CVOperation): """Dummy operation that does not support heisenberg representation""" num_wires = 1 num_params = 1 par_domain = "R" grad_method = "A" alpha = 0.5643 r = 0.23354 with qml.tape.JacobianTape() as tape: qml.Displacement(alpha, 0.0, wires=[0]) qml.Squeezing(r, 0.0, wires=[0]) # The following two gates have no effect # on the circuit gradient and expectation value qml.Beamsplitter(0.0, 0.0, wires=[0, 1]) Rotation(0.543, wires=[1]) qml.expval(qml.X(0)) tape.trainable_params = {2} spy2 = mocker.spy(qml.gradients.parameter_shift_cv, "second_order_param_shift") tapes, fn = param_shift_cv(tape, dev) grad_A = fn(dev.batch_execute(tapes)) spy2.assert_not_called() tapes, fn = param_shift_cv(tape, dev, force_order2=True) grad_A2 = fn(dev.batch_execute(tapes)) spy2.assert_called() expected = -np.exp(-r) * hbar * alpha assert np.allclose(grad_A, expected, atol=tol, rtol=0) assert np.allclose(grad_A2, expected, atol=tol, rtol=0)