def test_projection(): """Test that the Projector observable is correctly supported.""" wires = 2 dev = BraketLocalQubitDevice(wires=wires) thetas = [1.5, 1.6] p_01 = np.cos(thetas[0] / 2)**2 * np.sin(thetas[1] / 2)**2 p_10 = np.sin(thetas[0] / 2)**2 * np.cos(thetas[1] / 2)**2 def f(thetas, **kwargs): [qml.RY(thetas[i], wires=i) for i in range(wires)] measure_types = ["expval", "var", "sample"] projector_01 = qml.Projector([0, 1], wires=range(wires)) projector_10 = qml.Projector([1, 0], wires=range(wires)) # 01 case fs = [qml.map(f, [projector_01], dev, measure=m) for m in measure_types] assert np.allclose(fs[0](thetas), p_01) assert np.allclose(fs[1](thetas), p_01 - p_01**2) samples = fs[2](thetas, shots=100)[0].tolist() assert set(samples) == {0, 1} # 10 case fs = [qml.map(f, [projector_10], dev, measure=m) for m in measure_types] assert np.allclose(fs[0](thetas), p_10) assert np.allclose(fs[1](thetas), p_10 - p_10**2) samples = fs[2](thetas, shots=100)[0].tolist() assert set(samples) == {0, 1}
def test_compiled_program_was_correct_compared_with_default_qubit( self, qvm, device, tol): """Test that QVM device stores the compiled program correctly by comparing it with default.qubit. As the results coming from the qvm are stochastic, a constraint of 1 out of 5 runs was added. """ number_of_qnodes = 6 obs = [qml.PauliZ(0) @ qml.PauliZ(1)] obs_list = obs * number_of_qnodes dev = qml.device("forest.qvm", device=device, timeout=100) shape = qml.StronglyEntanglingLayers.shape(n_layers=4, n_wires=dev.num_wires) params = np.random.random(size=shape) qnodes = qml.map(qml.templates.StronglyEntanglingLayers, obs_list, dev) results = qnodes(params) dev2 = qml.device("default.qubit", wires=dev.num_wires) qnodes2 = qml.map(qml.templates.StronglyEntanglingLayers, obs_list, dev2) results2 = qnodes2(params) assert np.allclose(results, results2, atol=6e-02, rtol=0) assert dev.circuit_hash in dev._compiled_program_dict assert len(dev._compiled_program_dict.items()) == 1
def test_multiple_observables_same_wire_mixed(self, mocker): """Test that the QNode supports returning observables that are on the same wire but with different return types (provided that the observables are Pauli words and qubit-wise commuting)""" dev = qml.device("default.qubit", wires=3) w = np.random.random((2, 3, 3)) @qnode(dev) def f(w): qml.templates.StronglyEntanglingLayers(w, wires=range(3)) return qml.expval(qml.PauliX(0)), qml.var(qml.PauliX(0) @ qml.PauliZ(1)) spy = mocker.spy(qml.devices.DefaultQubit, "apply") res = f(w) spy.assert_called_once() q1 = qml.map(qml.templates.StronglyEntanglingLayers, [qml.PauliX(0)], dev, measure="expval") q2 = qml.map( qml.templates.StronglyEntanglingLayers, [qml.PauliX(0) @ qml.PauliZ(1)], dev, measure="var", ) res_2 = np.array([q1(w), q2(w)]).squeeze() assert np.allclose(res, res_2)
def test_invalid_observable(self): """Test that an invalid observable raises an exception""" dev = qml.device("default.qubit", wires=1) obs_list = [qml.PauliX(0), qml.S(wires=0)] template = lambda x, wires: qml.RX(x, wires=0) with pytest.raises(ValueError, match="Some or all observables are not valid"): qml.map(template, obs_list, dev, measure=["expval", "var"])
def test_compiled_program_was_used(self, qvm, device, monkeypatch): """Test that QVM device used the compiled program correctly, after it was stored""" dev = qml.device("forest.qvm", device=device, timeout=100) number_of_qnodes = 6 obs = [qml.PauliZ(0) @ qml.PauliZ(1)] obs_list = obs * number_of_qnodes qnodes = qml.map(qml.templates.StronglyEntanglingLayers, obs_list, dev) shape = qml.StronglyEntanglingLayers.shape(n_layers=4, n_wires=dev.num_wires) params = np.random.random(size=shape) # For the first evaluation, use the real compile method qnodes[0](params) call_history = [] with monkeypatch.context() as m: m.setattr(QuantumComputer, "compile", lambda self, prog: call_history.append(prog)) for i in range(1, number_of_qnodes): qnodes[i](params) # Then use the mocked one to see if it was called results = qnodes(params) assert len(call_history) == 0 assert dev.circuit_hash in dev._compiled_program_dict assert len(dev._compiled_program_dict.items()) == 1
def test_compiled_program_was_stored_mutable_qnode_with_if_statement( self, qvm, device, statements): """Test that QVM device stores the compiled program when the QNode is mutated correctly""" dev = qml.device("forest.qvm", device=device, timeout=100) assert len(dev._compiled_program_dict.items()) == 0 def circuit(params, wires, statement=None): if statement: qml.Hadamard(0) qml.CNOT(wires=[0, 1]) obs = [qml.PauliZ(0) @ qml.PauliZ(1)] obs_list = obs * 6 qnodes = qml.map(circuit, obs_list, dev) for idx, stmt in enumerate(statements): qnodes[idx]([], statement=stmt) assert dev.circuit_hash in dev._compiled_program_dict # Using that True evaluates to 1 number_of_true = sum(statements) # Checks if all elements in the list were either ``True`` or ``False`` # In such a case we have compiled only one program length = 1 if (number_of_true == 6 or number_of_true == 0) else 2 assert len(dev._compiled_program_dict.items()) == length
def test_multiple_observables_same_wire_expval(self, mocker): """Test that the QNode supports returning expectation values of observables that are on the same wire (provided that they are Pauli words and qubit-wise commuting)""" dev = qml.device("default.qubit", wires=3) w = np.random.random((2, 3, 3)) @qnode(dev) def f(w): qml.templates.StronglyEntanglingLayers(w, wires=range(3)) return ( qml.expval(qml.PauliX(0)), qml.expval(qml.PauliX(0) @ qml.PauliZ(1)), qml.expval(qml.PauliX(2)), ) spy = mocker.spy(qml.devices.DefaultQubit, "apply") res = f(w) spy.assert_called_once() obs = [qml.PauliX(0), qml.PauliX(0) @ qml.PauliZ(1), qml.PauliX(2)] qnodes = qml.map(qml.templates.StronglyEntanglingLayers, obs, dev) res_2 = qnodes(w) assert np.allclose(res, res_2)
def test_multiple_devices(self, mocker): """Test that passing multiple devices to ExpvalCost works correctly""" dev = [ qml.device("default.qubit", wires=2), qml.device("default.mixed", wires=2) ] spy = mocker.spy(DefaultQubit, "apply") spy2 = mocker.spy(DefaultMixed, "apply") obs = [qml.PauliZ(0), qml.PauliZ(1)] h = qml.Hamiltonian([1, 1], obs) qnodes = qml.ExpvalCost(qml.templates.BasicEntanglerLayers, h, dev) w = qml.init.basic_entangler_layers_uniform(3, 2, seed=1967) res = qnodes(w) spy.assert_called_once() spy2.assert_called_once() mapped = qml.map(qml.templates.BasicEntanglerLayers, obs, dev) exp = sum(mapped(w)) assert np.allclose(res, exp) with pytest.warns( UserWarning, match="ExpvalCost was instantiated with multiple devices."): qnodes.metric_tensor([w])
def test_qnode_collection_integration(self): """Test that a PassthruQNode using default.qubit.jax works with QNodeCollections.""" dev = qml.device("default.qubit.jax", wires=2) def ansatz(weights, **kwargs): qml.RX(weights[0], wires=0) qml.RY(weights[1], wires=1) qml.CNOT(wires=[0, 1]) obs_list = [ qml.PauliX(0) @ qml.PauliY(1), qml.PauliZ(0), qml.PauliZ(0) @ qml.PauliZ(1) ] qnodes = qml.map(ansatz, obs_list, dev, interface="jax") if not qml.tape_mode_active(): assert qnodes.interface == "jax" weights = jnp.array([0.1, 0.2]) def cost(weights): return jnp.sum(jnp.array(qnodes(weights))) grad = jax.grad(cost)(weights) assert grad.shape == weights.shape
def test_mapping_over_observables_as_tuples(self): """Test that mapping over a tuple of observables produces a QNodeCollection with the correct QNodes, with a single device broadcast.""" dev = qml.device("default.qubit", wires=1) obs_list = (qml.PauliX(0), qml.PauliY(0)) template = lambda x, wires: qml.RX(x, wires=0) qc = qml.map(template, obs_list, dev) assert len(qc) == 2 # evaluate collection so that queue is populated qc(1) assert len(qc[0].ops) == 2 assert qc[0].ops[0].name == "RX" assert qc[0].ops[1].name == "PauliX" assert len(qc[1].ops) == 2 assert qc[1].ops[0].name == "RX" assert qc[1].ops[1].name == "PauliY" # test that device is broadcast assert qc[0].device is qc[1].device
def test_circuits_evaluate(self, ansatz, observables, params, mock_device, seed): """Tests that the circuits returned by ``vqe.circuits`` evaluate properly""" dev = mock_device(wires=3) circuits = qml.map(ansatz, observables, device=dev) res = circuits(params) assert all(val == 1.0 for val in res)
def test_qnode_collection_integration(self): """Test that a PassthruQNode default.qubit.tf works with QNodeCollections.""" dev = qml.device("default.qubit.tf", wires=2) obs_list = [ qml.PauliX(0) @ qml.PauliY(1), qml.PauliZ(0), qml.PauliZ(0) @ qml.PauliZ(1) ] qnodes = qml.map(qml.templates.StronglyEntanglingLayers, obs_list, dev, interface="tf") assert qnodes.interface == "tf" weights = tf.Variable( qml.init.strong_ent_layers_normal(n_wires=2, n_layers=2)) @tf.function def cost(weights): return tf.reduce_sum(qnodes(weights)) with tf.GradientTape() as tape: res = qnodes(weights) grad = tape.gradient(res, weights) assert isinstance(grad, tf.Tensor) assert grad.shape == weights.shape
def test_passing_kwargs(self): """Test that the step size and order used for the finite differences differentiation method were passed to the QNode instances using the keyword arguments.""" dev = qml.device("default.qubit", wires=1) obs_list = [qml.PauliX(0), qml.PauliY(0)] template = lambda x, wires: qml.RX(x, wires=0) qc = qml.map(template, obs_list, dev, measure=["expval", "var"], h=123, order=2) qc(1) assert len(qc) == 2 # Checking the h attribute which contains the step size assert qc[0].h == 123 assert qc[1].h == 123 # Checking that the order is set in each QNode assert qc[0].order == 2 assert qc[1].order == 2
def test_mapping_over_devices(self): """Test that mapping over a list of devices produces a QNodeCollection with the correct QNodes""" dev_list = [qml.device("default.qubit", wires=1), qml.device("default.qubit", wires=1)] obs_list = [qml.PauliX(0), qml.PauliY(0)] template = lambda x, wires: qml.RX(x, wires=0) qc = qml.map(template, obs_list, dev_list) assert len(qc) == 2 # evaluate collection so that queue is populated qc(1) queue = qc[0].qtape.operations + qc[0].qtape.observables assert len(queue) == 2 assert queue[0].name == "RX" assert queue[1].name == "PauliX" queue = qc[1].qtape.operations + qc[1].qtape.observables assert len(queue) == 2 assert queue[0].name == "RX" assert queue[1].name == "PauliY" # test that device is not broadcast assert qc[0].device is not qc[1].device
def test_multiple_devices(self, mocker): """Test that passing multiple devices to ExpvalCost works correctly""" dev = [qml.device("default.qubit", wires=2), qml.device("default.mixed", wires=2)] spy = mocker.spy(DefaultQubit, "apply") spy2 = mocker.spy(DefaultMixed, "apply") obs = [qml.PauliZ(0), qml.PauliZ(1)] h = qml.Hamiltonian([1, 1], obs) qnodes = qml.ExpvalCost(qml.templates.BasicEntanglerLayers, h, dev) np.random.seed(1967) w = np.random.random(qml.templates.BasicEntanglerLayers.shape(n_layers=3, n_wires=2)) res = qnodes(w) spy.assert_called_once() spy2.assert_called_once() mapped = qml.map(qml.templates.BasicEntanglerLayers, obs, dev) exp = sum(mapped(w)) assert np.allclose(res, exp) with pytest.warns(UserWarning, match="ExpvalCost was instantiated with multiple devices."): qml.metric_tensor(qnodes, approx="block-diag")(w)
def test_mapping_over_measurements(self): """Test that mapping over a list of measurement types produces a QNodeCollection with the correct QNodes""" dev = qml.device("default.qubit", wires=1) obs_list = [qml.PauliX(0), qml.PauliY(0)] template = lambda x, wires: qml.RX(x, wires=0) qc = qml.map(template, obs_list, dev, measure=["expval", "var"]) assert len(qc) == 2 # evaluate collection so that queue is populated qc(1) queue = qc[0].qtape.operations + qc[0].qtape.observables assert len(queue) == 2 assert queue[0].name == "RX" assert queue[1].name == "PauliX" assert queue[1].return_type == qml.operation.Expectation queue = qc[1].qtape.operations + qc[1].qtape.observables assert len(queue) == 2 assert queue[0].name == "RX" assert queue[1].name == "PauliY" assert queue[1].return_type == qml.operation.Variance
def test_qnode_collection_integration(self): """Test that a PassthruQNode default.qubit.autograd works with QNodeCollections.""" dev = qml.device("default.qubit.autograd", wires=2) def ansatz(weights, **kwargs): qml.RX(weights[0], wires=0) qml.RY(weights[1], wires=1) qml.CNOT(wires=[0, 1]) obs_list = [ qml.PauliX(0) @ qml.PauliY(1), qml.PauliZ(0), qml.PauliZ(0) @ qml.PauliZ(1) ] qnodes = qml.map(ansatz, obs_list, dev, interface="autograd") assert qnodes.interface == "autograd" weights = np.array([0.1, 0.2], requires_grad=True) def cost(weights): return np.sum(qnodes(weights)) grad = qml.grad(cost)(weights)[0] assert grad.shape == weights.shape
def test_QNodes_have_right_interface(self, ansatz, observables, params, mock_device): """Test that QNodes have the torch interface""" dev = mock_device(wires=3) circuits = qml.map(ansatz, observables, device=dev, interface="torch") assert all(c.interface == "torch" for c in circuits) res = [c(params) for c in circuits] assert all(isinstance(val, torch.Tensor) for val in res)
def test_circuits_valid_init(self, ansatz, observables, mock_device): """Tests that a collection of circuits is properly created by vqe.circuits""" dev = mock_device() circuits = qml.map(ansatz, observables, device=dev) assert len(circuits) == len(observables) assert all(callable(c) for c in circuits) assert all(c.device == dev for c in circuits)
def test_QNodes_have_right_interface(self, ansatz, observables, params, mock_device, interface): """Test that QNodes have the Autograd interface""" dev = mock_device(wires=3) circuits = qml.map(ansatz, observables, device=dev, interface=interface) assert all(c.interface == "autograd" for c in circuits) res = [c(params) for c in circuits] assert all(isinstance(val, (np.ndarray, float)) for val in res)
def test_QNodes_have_right_interface(self, ansatz, observables, params, mock_device, interface): """Test that QNodes have the Autograd interface""" mock_device.num_wires = 3 circuits = qml.map(ansatz, observables, device=mock_device, interface=interface) assert all(c.interface == "autograd" for c in circuits) assert all(c.__class__.__name__ == "AutogradQNode" for c in circuits) res = [c(params) for c in circuits] assert all(isinstance(val, float) for val in res)
def __init__( self, ansatz, hamiltonian, device, interface="autograd", diff_method="best", optimize=False, **kwargs, ): coeffs, observables = hamiltonian.terms self.hamiltonian = hamiltonian """Hamiltonian: the hamiltonian defining the VQE problem.""" self.qnodes = None """QNodeCollection: The QNodes to be evaluated. Each QNode corresponds to the expectation value of each observable term after applying the circuit ansatz.""" self._optimize = optimize if self._optimize: if not qml.tape_mode_active(): raise ValueError( "Observable optimization is only supported in tape mode. Tape " "mode can be enabled with the command:\n" "qml.enable_tape()" ) obs_groupings, coeffs_groupings = qml.grouping.group_observables(observables, coeffs) wires = device.wires.tolist() @qml.qnode(device, interface=interface, diff_method=diff_method, **kwargs) def circuit(*qnode_args, obs, **qnode_kwargs): """Converting ansatz into a full circuit including measurements""" ansatz(*qnode_args, wires=wires, **qnode_kwargs) return [qml.expval(o) for o in obs] def cost_fn(*qnode_args, **qnode_kwargs): """Combine results from grouped QNode executions with grouped coefficients""" total = 0 for o, c in zip(obs_groupings, coeffs_groupings): res = circuit(*qnode_args, obs=o, **qnode_kwargs) total += sum([r * c_ for r, c_ in zip(res, c)]) return total self.cost_fn = cost_fn else: self.qnodes = qml.map( ansatz, observables, device, interface=interface, diff_method=diff_method, **kwargs ) self.cost_fn = qml.dot(coeffs, self.qnodes)
def __init__(self, ansatz, hamiltonian, device, interface="autograd", diff_method="best"): coeffs, observables = hamiltonian.terms self.hamiltonian = hamiltonian """Hamiltonian: the hamiltonian defining the VQE problem.""" self.qnodes = qml.map(ansatz, observables, device, interface=interface, diff_method=diff_method) """QNodeCollection: The QNodes to be evaluated. Each QNode corresponds to the the expectation value of each observable term after applying the circuit ansatz. """ self.cost_fn = qml.dot(coeffs, self.qnodes)
def test_QNodes_have_right_interface(self, ansatz, observables, params, mock_device): """Test that QNodes have the tf interface""" if ansatz == amp_embed_and_strong_ent_layer: pytest.skip("TF doesn't work with ragged arrays") dev = mock_device(wires=3) circuits = qml.map(ansatz, observables, device=dev, interface="tf") assert all(c.interface == "tf" for c in circuits) res = [c(params) for c in circuits] assert all(isinstance(val, (Variable, tf.Tensor)) for val in res)
def test_single_qubit_vqe(self, tol): """Test single-qubit VQE has the correct QNG value every step, the correct parameter updates, and correct cost after 200 steps""" dev = qml.device("default.qubit", wires=1) def circuit(params, wires=0): qml.RX(params[0], wires=wires) qml.RY(params[1], wires=wires) coeffs = [1, 1] obs_list = [ qml.PauliX(0), qml.PauliZ(0) ] qnodes = qml.map(circuit, obs_list, dev, measure='expval') cost_fn = qml.dot(coeffs, qnodes) def gradient(params): """Returns the gradient""" da = -np.sin(params[0]) * (np.cos(params[1]) + np.sin(params[1])) db = np.cos(params[0]) * (np.cos(params[1]) - np.sin(params[1])) return np.array([da, db]) eta = 0.01 init_params = np.array([0.011, 0.012]) num_steps = 200 opt = qml.QNGOptimizer(eta) theta = init_params # optimization for 200 steps total for t in range(num_steps): theta_new = opt.step(cost_fn, theta, metric_tensor_fn=qnodes.qnodes[0].metric_tensor) # check metric tensor res = opt.metric_tensor exp = np.diag([0.25, (np.cos(theta[0]) ** 2)/4]) assert np.allclose(res, exp, atol=tol, rtol=0) # check parameter update dtheta = eta * sp.linalg.pinvh(exp) @ gradient(theta) assert np.allclose(dtheta, theta - theta_new, atol=tol, rtol=0) theta = theta_new # check final cost assert np.allclose(cost_fn(theta), -1.41421356, atol=tol, rtol=0)
def test_compiled_program_was_stored(self, qvm, device): """Test that QVM device stores the compiled program correctly""" dev = qml.device("forest.qvm", device=device, timeout=100) assert len(dev._compiled_program_dict.items()) == 0 def circuit(params, wires): qml.Hadamard(0) qml.CNOT(wires=[0, 1]) obs = [qml.PauliZ(0) @ qml.PauliZ(1)] obs_list = obs * 6 qnodes = qml.map(circuit, obs_list, dev) qnodes([]) assert dev.circuit_hash in dev._compiled_program_dict assert len(dev._compiled_program_dict.items()) == 1
def __init__( self, ansatz, observables, device, measure="expval", interface="autograd", diff_method="best", **kwargs, ): self.qnodes = qml.map( ansatz, observables, device, measure=measure, interface=interface, diff_method=diff_method, **kwargs, )
def test_compiled_program_was_stored(self): """Test that QVM device stores the compiled program correctly""" dev = qml.device("default.qubit", wires=3) def circuit(params, wires): qml.Hadamard(0) qml.CNOT(wires=[0, 1]) obs = [qml.PauliZ(0) @ qml.PauliZ(1)] obs_list = obs * 6 qnodes = qml.map(circuit, obs_list, dev) qnodes([], parallel=True) hashes = set() for qnode in qnodes: hashes.add(qnode.circuit.hash) assert len(hashes) == 1
def __init__( self, ansatz, observables, device, measure="expval", interface="autograd", diff_method="best", **kwargs, ): warn(WARNING_STRING, DeprecationWarning, stacklevel=2) self.qnodes = qml.map( ansatz, observables, device, measure=measure, interface=interface, diff_method=diff_method, **kwargs, )
def test_qnode_collection_integration(self, tol): """Test that a PassthruQNode strawberryfields.tf works with QNodeCollections.""" cutoff = 15 dev = qml.device("strawberryfields.tf", wires=2, cutoff_dim=cutoff) def circuit(weights, input_state=None, **kwargs): qml.FockStateVector(input_state, wires=[0, 1]) qml.TwoModeSqueezing(weights[0], weights[1], wires=[0, 1]) obs_list = [ qml.FockStateProjector(np.array([0, 0]), wires=[0, 1]), qml.FockStateProjector(np.array([1, 1]), wires=[0, 1]), ] qnodes = qml.map(circuit, obs_list, dev, interface="tf") assert qnodes.interface == "tf" weights = tf.Variable([0.12, -0.543]) def cost(weights): vacuum = np.zeros((cutoff, cutoff), dtype=np.complex64) vacuum[0, 0] = 1.0 + 0.0j vacuum = tf.constant(vacuum) return tf.reduce_sum(qnodes(weights, input_state=vacuum)) with tf.GradientTape() as tape: res = cost(weights) grad = tape.gradient(res, weights) assert isinstance(grad, tf.Tensor) assert grad.shape == weights.shape R = weights[0] expected_grad = [ -2 * tf.math.tanh(R) / tf.math.cosh(R)**2 + 2 * (tf.math.sinh(R) - tf.math.sinh(R)**3) / tf.math.cosh(R)**5, 0, ] assert np.allclose(grad, expected_grad, atol=tol, rtol=0)