def test_measurement_expansion(self): """Test that measurement expansion works as expected""" with QuantumTape() as tape: # expands into 2 PauliX qml.BasisState(np.array([1, 1]), wires=[0, "a"]) qml.CNOT(wires=[0, "a"]) qml.RY(0.2, wires="a") probs(wires=0) # expands into RY on wire b expval(qml.PauliZ("a") @ qml.Hadamard("b")) # expands into QubitUnitary on wire 0 var(qml.Hermitian(np.array([[1, 2], [2, 4]]), wires=[0])) new_tape = tape.expand(expand_measurements=True) assert len(new_tape.operations) == 6 expected = [ qml.operation.Probability, qml.operation.Expectation, qml.operation.Variance ] assert [ m.return_type is r for m, r in zip(new_tape.measurements, expected) ] expected = [None, None, None] assert [m.obs is r for m, r in zip(new_tape.measurements, expected)] expected = [None, [1, -1, -1, 1], [0, 5]] assert [ m.eigvals is r for m, r in zip(new_tape.measurements, expected) ]
def test_ragged_differentiation(self, tol): """Tests correct output shape and evaluation for a tape with prob and expval outputs""" dev = qml.device("default.qubit", wires=2) x = tf.Variable(0.543, dtype=tf.float64) y = tf.Variable(-0.654, dtype=tf.float64) with tf.GradientTape() as tape: with TFInterface.apply(QuantumTape()) as qtape: qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) qml.CNOT(wires=[0, 1]) expval(qml.PauliZ(0)) probs(wires=[1]) res = qtape.execute(dev) expected = np.array([ tf.cos(x), (1 + tf.cos(x) * tf.cos(y)) / 2, (1 - tf.cos(x) * tf.cos(y)) / 2 ]) assert np.allclose(res, expected, atol=tol, rtol=0) res = tape.jacobian(res, [x, y]) expected = np.array([ [ -tf.sin(x), -tf.sin(x) * tf.cos(y) / 2, tf.cos(y) * tf.sin(x) / 2 ], [0, -tf.cos(x) * tf.sin(y) / 2, tf.cos(x) * tf.sin(y) / 2], ]) assert np.allclose(res, expected, atol=tol, rtol=0)
def test_ragged_differentiation(self, tol): """Tests correct output shape and evaluation for a tape with prob and expval outputs""" dev = qml.device("default.qubit", wires=2) x_val = 0.543 y_val = -0.654 x = torch.tensor(x_val, requires_grad=True) y = torch.tensor(y_val, requires_grad=True) with TorchInterface.apply(QuantumTape()) as tape: qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) qml.CNOT(wires=[0, 1]) expval(qml.PauliZ(0)) probs(wires=[1]) res = tape.execute(dev) expected = np.array([ np.cos(x_val), (1 + np.cos(x_val) * np.cos(y_val)) / 2, (1 - np.cos(x_val) * np.cos(y_val)) / 2, ]) assert np.allclose(res.detach().numpy(), expected, atol=tol, rtol=0) loss = torch.sum(res) loss.backward() expected = np.array([ -np.sin(x_val) + -np.sin(x_val) * np.cos(y_val) / 2 + np.cos(y_val) * np.sin(x_val) / 2, -np.cos(x_val) * np.sin(y_val) / 2 + np.cos(x_val) * np.sin(y_val) / 2, ]) assert np.allclose(x.grad, expected[0], atol=tol, rtol=0) assert np.allclose(y.grad, expected[1], atol=tol, rtol=0)
def test_inverse(self): """Test that inversion works as expected""" init_state = np.array([1, 1]) p = [0.1, 0.2, 0.3, 0.4] with QuantumTape() as tape: prep = qml.BasisState(init_state, wires=[0, "a"]) ops = [ qml.RX(p[0], wires=0), qml.Rot(*p[1:], wires=0).inv(), qml.CNOT(wires=[0, "a"]) ] m1 = probs(wires=0) m2 = probs(wires="a") tape.inv() # check that operation order is reversed assert tape.operations == [prep] + ops[::-1] # check that operations are inverted assert ops[0].inverse assert not ops[1].inverse assert ops[2].inverse # check that parameter order has reversed print(tape.get_parameters()) print([init_state, p[1], p[2], p[3], p[0]]) assert tape.get_parameters() == [init_state, p[1], p[2], p[3], p[0]]
def test_parameter_transforms(self): """Test that inversion correctly changes trainable parameters""" init_state = np.array([1, 1]) p = [0.1, 0.2, 0.3, 0.4] with QuantumTape() as tape: prep = qml.BasisState(init_state, wires=[0, "a"]) ops = [ qml.RX(p[0], wires=0), qml.Rot(*p[1:], wires=0).inv(), qml.CNOT(wires=[0, "a"]) ] m1 = probs(wires=0) m2 = probs(wires="a") tape.trainable_params = {1, 2} tape.inv() # check that operation order is reversed assert tape.trainable_params == {1, 4} assert tape.get_parameters() == [p[1], p[0]] # undo the inverse tape.inv() assert tape.trainable_params == {1, 2} assert tape.get_parameters() == [p[0], p[1]] assert tape._ops == ops
def cost(x, y, device): with QuantumTape() as tape: qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) qml.CNOT(wires=[0, 1]) expval(qml.PauliZ(0)) probs(wires=[1]) return tape.execute(device)
def cost(x, y, device): with AutogradInterface.apply(QuantumTape()) as tape: qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) qml.CNOT(wires=[0, 1]) expval(qml.PauliZ(0)) probs(wires=[1]) return tape.execute(device)
def test_probability(self): """Probability is the expectation value of a higher order observable, and thus only supports numerical differentiation""" with CVParamShiftTape() as tape: qml.Rotation(0.543, wires=[0]) qml.Squeezing(0.543, 0, wires=[0]) probs(wires=0) assert tape._grad_method(0) == "F" assert tape._grad_method(1) == "F" assert tape._grad_method(2) == "F"
def test_stopping_criterion(self): """Test that gates specified in the stop_at argument are not expanded.""" with QuantumTape() as tape: qml.U3(0, 1, 2, wires=0) qml.Rot(3, 4, 5, wires=0) probs(wires=0), probs(wires="a") new_tape = tape.expand(stop_at=lambda obj: obj.name in ["Rot"]) assert len(new_tape.operations) == 4 assert "Rot" in [i.name for i in new_tape.operations] assert not "U3" in [i.name for i in new_tape.operations]
def make_tape(self): params = [0.432, 0.123, 0.546, 0.32, 0.76] with QuantumTape() as tape: qml.RX(params[0], wires=0) qml.Rot(*params[1:4], wires=0) qml.CNOT(wires=[0, "a"]) qml.RX(params[4], wires=4) expval(qml.PauliX(wires="a")) probs(wires=[0, "a"]) return tape, params
def test_probs_exception(self): """Tests that an exception is raised when probability is used with the ReversibleTape.""" # TODO: remove this test when this support is added dev = qml.device("default.qubit", wires=2) with ReversibleTape() as tape: qml.PauliX(wires=0) qml.RX(0.542, wires=0) probs(wires=[0, 1]) with pytest.raises(ValueError, match="Probability is not supported"): tape.jacobian(dev)
def test_nesting_and_decomposition(self): """Test an example that contains nested tapes and operation decompositions.""" with QuantumTape() as tape: qml.BasisState(np.array([1, 1]), wires=[0, "a"]) with QuantumTape() as tape2: qml.Rot(0.543, 0.1, 0.4, wires=0) qml.CNOT(wires=[0, "a"]) qml.RY(0.2, wires="a") probs(wires=0), probs(wires="a") new_tape = tape.expand() assert len(new_tape.operations) == 5
def test_multiple_contexts(self): """Test multiple contexts with a single tape.""" ops = [] obs = [] with QuantumTape() as tape: ops += [qml.RX(0.432, wires=0)] a = qml.Rot(0.543, 0, 0.23, wires=1) b = qml.CNOT(wires=[2, "a"]) with tape: ops += [qml.RX(0.133, wires=0)] obs += [qml.PauliX(wires="a")] expval(obs[0]) obs += [probs(wires=[0, "a"])] assert len(tape.queue) == 5 assert tape.operations == ops assert tape.observables == obs assert tape.output_dim == 5 assert a not in tape.operations assert b not in tape.operations assert tape.wires == qml.wires.Wires([0, "a"])
def test_finite_diff(self, monkeypatch): """If an op has grad_method=F, this should be respected by the QubitParamShiftTape""" monkeypatch.setattr(qml.RX, "grad_method", "F") psi = np.array([1, 0, 1, 0]) / np.sqrt(2) with QubitParamShiftTape() as tape: qml.QubitStateVector(psi, wires=[0, 1]) qml.RX(0.543, wires=[0]) qml.RY(-0.654, wires=[1]) qml.CNOT(wires=[0, 1]) probs(wires=[0, 1]) assert tape._grad_method(0) is None assert tape._grad_method(1) == "F" assert tape._grad_method(2) == "A"
def test_depth_expansion(self): """Test expanding with depth=2""" with QuantumTape() as tape: # Will be decomposed into PauliX(0), PauliX(0) # Each PauliX will then be decomposed into PhaseShift, RX, PhaseShift. qml.BasisState(np.array([1, 1]), wires=[0, "a"]) with QuantumTape() as tape2: # will be decomposed into a RZ, RY, RZ qml.Rot(0.543, 0.1, 0.4, wires=0) qml.CNOT(wires=[0, "a"]) qml.RY(0.2, wires="a") probs(wires=0), probs(wires="a") new_tape = tape.expand(depth=2) assert len(new_tape.operations) == 11
def test_ragged_differentiation(self, monkeypatch, tol): """Tests correct output shape and evaluation for a tape with prob and expval outputs""" dev = qml.device("default.qubit.tf", wires=2) x = tf.Variable(0.543, dtype=tf.float64) y = tf.Variable(-0.654, dtype=tf.float64) def _asarray(args, dtype=tf.float64): res = [tf.reshape(i, [-1]) for i in args] res = tf.concat(res, axis=0) return tf.cast(res, dtype=dtype) # The current DefaultQubitTF device provides an _asarray method that does # not work correctly for ragged arrays. For ragged arrays, we would like _asarray to # flatten the array. Here, we patch the _asarray method on the device to achieve this # behaviour; once the tape has moved from the beta folder, we should implement # this change directly in the device. monkeypatch.setattr(dev, "_asarray", _asarray) with tf.GradientTape() as tape: with QuantumTape() as qtape: qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) qml.CNOT(wires=[0, 1]) expval(qml.PauliZ(0)) probs(wires=[1]) res = qtape.execute(dev) expected = np.array([ tf.cos(x), (1 + tf.cos(x) * tf.cos(y)) / 2, (1 - tf.cos(x) * tf.cos(y)) / 2 ]) assert np.allclose(res, expected, atol=tol, rtol=0) res = tape.jacobian(res, [x, y]) expected = np.array([ [ -tf.sin(x), -tf.sin(x) * tf.cos(y) / 2, tf.cos(y) * tf.sin(x) / 2 ], [0, -tf.cos(x) * tf.sin(y) / 2, tf.cos(x) * tf.sin(y) / 2], ]) assert np.allclose(res, expected, atol=tol, rtol=0)
def test_stopping_criterion_with_depth(self): """Test that gates specified in the stop_at argument are not expanded.""" with QuantumTape() as tape: # Will be decomposed into PauliX(0), PauliX(0) qml.BasisState(np.array([1, 1]), wires=[0, "a"]) with QuantumTape() as tape2: # will be decomposed into a RZ, RY, RZ qml.Rot(0.543, 0.1, 0.4, wires=0) qml.CNOT(wires=[0, "a"]) qml.RY(0.2, wires="a") probs(wires=0), probs(wires="a") new_tape = tape.expand(depth=2, stop_at=lambda obj: obj.name in ["PauliX"]) assert len(new_tape.operations) == 7
def test_non_differentiable(self): """Test that a non-differentiable parameter is correctly marked""" psi = np.array([1, 0, 1, 0]) / np.sqrt(2) with QubitParamShiftTape() as tape: qml.QubitStateVector(psi, wires=[0, 1]) qml.RX(0.543, wires=[0]) qml.RY(-0.654, wires=[1]) qml.CNOT(wires=[0, 1]) probs(wires=[0, 1]) assert tape._grad_method(0) is None assert tape._grad_method(1) == "A" assert tape._grad_method(2) == "A" tape._update_gradient_info() 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"
def test_probability_differentiation(self, tol): """Tests correct output shape and evaluation for a tape with multiple prob outputs""" dev = qml.device("default.qubit.tf", wires=2) x = tf.Variable(0.543, dtype=tf.float64) y = tf.Variable(-0.654, dtype=tf.float64) with tf.GradientTape() as tape: with QuantumTape() as qtape: qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) qml.CNOT(wires=[0, 1]) probs(wires=[0]) probs(wires=[1]) res = qtape.execute(dev) expected = np.array([ [tf.cos(x / 2)**2, tf.sin(x / 2)**2], [(1 + tf.cos(x) * tf.cos(y)) / 2, (1 - tf.cos(x) * tf.cos(y)) / 2], ]) assert np.allclose(res, expected, atol=tol, rtol=0) res = tape.jacobian(res, [x, y]) expected = np.array([ [ [-tf.sin(x) / 2, tf.sin(x) / 2], [-tf.sin(x) * tf.cos(y) / 2, tf.cos(y) * tf.sin(x) / 2], ], [ [0, 0], [-tf.cos(x) * tf.sin(y) / 2, tf.cos(x) * tf.sin(y) / 2], ], ]) assert np.allclose(res, expected, atol=tol, rtol=0)
def test_prob_expectation_values(self, tol): """Tests correct output shape and evaluation for a tape with prob and expval outputs""" dev = qml.device("default.qubit", wires=2) x = 0.543 y = -0.654 with QubitParamShiftTape() as tape: qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) qml.CNOT(wires=[0, 1]) expval(qml.PauliZ(0)) probs(wires=[0, 1]) res = tape.jacobian(dev, method="analytic") assert res.shape == (5, 2) expected = (np.array([ [-2 * np.sin(x), 0], [ -(np.cos(y / 2)**2 * np.sin(x)), -(np.cos(x / 2)**2 * np.sin(y)), ], [ -(np.sin(x) * np.sin(y / 2)**2), (np.cos(x / 2)**2 * np.sin(y)), ], [ (np.sin(x) * np.sin(y / 2)**2), (np.sin(x / 2)**2 * np.sin(y)), ], [ (np.cos(y / 2)**2 * np.sin(x)), -(np.sin(x / 2)**2 * np.sin(y)), ], ]) / 2) assert np.allclose(res, expected, atol=tol, rtol=0)
def make_tape(self): ops = [] obs = [] with QuantumTape() as tape: ops += [qml.RX(0.432, wires=0)] ops += [qml.Rot(0.543, 0, 0.23, wires=0)] ops += [qml.CNOT(wires=[0, "a"])] ops += [qml.RX(0.133, wires=4)] obs += [qml.PauliX(wires="a")] expval(obs[0]) obs += [probs(wires=[0, "a"])] return tape, ops, obs
def circuit(x, y): qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) qml.CNOT(wires=[0, 1]) return [expval(qml.PauliZ(0)), probs(wires=[1])]
def circuit(x, y): qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) qml.CNOT(wires=[0, 1]) return probs(wires=[0]), probs(wires=[1])
def func(x, y): qml.RX(x, wires=0) qml.RY(y, wires=1) qml.CNOT(wires=[0, 1]) return probs(wires=0), probs(wires=1)