def test_regular_keyword_arguments(self, mock_device): """Test that regular keyword arguments are properly converted to Variable instances.""" def circuit(*, a=1, b=2, c=3, d=4): qml.RX(a, wires=[0]) qml.RY(b, wires=[0]) qml.RZ(c, wires=[0]) qml.RZ(d, wires=[0]) return qml.expval(qml.PauliX(0)) node = BaseQNode(circuit, mock_device) arg_vars, kwarg_vars = node._make_variables([], {"b": 3}) expected_kwarg_vars = { "a": [Variable(0, "a", is_kwarg=True)], "b": [Variable(0, "b", is_kwarg=True)], "c": [Variable(0, "c", is_kwarg=True)], "d": [Variable(0, "d", is_kwarg=True)], } assert not arg_vars for expected_key in expected_kwarg_vars: for var, expected in zip( qml.utils._flatten(kwarg_vars[expected_key]), qml.utils._flatten(expected_kwarg_vars[expected_key]), ): assert var == expected
def test_variadic_arguments(self, mock_device): """Test that variadic arguments are properly converted to Variable instances.""" def circuit(a, *b): qml.RX(a, wires=[0]) qml.RX(b[0], wires=[0]) qml.RX(b[1][1], wires=[0]) qml.RX(b[2], wires=[0]) return qml.expval(qml.PauliX(0)) node = BaseQNode(circuit, mock_device) arg_vars, kwarg_vars = node._make_variables( [0.1, 0.2, np.array([0, 1, 2, 3]), 0.5], {}) expected_arg_vars = [ Variable(0, "a"), Variable(1, "b[0]"), Variable(2, "b[1][0]"), Variable(3, "b[1][1]"), Variable(4, "b[1][2]"), Variable(5, "b[1][3]"), Variable(6, "b[2]"), ] assert not kwarg_vars for var, expected in zip(qml.utils._flatten(arg_vars), expected_arg_vars): assert var == expected
def test_regular_arguments(self, mock_device): """Test that regular arguments are properly converted to Variable instances.""" def circuit(a, b, c, d): qml.RX(a, wires=[0]) qml.RY(b, wires=[0]) qml.RZ(c, wires=[0]) qml.RZ(d, wires=[0]) return qml.expval(qml.PauliX(0)) node = BaseQNode(circuit, mock_device) arg_vars, kwarg_vars = node._make_variables([1.0, 2.0, 3.0, 4.0], {}) expected_arg_vars = [ Variable(0, "a"), Variable(1, "b"), Variable(2, "c"), Variable(3, "d"), ] for var, expected in zip(qml.utils._flatten(arg_vars), expected_arg_vars): assert var == expected assert not kwarg_vars
def test_array_arguments(self, mock_device): """Test that array arguments are properly converted to Variable instances.""" def circuit(weights): qml.RX(weights[0, 0], wires=[0]) qml.RY(weights[0, 1], wires=[0]) qml.RZ(weights[1, 0], wires=[0]) qml.RZ(weights[1, 1], wires=[0]) return qml.expval(qml.PauliX(0)) node = BaseQNode(circuit, mock_device) weights = np.array([[1, 2], [3, 4]]) arg_vars, kwarg_vars = node._make_variables([weights], {}) expected_arg_vars = [ Variable(0, "weights[0,0]"), Variable(1, "weights[0,1]"), Variable(2, "weights[1,0]"), Variable(3, "weights[1,1]"), ] for var, expected in zip(qml.utils._flatten(arg_vars), expected_arg_vars): assert var == expected assert not kwarg_vars
def test_prune_tensors_construct(self, mock_device): """Test that the tensors are pruned in construct.""" def circuit(x): return qml.expval(qml.PauliX(0) @ qml.Identity(1)) qnode = BaseQNode(circuit, mock_device) qnode._construct([1.0], {}) assert qnode.ops[0].name == "PauliX" assert len(qnode.ops[0].wires) == 1 assert qnode.ops[0].wires[0] == 0
def test_prune_tensors(self, mock_device): """Test that the _prune_tensors auxiliary method prunes correct for a single Identity in the Tensor.""" px = qml.PauliX(1) obs = qml.Identity(0) @ px def circuit(x): return qml.expval(obs) qnode = BaseQNode(circuit, mock_device) assert qnode._prune_tensors(obs) == px
def mock_qnode(mock_device): """Provides a circuit for the subsequent tests of the operation queue""" def circuit(x): qml.RX(x, wires=[0]) qml.CNOT(wires=[0, 1]) qml.RY(0.4, wires=[0]) qml.RZ(-0.2, wires=[1]) return qml.expval(qml.PauliX(0)), qml.expval(qml.PauliZ(1)) node = BaseQNode(circuit, mock_device) node._construct([1.0], {}) return node
def test_prune_tensors_no_pruning_took_place(self, mock_device): """Test that the _prune_tensors auxiliary method returns the original tensor if no observables were pruned.""" px = qml.PauliX(1) obs = px def circuit(x): return qml.expval(obs) qnode = BaseQNode(circuit, mock_device) assert qnode._prune_tensors(obs) == px
def test_evaluate(self, x, y, tol): """Tests correct evaluation""" dev = qml.device("default.qubit", wires=2) def circuit(x, y): qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) qml.CNOT(wires=[0, 1]) return qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) node = BaseQNode(circuit, dev) res = node.evaluate([x, y], {}) expected = np.sin(y) * np.cos(x) assert np.allclose(res, expected, atol=tol, rtol=0)
def test_print_applied_with_probs(self, mock_device): """Test that printing applied gates works correctly when probs are returned""" H = np.array([[0, 1], [1, 0]]) def circuit(x): qml.RX(x, wires=[0]) qml.CNOT(wires=[0, 1]) qml.SWAP(wires=[1, 0]) qml.RZ(-0.2, wires=[1]) return qml.probs(wires=[0]), qml.var(qml.Hermitian(H, wires=1)) expected_qnode_print = textwrap.dedent( """\ Operations ========== RX({x}, wires=[0]) CNOT(wires=[0, 1]) SWAP(wires=[1, 0]) RZ(-0.2, wires=[1]) Observables =========== probs(wires=[0]) var(Hermitian(array([[0, 1], [1, 0]]), wires=[1]))""" ) node = BaseQNode(circuit, mock_device) # test before construction f = io.StringIO() with contextlib.redirect_stdout(f): node.print_applied() out = f.getvalue().strip() assert out == "QNode has not yet been executed." # construct QNode f = io.StringIO() node._set_variables([0.1], {}) node._construct([0.1], {}) with contextlib.redirect_stdout(f): node.print_applied() out = f.getvalue().strip() assert out == expected_qnode_print.format(x=0.1)
def test_calling_with_kwargs(self, operable_mock_device_2_wires): """Various quantum func calling syntax errors.""" def circuit(x, y=0.2, *, m=0.3, n, **kwargs): circuit.in_args = (x, y, m, n) return qml.expval(qml.PauliZ(0)) node = BaseQNode(circuit, operable_mock_device_2_wires, mutable=True) with pytest.raises(QuantumFunctionError, match="parameter 'x' given twice"): node(0.1, x=1.1) with pytest.raises( QuantumFunctionError, match="'kwargs' cannot be given using the keyword syntax"): node(kwargs=1) with pytest.raises(QuantumFunctionError, match="takes 2 positional parameters, 3 given"): node(0.1, 0.2, 100, n=0.4) with pytest.raises(QuantumFunctionError, match="positional parameter 'x' missing"): node(n=0.4) with pytest.raises(QuantumFunctionError, match="keyword-only parameter 'n' missing"): node(0.1) # valid calls node(x=0.1, n=0.4) assert circuit.in_args[2:] == (0.3, 0.4) # first two are Variables node(0.1, n=0.4) assert circuit.in_args[2:] == (0.3, 0.4)
def test_calling_errors(self, operable_mock_device_2_wires): """Good quantum func calling syntax errors (auxiliary-equals-parameters-with-default syntax).""" def circuit(x, y=0.2, *args, z=0.3): circuit.in_args = (x, y, z) return qml.expval(qml.PauliZ(0)) node = BaseQNode(circuit, operable_mock_device_2_wires, mutable=True) with pytest.raises( QuantumFunctionError, match="'x' cannot be given using the keyword syntax"): node(0.1, x=1.1) with pytest.raises(QuantumFunctionError, match="Unknown quantum function parameter 'foo'"): node(foo=1) with pytest.raises( QuantumFunctionError, match="'args' cannot be given using the keyword syntax"): node(args=1) with pytest.raises( TypeError, match="missing 1 required positional argument: 'x'"): node(z=0.4) # valid calls node(0.1) assert circuit.in_args[1:] == (0.2, 0.3) # first is a Variable node(0.1, y=1.2) assert circuit.in_args[1:] == (1.2, 0.3) node(0.1, z=1.3, y=1.2) assert circuit.in_args[1:] == (1.2, 1.3)
def test_device_executions(self): """Test the number of times a qubit device is executed over a QNode's lifetime is tracked by `num_executions`""" dev_1 = qml.device("default.qubit", wires=2) def circuit_1(x, y): qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) qml.CNOT(wires=[0, 1]) return qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) node_1 = BaseQNode(circuit_1, dev_1) num_evals_1 = 10 for _ in range(num_evals_1): node_1(0.432, 0.12) assert dev_1.num_executions == num_evals_1 # test a second instance of a default qubit device dev_2 = qml.device("default.qubit", wires=2) def circuit_2(x, y): qml.RX(x, wires=[0]) qml.CNOT(wires=[0, 1]) return qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) node_2 = BaseQNode(circuit_2, dev_2) num_evals_2 = 5 for _ in range(num_evals_2): node_2(0.432, 0.12) assert dev_2.num_executions == num_evals_2 # test a new circuit on an existing instance of a qubit device def circuit_3(x, y): qml.RY(y, wires=[1]) qml.CNOT(wires=[0, 1]) return qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) node_3 = BaseQNode(circuit_3, dev_1) num_evals_3 = 7 for _ in range(num_evals_3): node_3(0.432, 0.12) assert dev_1.num_executions == num_evals_1 + num_evals_3
def test_arg_as_wire_argument(self, operable_mock_device_2_wires): """Error: trying to use a differentiable parameter as a wire argument.""" def circuit(x): qml.RX(0.5, wires=[x]) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(2)) node = BaseQNode(circuit, operable_mock_device_2_wires) with pytest.raises(TypeError, match="Wires must be integers"): node(1)
def test_kwarg_as_wire_argument(self, operable_mock_device_2_wires): """Error: trying to use a keyword-only parameter as a wire argument in an immutable circuit.""" def circuit(*, x=None): qml.RX(0.5, wires=[x]) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)) node = BaseQNode(circuit, operable_mock_device_2_wires, mutable=False) with pytest.raises(TypeError, match="Wires must be integers"): node(x=1)
def test_bad_wire_argument(self, operable_mock_device_2_wires): """Error: wire arguments must be intergers.""" def circuit(x): qml.RX(x, wires=[0.5]) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(2)) node = BaseQNode(circuit, operable_mock_device_2_wires) with pytest.raises(TypeError, match="Wires must be integers"): node(1)
def test_keywordargs_used(self, qubit_device_1_wire, tol): """Tests that qnodes use keyword arguments.""" def circuit(w, x=None): qml.RX(x, wires=[0]) return qml.expval(qml.PauliZ(0)) node = BaseQNode(circuit, qubit_device_1_wire) c = node(1.0, x=np.pi) assert c == pytest.approx(-1.0, abs=tol)
def test_operation_appending(self, mock_device): """Tests that operations are correctly appended.""" CNOT = qml.CNOT(wires=[0, 1]) def circuit(x): qml.QueuingContext.append_operator(CNOT) qml.RY(0.4, wires=[0]) qml.RZ(-0.2, wires=[1]) return qml.expval(qml.PauliX(0)), qml.expval(qml.PauliZ(1)) qnode = BaseQNode(circuit, mock_device) qnode._construct([1.0], {}) assert qnode.ops[0].name == "CNOT" assert qnode.ops[1].name == "RY" assert qnode.ops[2].name == "RZ" assert qnode.ops[3].name == "PauliX"
def test_calling_bad_errors(self, operable_mock_device_2_wires): """Confusing quantum func calling errors and bugs (auxiliary-equals-parameters-with-default syntax).""" def circuit(x=0.1): return qml.expval(qml.PauliZ(0)) node = BaseQNode(circuit, operable_mock_device_2_wires) with pytest.raises(TypeError, match="got multiple values for argument 'x'"): node(0.3) # default arg given positionally, wrong error message
def test_return_of_non_observable(self, operable_mock_device_2_wires): """Error: qfunc returns something besides observables.""" def circuit(x): qml.RX(x, wires=[0]) return qml.expval(qml.PauliZ(wires=0)), 0.3 node = BaseQNode(circuit, operable_mock_device_2_wires) with pytest.raises(QuantumFunctionError, match="A quantum function must return either"): node(0.5)
def test_simple_valid_call(self, operable_mock_device_2_wires): """BaseQNode gives an error here, "got multiple values for argument 'x'" """ def circuit(x=0): qml.RX(x, wires=[0]) return qml.expval(qml.PauliZ(0)) node = BaseQNode(circuit, operable_mock_device_2_wires) node(0.3) assert node.ops[0].parameters[0] == 0.3
def test_unused_positional_parameter(self, operable_mock_device_2_wires): """Error: a positional parameter is not used in the circuit.""" def circuit(a, x): qml.RX(a, wires=[0]) return qml.expval(qml.PauliZ(0)) node = BaseQNode(circuit, operable_mock_device_2_wires, properties={"par_check": True}) with pytest.raises(QuantumFunctionError, match="The positional parameters"): node(1.0, 2.0)
def test_operation_removal(self, mock_device): """Tests that operations are correctly removed.""" def circuit(x): RX = qml.RX(x, wires=[0]) qml.CNOT(wires=[0, 1]) qml.RY(0.4, wires=[0]) qml.RZ(-0.2, wires=[1]) qml._current_context._remove_op(RX) return qml.expval(qml.PauliX(0)), qml.expval(qml.PauliZ(1)) qnode = BaseQNode(circuit, mock_device) qnode._construct([1.0], {}) assert qnode.ops[0].name == "CNOT" assert qnode.ops[1].name == "RY" assert qnode.ops[2].name == "RZ" assert qnode.ops[3].name == "PauliX"
def test_observable_on_nonexistant_wire(self, operable_mock_device_2_wires): """Error: an observable is measured on a non-existant wire.""" def circuit(x): qml.RX(x, wires=[0]) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(2)) node = BaseQNode(circuit, operable_mock_device_2_wires) with pytest.raises(QuantumFunctionError, match="applied to invalid wire"): node(0.5)
def test_keywordargs_with_kwargs(self, qubit_device_1_wire, tol): """Tests that nothing happens if unknown keyword arg passed with qnodes accepting **kwargs.""" def circuit(w, x=None, **kwargs): qml.RX(x, wires=[0]) return qml.expval(qml.PauliZ(0)) node = BaseQNode(circuit, qubit_device_1_wire) c = node(1.0, x=np.pi, y=10) assert c == pytest.approx(-1.0, abs=tol)
def test_arraylike_args_used(self, qubit_device_2_wires, tol): """Tests that qnodes use array-like positional arguments.""" def circuit(x): qml.RX(x[0], wires=[0]) qml.RX(x[1], wires=[1]) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)) node = BaseQNode(circuit, qubit_device_2_wires) c = node([np.pi, np.pi]) assert c == pytest.approx([-1.0, -1.0], abs=tol)
def test_observable_order_violated(self, operable_mock_device_2_wires): """Error: qfunc does not return all observables in the correct order.""" def circuit(x): qml.RX(x, wires=[0]) ex = qml.expval(qml.PauliZ(wires=1)) return qml.expval(qml.PauliZ(wires=0)), ex node = BaseQNode(circuit, operable_mock_device_2_wires) with pytest.raises(QuantumFunctionError, match="All measured observables must be returned"): node(0.5)
def test_cv_operations_on_qubit_device(self, operable_mock_device_2_wires): """Error: cannot use CV operations on a qubit device.""" def circuit(x): qml.Displacement(0.5, 0, wires=[0]) return qml.expval(qml.X(0)) node = BaseQNode(circuit, operable_mock_device_2_wires) with pytest.raises( QuantumFunctionError, match="a qubit device; CV operations are not allowed"): node(0.5)
def test_arraylike_keywordargs_used(self, qubit_device_2_wires, tol): """Tests that qnodes use array-like keyword-only arguments.""" def circuit(w, *, x=None): qml.RX(x[0], wires=[0]) qml.RX(x[1], wires=[1]) qml.RZ(w, wires=[0]) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)) node = BaseQNode(circuit, qubit_device_2_wires) c = node(1.0, x=[np.pi, np.pi / 2]) assert c == pytest.approx([-1.0, 0.0], abs=tol)
def test_multiple_keywordargs_used(self, qubit_device_2_wires, tol): """Tests that qnodes can use multiple keyword-only arguments.""" def circuit(w, *, x=None, y=None): qml.RX(x, wires=[0]) qml.RX(y, wires=[1]) qml.RZ(w, wires=[0]) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)) node = BaseQNode(circuit, qubit_device_2_wires) c = node(1.0, x=np.pi, y=np.pi / 2) assert c == pytest.approx([-1.0, 0.0], abs=tol)