diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6ca1602b0..ef6f667b1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -74,10 +74,10 @@ jobs: - name: Run tests run: | - pl-device-test --device=qiskit.basicaer --tb=short --skip-ops --analytic=False --shots=20000 --device-kwargs backend=qasm_simulator - pl-device-test --device=qiskit.aer --tb=short --skip-ops --analytic=False --shots=20000 --device-kwargs backend=qasm_simulator - pl-device-test --device=qiskit.aer --tb=short --skip-ops --analytic=True --device-kwargs backend=statevector_simulator - pl-device-test --device=qiskit.aer --tb=short --skip-ops --analytic=True --device-kwargs backend=unitary_simulator + pl-device-test --device=qiskit.basicaer --tb=short --skip-ops --shots=20000 --device-kwargs backend=qasm_simulator + pl-device-test --device=qiskit.aer --tb=short --skip-ops --shots=20000 --device-kwargs backend=qasm_simulator + pl-device-test --device=qiskit.aer --tb=short --skip-ops --shots=None --device-kwargs backend=statevector_simulator + pl-device-test --device=qiskit.aer --tb=short --skip-ops --shots=None --device-kwargs backend=unitary_simulator - name: Upload coverage to Codecov uses: codecov/codecov-action@v1 diff --git a/doc/conf.py b/doc/conf.py index 4089fb0e0..5c1acfc66 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -409,8 +409,9 @@ #autodoc_default_flags = ['members'] autosummary_generate = True -from directives import CustomDeviceGalleryItemDirective, CustomDemoGalleryItemDirective +from directives import UsageDetails, CustomDeviceGalleryItemDirective, CustomDemoGalleryItemDirective def setup(app): app.add_directive('devicegalleryitem', CustomDeviceGalleryItemDirective) app.add_directive('demogalleryitem', CustomDemoGalleryItemDirective) + app.add_directive("usagedetails", UsageDetails) diff --git a/doc/directives.py b/doc/directives.py index 99462acb9..f1d1b2913 100644 --- a/doc/directives.py +++ b/doc/directives.py @@ -20,6 +20,42 @@ import os +USAGE_DETAILS_TEMPLATE = """ +.. raw:: html + + +
+ +{content} + +.. raw:: html + +
+""" + + +class UsageDetails(Directive): + """Create a collapsed Usage Details section in the documentation.""" + + # defines the parameter the directive expects + # directives.unchanged means you get the raw value from RST + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = False + has_content = True + + def run(self): + rst = USAGE_DETAILS_TEMPLATE.format(content="\n".join(self.content)) + string_list = StringList(rst.split('\n')) + node = nodes.section() + self.state.nested_parse(string_list, self.content_offset, node) + return [node] + + GALLERY_TEMPLATE = """ .. raw:: html diff --git a/pennylane_qiskit/aer.py b/pennylane_qiskit/aer.py index d82872476..f00d8f575 100644 --- a/pennylane_qiskit/aer.py +++ b/pennylane_qiskit/aer.py @@ -38,16 +38,14 @@ class AerDevice(QiskitDevice): or iterable that contains unique labels for the subsystems as numbers (i.e., ``[-1, 0, 2]``) or strings (``['ancilla', 'q1', 'q2']``). backend (str): the desired backend - shots (int): number of circuit evaluations/random samples used - to estimate expectation values and variances of observables + shots (int or None): number of circuit evaluations/random samples used + to estimate expectation values and variances of observables. For statevector backends, + setting to ``None`` results in computing statistics like expectation values and variances analytically. Keyword Args: name (str): The name of the circuit. Default ``'circuit'``. compile_backend (BaseBackend): The backend used for compilation. If you wish to simulate a device compliant circuit, you can specify a backend here. - analytic (bool): For statevector backends, determines if the - expectation values and variances are to be computed analytically. - Default value is ``False``. noise_model (NoiseModel): NoiseModel Object from ``qiskit.providers.aer.noise`` """ diff --git a/pennylane_qiskit/basic_aer.py b/pennylane_qiskit/basic_aer.py index 84b58e182..814b235d6 100644 --- a/pennylane_qiskit/basic_aer.py +++ b/pennylane_qiskit/basic_aer.py @@ -38,16 +38,14 @@ class BasicAerDevice(QiskitDevice): or iterable that contains unique labels for the subsystems as numbers (i.e., ``[-1, 0, 2]``) or strings (``['ancilla', 'q1', 'q2']``). backend (str): the desired backend - shots (int): number of circuit evaluations/random samples used - to estimate expectation values and variances of observables + shots (int or None): number of circuit evaluations/random samples used + to estimate expectation values and variances of observables. For statevector backends, + setting to ``None`` results in computing statistics like expectation values and variances analytically. Keyword Args: name (str): The name of the circuit. Default ``'circuit'``. compile_backend (BaseBackend): The backend used for compilation. If you wish to simulate a device compliant circuit, you can specify a backend here. - analytic (bool): For statevector backends, determines if the - expectation values and variances are to be computed analytically. - Default value is ``False``. """ short_name = "qiskit.basicaer" diff --git a/pennylane_qiskit/qiskit_device.py b/pennylane_qiskit/qiskit_device.py index efebd5562..f5171c6e2 100644 --- a/pennylane_qiskit/qiskit_device.py +++ b/pennylane_qiskit/qiskit_device.py @@ -75,16 +75,14 @@ class QiskitDevice(QubitDevice, abc.ABC): or strings (``['ancilla', 'q1', 'q2']``). provider (Provider): The Qiskit simulation provider backend (str): the desired backend - shots (int): Number of circuit evaluations/random samples used - to estimate expectation values of observables. + shots (int or None): number of circuit evaluations/random samples used + to estimate expectation values and variances of observables. For statevector backends, + setting to ``None`` results in computing statistics like expectation values and variances analytically. Keyword Args: name (str): The name of the circuit. Default ``'circuit'``. compile_backend (BaseBackend): The backend used for compilation. If you wish to simulate a device compliant circuit, you can specify a backend here. - analytic (bool): For statevector backends, determines if the - expectation values and variances are to be computed analytically. - Default value is ``False``. """ name = "Qiskit PennyLane plugin" pennylane_requires = ">=0.12.0" @@ -113,16 +111,12 @@ def __init__(self, wires, provider, backend, shots=1024, **kwargs): super().__init__(wires=wires, shots=shots) # Keep track if the user specified analytic to be True - user_specified_analytic = "analytic" in kwargs and kwargs["analytic"] - self.analytic = kwargs.pop("analytic", False) + if shots is None and backend not in self._state_backends: - if self.analytic and backend not in self._state_backends: + # Raise a warning if no shots were specified for a hardware device + warnings.warn(self.hw_analytic_warning_message.format(backend), UserWarning) - if user_specified_analytic: - # Raise a warning if the analytic attribute was set to True - warnings.warn(self.hw_analytic_warning_message.format(backend), UserWarning) - - self.analytic = False + self.shots = 1024 self._backend = None diff --git a/requirements.txt b/requirements.txt index ee1f33052..477160fbe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ qiskit>=0.23.4 -pennylane>=0.14.0 +git+https://github.com/PennyLaneAI/pennylane.git numpy networkx>=2.2;python_version>'3.5' networkx>=2.2,<2.4;python_version=='3.5' diff --git a/tests/conftest.py b/tests/conftest.py index b8de9e138..c96c33b4f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -26,8 +26,8 @@ @pytest.fixture -def tol(analytic): - if analytic: +def tol(shots): + if shots is None: return {"atol": 0.01, "rtol": 0} return {"atol": 0.05, "rtol": 0.1} @@ -42,16 +42,19 @@ def _init_state(n): return _init_state + @pytest.fixture def skip_unitary(backend): if backend == "unitary_simulator": pytest.skip("This test does not support the unitary simulator backend.") + @pytest.fixture def run_only_for_unitary(backend): if backend != "unitary_simulator": pytest.skip("This test only supports the unitary simulator.") + @pytest.fixture(params=state_backends + hw_backends) def backend(request): return request.param @@ -68,22 +71,22 @@ def hardware_backend(request): @pytest.fixture(params=[AerDevice, BasicAerDevice]) -def device(request, backend, shots, analytic): - if backend not in state_backends and analytic == True: +def device(request, backend, shots): + if backend not in state_backends and shots is None: pytest.skip("Hardware simulators do not support analytic mode") def _device(n, device_options=None): if device_options is None: device_options = {} - return request.param(wires=n, backend=backend, shots=shots, analytic=analytic, **device_options) + return request.param(wires=n, backend=backend, shots=shots, **device_options) return _device @pytest.fixture(params=[AerDevice, BasicAerDevice]) -def state_vector_device(request, statevector_backend, shots, analytic): +def state_vector_device(request, statevector_backend, shots): def _device(n): - return request.param(wires=n, backend=statevector_backend, shots=shots, analytic=analytic) + return request.param(wires=n, backend=statevector_backend, shots=shots) return _device @@ -94,20 +97,20 @@ def mock_device(monkeypatch): with monkeypatch.context() as m: dev = qml.Device - m.setattr(dev, '__abstractmethods__', frozenset()) + m.setattr(dev, "__abstractmethods__", frozenset()) yield qml.Device() @pytest.fixture(scope="function") def recorder(): - return qml._queuing.OperationRecorder() + return qml.tape.OperationRecorder() @pytest.fixture(scope="function") def qubit_device_single_wire(): - return qml.device('default.qubit', wires=1) + return qml.device("default.qubit", wires=1) @pytest.fixture(scope="function") def qubit_device_2_wires(): - return qml.device('default.qubit', wires=2) + return qml.device("default.qubit", wires=2) diff --git a/tests/test_apply.py b/tests/test_apply.py index b54e28ac9..38b47baaa 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -42,25 +42,18 @@ ) -single_qubit_operations = [ - qml.PauliX, - qml.PauliY, - qml.PauliZ, - qml.Hadamard, - qml.S, - qml.T -] +single_qubit_operations = [qml.PauliX, qml.PauliY, qml.PauliZ, qml.Hadamard, qml.S, qml.T] single_qubit_operations_param = [qml.PhaseShift, qml.RX, qml.RY, qml.RZ] two_qubit = [qml.CNOT, qml.SWAP, qml.CZ] two_qubit_param = [qml.CRZ] three_qubit = [qml.Toffoli, qml.CSWAP] -@pytest.mark.parametrize("analytic", [True]) -@pytest.mark.parametrize("shots", [8192]) + +@pytest.mark.parametrize("shots", [None]) @pytest.mark.usefixtures("skip_unitary") class TestAnalyticApply: - """Test application of PennyLane operations with analytic attribute set to True.""" + """Test application of PennyLane operations with analytic calculation.""" def test_qubit_state_vector(self, init_state, device, tol): """Test that the QubitStateVector operation produces the expected @@ -149,8 +142,8 @@ def test_three_qubit_operations_no_parameters(self, init_state, device, operatio expected = np.abs(applied_operation.matrix @ state) ** 2 assert np.allclose(res, expected, **tol) -@pytest.mark.parametrize("analytic", [True]) -@pytest.mark.parametrize("shots", [8192]) + +@pytest.mark.parametrize("shots", [None]) @pytest.mark.usefixtures("run_only_for_unitary") class TestStateApplyUnitarySimulator: """Test application of PennyLane operations to the unitary simulator.""" @@ -161,14 +154,17 @@ def test_invalid_qubit(self, init_state, device): dev = device(1) state = init_state(1) - with pytest.raises(qml.DeviceError, match="The QubitStateVector operation is not supported on the unitary simulator backend"): + with pytest.raises( + qml.DeviceError, + match="The QubitStateVector operation is not supported on the unitary simulator backend", + ): dev.apply([qml.QubitStateVector(state, wires=[0])]) + @pytest.mark.parametrize("shots", [8192]) -@pytest.mark.parametrize("analytic", [False]) @pytest.mark.usefixtures("skip_unitary") class TestNonAnalyticApply: - """Test application of PennyLane operations with the analytic attribute set to False.""" + """Test application of PennyLane operations with non-analytic calculation.""" def test_qubit_state_vector(self, init_state, device, tol): """Test that the QubitStateVector operation produces the expected @@ -302,4 +298,3 @@ def test_three_qubit_no_parameters(self, init_state, device, operation, tol): res = np.fromiter(dev.probability(), dtype=np.float64) expected = np.abs(applied_operation.matrix @ state) ** 2 assert np.allclose(res, expected, **tol) - diff --git a/tests/test_converter.py b/tests/test_converter.py index 0aa838f35..41f394325 100644 --- a/tests/test_converter.py +++ b/tests/test_converter.py @@ -21,11 +21,11 @@ class TestConverter: """Tests the converter function that allows converting QuantumCircuit objects - to Pennylane templates.""" + to Pennylane templates.""" def test_quantum_circuit_init_by_specifying_rotation_in_circuit(self, recorder): """Tests the load method for a QuantumCircuit initialized using separately defined - quantum and classical registers.""" + quantum and classical registers.""" angle = 0.5 @@ -41,7 +41,7 @@ def test_quantum_circuit_init_by_specifying_rotation_in_circuit(self, recorder): quantum_circuit() assert len(recorder.queue) == 1 - assert recorder.queue[0].name == 'RZ' + assert recorder.queue[0].name == "RZ" assert recorder.queue[0].parameters == [angle] assert recorder.queue[0].wires == Wires([0]) @@ -49,7 +49,7 @@ def test_quantum_circuit_by_passing_parameters(self, recorder): """Tests the load method for a QuantumCircuit initialized by passing the number of registers required.""" - theta = Parameter('θ') + theta = Parameter("θ") angle = 0.5 qc = QuantumCircuit(3, 1) @@ -61,7 +61,7 @@ def test_quantum_circuit_by_passing_parameters(self, recorder): quantum_circuit(params={theta: angle}) assert len(recorder.queue) == 1 - assert recorder.queue[0].name == 'RZ' + assert recorder.queue[0].name == "RZ" assert recorder.queue[0].parameters == [angle] assert recorder.queue[0].wires == Wires([0]) @@ -69,7 +69,7 @@ def test_loaded_quantum_circuit_and_further_pennylane_operations(self, recorder) """Tests that a loaded quantum circuit can be used around other PennyLane templates in a circuit.""" - theta = Parameter('θ') + theta = Parameter("θ") angle = 0.5 qc = QuantumCircuit(3, 1) @@ -83,13 +83,13 @@ def test_loaded_quantum_circuit_and_further_pennylane_operations(self, recorder) qml.Hadamard(0) assert len(recorder.queue) == 3 - assert recorder.queue[0].name == 'PauliZ' + assert recorder.queue[0].name == "PauliZ" assert recorder.queue[0].parameters == [] assert recorder.queue[0].wires == Wires([0]) - assert recorder.queue[1].name == 'RZ' + assert recorder.queue[1].name == "RZ" assert recorder.queue[1].parameters == [angle] assert recorder.queue[1].wires == Wires([0]) - assert recorder.queue[2].name == 'Hadamard' + assert recorder.queue[2].name == "Hadamard" assert recorder.queue[2].parameters == [] assert recorder.queue[2].wires == Wires([0]) @@ -99,8 +99,8 @@ def test_quantum_circuit_with_multiple_parameters(self, recorder): angle1 = 0.5 angle2 = 0.3 - phi = Parameter('φ') - theta = Parameter('θ') + phi = Parameter("φ") + theta = Parameter("θ") qc = QuantumCircuit(3, 1) qc.rx(phi, 1) @@ -112,10 +112,10 @@ def test_quantum_circuit_with_multiple_parameters(self, recorder): quantum_circuit(params={phi: angle1, theta: angle2}) assert len(recorder.queue) == 2 - assert recorder.queue[0].name == 'RX' + assert recorder.queue[0].name == "RX" assert recorder.queue[0].parameters == [angle1] assert recorder.queue[0].wires == Wires([1]) - assert recorder.queue[1].name == 'RZ' + assert recorder.queue[1].name == "RZ" assert recorder.queue[1].parameters == [angle2] assert recorder.queue[1].wires == Wires([0]) @@ -127,9 +127,9 @@ def test_quantum_circuit_with_gate_requiring_multiple_parameters(self, recorder) angle2 = 0.3 angle3 = 0.1 - phi = Parameter('φ') - lam = Parameter('λ') - theta = Parameter('θ') + phi = Parameter("φ") + lam = Parameter("λ") + theta = Parameter("θ") qc = QuantumCircuit(3, 1) qc.u(phi, lam, theta, [0]) @@ -139,7 +139,7 @@ def test_quantum_circuit_with_gate_requiring_multiple_parameters(self, recorder) with recorder: quantum_circuit(params={phi: angle1, lam: angle2, theta: angle3}) - assert recorder.queue[0].name == 'U3' + assert recorder.queue[0].name == "U3" assert len(recorder.queue[0].parameters) == 3 assert recorder.queue[0].parameters == [0.5, 0.3, 0.1] assert recorder.queue[0].wires == Wires([0]) @@ -148,7 +148,7 @@ def test_quantum_circuit_loaded_multiple_times_with_different_arguments(self, re """Tests that a loaded quantum circuit can be called multiple times with different arguments.""" - theta = Parameter('θ') + theta = Parameter("θ") angle1 = 0.5 angle2 = -0.5 angle3 = 0 @@ -164,20 +164,20 @@ def test_quantum_circuit_loaded_multiple_times_with_different_arguments(self, re quantum_circuit(params={theta: angle3}) assert len(recorder.queue) == 3 - assert recorder.queue[0].name == 'RZ' + assert recorder.queue[0].name == "RZ" assert recorder.queue[0].parameters == [angle1] assert recorder.queue[0].wires == Wires([0]) - assert recorder.queue[1].name == 'RZ' + assert recorder.queue[1].name == "RZ" assert recorder.queue[1].parameters == [angle2] assert recorder.queue[1].wires == Wires([0]) - assert recorder.queue[2].name == 'RZ' + assert recorder.queue[2].name == "RZ" assert recorder.queue[2].parameters == [angle3] assert recorder.queue[2].wires == Wires([0]) def test_quantum_circuit_with_bound_parameters(self, recorder): """Tests loading a quantum circuit that already had bound parameters.""" - theta = Parameter('θ') + theta = Parameter("θ") qc = QuantumCircuit(3, 1) qc.rz(theta, [0]) @@ -189,14 +189,14 @@ def test_quantum_circuit_with_bound_parameters(self, recorder): quantum_circuit() assert len(recorder.queue) == 1 - assert recorder.queue[0].name == 'RZ' + assert recorder.queue[0].name == "RZ" assert recorder.queue[0].parameters == [0.5] assert recorder.queue[0].wires == Wires([0]) def test_pass_parameters_to_bind(self, recorder): """Tests parameter binding by passing parameters when loading a quantum circuit.""" - theta = Parameter('θ') + theta = Parameter("θ") qc = QuantumCircuit(3, 1) qc.rz(theta, [0]) @@ -207,37 +207,38 @@ def test_pass_parameters_to_bind(self, recorder): quantum_circuit(params={theta: 0.5}) assert len(recorder.queue) == 1 - assert recorder.queue[0].name == 'RZ' + assert recorder.queue[0].name == "RZ" assert recorder.queue[0].parameters == [0.5] assert recorder.queue[0].wires == Wires([0]) - def test_parameter_was_not_bound(self, recorder): """Tests that loading raises an error when parameters were not bound.""" - theta = Parameter('θ') + theta = Parameter("θ") qc = QuantumCircuit(3, 1) qc.rz(theta, [0]) quantum_circuit = load(qc) - with pytest.raises(ValueError, match='The parameter {} was not bound correctly.'.format(theta)): + with pytest.raises( + ValueError, match="The parameter {} was not bound correctly.".format(theta) + ): with recorder: quantum_circuit(params={}) def test_invalid_parameter_expression(self, recorder): """Tests that an operation with multiple parameters raises an error.""" - theta = Parameter('θ') - phi = Parameter('φ') + theta = Parameter("θ") + phi = Parameter("φ") qc = QuantumCircuit(3, 1) - qc.rz(theta*phi, [0]) + qc.rz(theta * phi, [0]) quantum_circuit = load(qc) - with pytest.raises(ValueError, match='PennyLane does not support expressions'): + with pytest.raises(ValueError, match="PennyLane does not support expressions"): with recorder: quantum_circuit(params={theta: 0, phi: 1}) @@ -245,8 +246,8 @@ def test_extra_parameters_were_passed(self, recorder): """Tests that loading raises an error when extra parameters were passed.""" - theta = Parameter('θ') - phi = Parameter('φ') + theta = Parameter("θ") + phi = Parameter("φ") x = np.tensor(0.5, requires_grad=False) y = np.tensor(0.3, requires_grad=False) @@ -258,7 +259,10 @@ def test_extra_parameters_were_passed(self, recorder): with recorder: quantum_circuit(params={theta: x, phi: y}) - @pytest.mark.parametrize("qiskit_operation, pennylane_name", [(QuantumCircuit.crx, "CRX"), (QuantumCircuit.crz, "CRZ"), (QuantumCircuit.cry, "CRY")]) + @pytest.mark.parametrize( + "qiskit_operation, pennylane_name", + [(QuantumCircuit.crx, "CRX"), (QuantumCircuit.crz, "CRZ"), (QuantumCircuit.cry, "CRY")], + ) def test_controlled_rotations(self, qiskit_operation, pennylane_name, recorder): """Tests loading a circuit with two qubit controlled rotations (except for CRY).""" @@ -268,7 +272,6 @@ def test_controlled_rotations(self, qiskit_operation, pennylane_name, recorder): qiskit_operation(qc, 0.5, q2[0], q2[1]) - quantum_circuit = load(qc) with recorder: @@ -298,27 +301,27 @@ def test_one_qubit_operations_supported_by_pennylane(self, recorder): assert len(recorder.queue) == 6 - assert recorder.queue[0].name == 'PauliX' + assert recorder.queue[0].name == "PauliX" assert recorder.queue[0].parameters == [] assert recorder.queue[0].wires == Wires(single_wire) - assert recorder.queue[1].name == 'PauliY' + assert recorder.queue[1].name == "PauliY" assert recorder.queue[1].parameters == [] assert recorder.queue[1].wires == Wires(single_wire) - assert recorder.queue[2].name == 'PauliZ' + assert recorder.queue[2].name == "PauliZ" assert recorder.queue[2].parameters == [] assert recorder.queue[2].wires == Wires(single_wire) - assert recorder.queue[3].name == 'Hadamard' + assert recorder.queue[3].name == "Hadamard" assert recorder.queue[3].parameters == [] assert recorder.queue[3].wires == Wires(single_wire) - assert recorder.queue[4].name == 'S' + assert recorder.queue[4].name == "S" assert recorder.queue[4].parameters == [] assert recorder.queue[4].wires == Wires(single_wire) - assert recorder.queue[5].name == 'T' + assert recorder.queue[5].name == "T" assert recorder.queue[5].parameters == [] assert recorder.queue[5].wires == Wires(single_wire) @@ -345,23 +348,23 @@ def test_one_qubit_parametrized_operations_supported_by_pennylane(self, recorder with recorder: quantum_circuit() - assert recorder.queue[0].name == 'PhaseShift' + assert recorder.queue[0].name == "PhaseShift" assert recorder.queue[0].parameters == [angle] assert recorder.queue[0].wires == Wires(single_wire) - assert recorder.queue[1].name == 'RX' + assert recorder.queue[1].name == "RX" assert recorder.queue[1].parameters == [angle] assert recorder.queue[1].wires == Wires(single_wire) - assert recorder.queue[2].name == 'RY' + assert recorder.queue[2].name == "RY" assert recorder.queue[2].parameters == [angle] assert recorder.queue[2].wires == Wires(single_wire) - assert recorder.queue[3].name == 'RZ' + assert recorder.queue[3].name == "RZ" assert recorder.queue[3].parameters == [angle] assert recorder.queue[3].wires == Wires(single_wire) - assert recorder.queue[4].name == 'U3' + assert recorder.queue[4].name == "U3" assert len(recorder.queue[4].parameters) == 3 assert recorder.queue[4].parameters == [0.3, 0.4, 0.2] assert recorder.queue[4].wires == Wires([0]) @@ -373,17 +376,13 @@ def test_two_qubit_operations_supported_by_pennylane(self, recorder): qc = QuantumCircuit(2, 1) - unitary_op = [[1, 0, 0, 0], - [0, 0, 1j, 0], - [0, 1j, 0, 0], - [0, 0, 0, 1]] + unitary_op = [[1, 0, 0, 0], [0, 0, 1j, 0], [0, 1j, 0, 0], [0, 0, 0, 1]] iswap_op = Operator(unitary_op) - qc.cx(*two_wires) qc.cz(*two_wires) qc.swap(*two_wires) - qc.unitary(iswap_op, [0, 1], label='iswap') + qc.unitary(iswap_op, [0, 1], label="iswap") quantum_circuit = load(qc) with recorder: @@ -391,19 +390,19 @@ def test_two_qubit_operations_supported_by_pennylane(self, recorder): assert len(recorder.queue) == 4 - assert recorder.queue[0].name == 'CNOT' + assert recorder.queue[0].name == "CNOT" assert recorder.queue[0].parameters == [] assert recorder.queue[0].wires == Wires(two_wires) - assert recorder.queue[1].name == 'CZ' + assert recorder.queue[1].name == "CZ" assert recorder.queue[1].parameters == [] assert recorder.queue[1].wires == Wires(two_wires) - assert recorder.queue[2].name == 'SWAP' + assert recorder.queue[2].name == "SWAP" assert recorder.queue[2].parameters == [] assert recorder.queue[2].wires == Wires(two_wires) - assert recorder.queue[3].name == 'QubitUnitary' + assert recorder.queue[3].name == "QubitUnitary" assert len(recorder.queue[3].parameters) == 1 assert np.array_equal(recorder.queue[3].parameters[0], np.array(unitary_op)) assert recorder.queue[3].wires == Wires(two_wires) @@ -424,7 +423,7 @@ def test_two_qubit_parametrized_operations_supported_by_pennylane(self, recorder assert len(recorder.queue) == 1 - assert recorder.queue[0].name == 'CRZ' + assert recorder.queue[0].name == "CRZ" assert recorder.queue[0].parameters == [angle] assert recorder.queue[0].wires == Wires(two_wires) @@ -441,11 +440,11 @@ def test_three_qubit_operations_supported_by_pennylane(self, recorder): with recorder: quantum_circuit() - assert recorder.queue[0].name == 'CSWAP' + assert recorder.queue[0].name == "CSWAP" assert recorder.queue[0].parameters == [] assert recorder.queue[0].wires == Wires(three_wires) - assert recorder.queue[1].name == 'Toffoli' + assert recorder.queue[1].name == "Toffoli" assert len(recorder.queue[1].parameters) == 0 assert recorder.queue[1].wires == Wires(three_wires) @@ -465,7 +464,7 @@ def test_wires_two_different_quantum_registers(self, recorder): with recorder: quantum_circuit() - assert recorder.queue[0].name == 'CSWAP' + assert recorder.queue[0].name == "CSWAP" assert recorder.queue[0].parameters == [] assert recorder.queue[0].wires == Wires(three_wires) @@ -486,7 +485,7 @@ def test_wires_quantum_circuit_init_with_two_different_quantum_registers(self, r with recorder: quantum_circuit(wires=three_wires) - assert recorder.queue[0].name == 'CSWAP' + assert recorder.queue[0].name == "CSWAP" assert recorder.queue[0].parameters == [] assert recorder.queue[0].wires == Wires(three_wires) @@ -506,7 +505,7 @@ def test_wires_pass_different_wires_than_for_circuit(self, recorder): with recorder: quantum_circuit(wires=three_wires) - assert recorder.queue[0].name == 'CSWAP' + assert recorder.queue[0].name == "CSWAP" assert recorder.queue[0].parameters == [] assert recorder.queue[0].wires == Wires(three_wires) @@ -522,17 +521,17 @@ def test_operations_sdg_and_tdg(self, recorder): with recorder: quantum_circuit() - assert recorder.queue[0].name == 'S.inv' + assert recorder.queue[0].name == "S.inv" assert len(recorder.queue[0].parameters) == 0 assert recorder.queue[0].wires == Wires([0]) - assert recorder.queue[1].name == 'T.inv' + assert recorder.queue[1].name == "T.inv" assert len(recorder.queue[1].parameters) == 0 assert recorder.queue[1].wires == Wires([0]) def test_operation_transformed_into_qubit_unitary(self, recorder): """Tests loading a circuit with operations that can be converted, - but not natively supported by PennyLane.""" + but not natively supported by PennyLane.""" qc = QuantumCircuit(3, 1) @@ -542,14 +541,14 @@ def test_operation_transformed_into_qubit_unitary(self, recorder): with recorder: quantum_circuit() - assert recorder.queue[0].name == 'QubitUnitary' + assert recorder.queue[0].name == "QubitUnitary" assert len(recorder.queue[0].parameters) == 1 assert np.array_equal(recorder.queue[0].parameters[0], ex.CHGate().to_matrix()) assert recorder.queue[0].wires == Wires([0, 1]) def test_qiskit_gates_to_be_deprecated(self, recorder): """Tests the Qiskit gates that will be deprecated in an upcoming Qiskit version. - + This test case can be removed once the gates are finally deprecated. """ qc = QuantumCircuit(1, 1) @@ -574,15 +573,15 @@ def test_qiskit_gates_to_be_deprecated(self, recorder): with recorder: quantum_circuit() - assert recorder.queue[0].name == 'U1' + assert recorder.queue[0].name == "U1" assert recorder.queue[0].parameters == [0.1] assert recorder.queue[0].wires == Wires(single_wire) - assert recorder.queue[1].name == 'U2' + assert recorder.queue[1].name == "U2" assert recorder.queue[1].parameters == [0.1, 0.2] assert recorder.queue[1].wires == Wires(single_wire) - assert recorder.queue[2].name == 'U3' + assert recorder.queue[2].name == "U3" assert recorder.queue[2].parameters == [0.1, 0.2, 0.3] assert recorder.queue[2].wires == Wires(single_wire) @@ -606,7 +605,7 @@ def test_quantum_circuit_error_passing_parameters_not_required(self, recorder): """Tests the load method raises a QiskitError if arguments that are not required were passed.""" - theta = Parameter('θ') + theta = Parameter("θ") angle = np.tensor(0.5, requires_grad=False) qc = QuantumCircuit(3, 1) @@ -622,14 +621,16 @@ def test_quantum_circuit_error_parameter_not_bound(self, recorder): """Tests the load method for a QuantumCircuit raises a ValueError, if one of the parameters was not bound correctly.""" - theta = Parameter('θ') + theta = Parameter("θ") qc = QuantumCircuit(3, 1) qc.rz(theta, [0]) quantum_circuit = load(qc) - with pytest.raises(ValueError, match="The parameter {} was not bound correctly.".format(theta)): + with pytest.raises( + ValueError, match="The parameter {} was not bound correctly.".format(theta) + ): with recorder: quantum_circuit() @@ -660,9 +661,11 @@ def test_wires_error_too_few_wires_specified(self, recorder): quantum_circuit = load(qc) - with pytest.raises(qml.QuantumFunctionError, match='The specified number of wires - {} - does not match the' - ' number of wires the loaded quantum circuit acts on.'. - format(len(only_two_wires))): + with pytest.raises( + qml.QuantumFunctionError, + match="The specified number of wires - {} - does not match the" + " number of wires the loaded quantum circuit acts on.".format(len(only_two_wires)), + ): with recorder: quantum_circuit(wires=only_two_wires) @@ -681,9 +684,13 @@ def test_wires_error_too_many_wires_specified(self, recorder): quantum_circuit = load(qc) - with pytest.raises(qml.QuantumFunctionError, match="The specified number of wires - {} - does not match the" - " number of wires the loaded quantum circuit acts on.". - format(len(more_than_three_wires))): + with pytest.raises( + qml.QuantumFunctionError, + match="The specified number of wires - {} - does not match the" + " number of wires the loaded quantum circuit acts on.".format( + len(more_than_three_wires) + ), + ): with recorder: quantum_circuit(wires=more_than_three_wires) @@ -698,7 +705,7 @@ def test_map_wires(self, recorder): qc = QuantumCircuit(1) qc_wires = [(q.register.name, q.index) for q in qc.qubits] - assert map_wires(qc_wires, wires) == {0: ('q', 0)} + assert map_wires(qc_wires, wires) == {0: ("q", 0)} def test_map_wires_instantiate_quantum_circuit_with_registers(self, recorder): """Tests the map_wires function for wires of a quantum circuit instantiated @@ -743,9 +750,11 @@ def test_map_wires_exception_mismatch_in_number_of_wires(self, recorder): qc = QuantumCircuit(1) qc_wires = [(q.register.name, q.index) for q in qc.qubits] - with pytest.raises(qml.QuantumFunctionError, match='The specified number of wires - {} - does not match ' - 'the number of wires the loaded quantum circuit acts on.' - .format(len(wires))): + with pytest.raises( + qml.QuantumFunctionError, + match="The specified number of wires - {} - does not match " + "the number of wires the loaded quantum circuit acts on.".format(len(wires)), + ): map_wires(wires, qc_wires) @@ -766,25 +775,30 @@ def test_barrier_not_supported(self, recorder): # check that only one warning was raised assert len(record) == 1 # check that the message matches - assert record[0].message.args[0] == "pennylane_qiskit.converter: The Barrier instruction is not supported by" \ - " PennyLane, and has not been added to the template." + assert ( + record[0].message.args[0] + == "pennylane_qiskit.converter: The Barrier instruction is not supported by" + " PennyLane, and has not been added to the template." + ) class TestConverterQasm: """Tests that the converter.load function allows conversion from qasm.""" - qft_qasm = 'OPENQASM 2.0;' \ - 'include "qelib1.inc";' \ - 'qreg q[4];' \ - 'creg c[4];' \ - 'x q[0]; ' \ - 'x q[2];' \ - 'barrier q;' \ - 'h q[0];' \ - 'h q[1];' \ - 'h q[2];' \ - 'h q[3];' \ - 'measure q -> c;' + qft_qasm = ( + "OPENQASM 2.0;" + 'include "qelib1.inc";' + "qreg q[4];" + "creg c[4];" + "x q[0]; " + "x q[2];" + "barrier q;" + "h q[0];" + "h q[1];" + "h q[2];" + "h q[3];" + "measure q -> c;" + ) @pytest.mark.skipif(sys.version_info < (3, 6), reason="tmpdir fixture requires Python >=3.6") def test_qasm_from_file(self, tmpdir, recorder): @@ -802,54 +816,60 @@ def test_qasm_from_file(self, tmpdir, recorder): assert len(recorder.queue) == 6 - assert recorder.queue[0].name == 'PauliX' + assert recorder.queue[0].name == "PauliX" assert recorder.queue[0].parameters == [] assert recorder.queue[0].wires == Wires([0]) - assert recorder.queue[1].name == 'PauliX' + assert recorder.queue[1].name == "PauliX" assert recorder.queue[1].parameters == [] assert recorder.queue[1].wires == Wires([2]) - assert recorder.queue[2].name == 'Hadamard' + assert recorder.queue[2].name == "Hadamard" assert recorder.queue[2].parameters == [] assert recorder.queue[2].wires == Wires([0]) - assert recorder.queue[3].name == 'Hadamard' + assert recorder.queue[3].name == "Hadamard" assert recorder.queue[3].parameters == [] assert recorder.queue[3].wires == Wires([1]) - assert recorder.queue[4].name == 'Hadamard' + assert recorder.queue[4].name == "Hadamard" assert recorder.queue[4].parameters == [] assert recorder.queue[4].wires == Wires([2]) - assert recorder.queue[5].name == 'Hadamard' + assert recorder.queue[5].name == "Hadamard" assert recorder.queue[5].parameters == [] assert recorder.queue[5].wires == Wires([3]) assert len(record) == 5 # check that the message matches - assert record[0].message.args[0] == "pennylane_qiskit.converter: The {} instruction is not supported by" \ - " PennyLane, and has not been added to the template."\ - .format('Barrier') - assert record[1].message.args[0] == "pennylane_qiskit.converter: The {} instruction is not supported by" \ - " PennyLane, and has not been added to the template."\ - .format('Measure') + assert record[0].message.args[ + 0 + ] == "pennylane_qiskit.converter: The {} instruction is not supported by" " PennyLane, and has not been added to the template.".format( + "Barrier" + ) + assert record[1].message.args[ + 0 + ] == "pennylane_qiskit.converter: The {} instruction is not supported by" " PennyLane, and has not been added to the template.".format( + "Measure" + ) def test_qasm_file_not_found_error(self): """Tests that an error is propagated, when a non-existing file is specified for parsing.""" - qft_qasm = 'some_qasm_file.qasm' + qft_qasm = "some_qasm_file.qasm" with pytest.raises(FileNotFoundError): load_qasm_from_file(qft_qasm) def test_qasm_(self, recorder): """Tests that a QuantumCircuit object is deserialized from a qasm string.""" - qasm_string = 'include "qelib1.inc";' \ - 'qreg q[4];' \ - 'creg c[4];' \ - 'x q[0];' \ - 'cx q[2],q[0];'\ - 'measure q -> c;' + qasm_string = ( + 'include "qelib1.inc";' + "qreg q[4];" + "creg c[4];" + "x q[0];" + "cx q[2],q[0];" + "measure q -> c;" + ) quantum_circuit = load_qasm(qasm_string) @@ -859,17 +879,16 @@ def test_qasm_(self, recorder): assert len(recorder.queue) == 2 - assert recorder.queue[0].name == 'PauliX' + assert recorder.queue[0].name == "PauliX" assert recorder.queue[0].parameters == [] assert recorder.queue[0].wires == Wires([0]) - assert recorder.queue[1].name == 'CNOT' + assert recorder.queue[1].name == "CNOT" assert recorder.queue[1].parameters == [] assert recorder.queue[1].wires == Wires([2, 0]) class TestConverterIntegration: - def test_use_loaded_circuit_in_qnode(self, qubit_device_2_wires): """Tests loading a converted template in a QNode.""" @@ -896,7 +915,7 @@ def test_load_circuit_inside_of_qnode(self, qubit_device_2_wires): """Tests loading a QuantumCircuit inside of the QNode circuit definition.""" - theta = Parameter('θ') + theta = Parameter("θ") angle = 0.5 qc = QuantumCircuit(2) @@ -912,13 +931,12 @@ def circuit_native_pennylane(): qml.RZ(angle, wires=0) return qml.expval(qml.PauliZ(0)) - assert circuit_loaded_qiskit_circuit() == \ - circuit_native_pennylane() + assert circuit_loaded_qiskit_circuit() == circuit_native_pennylane() def test_passing_parameter_into_qnode(self, qubit_device_2_wires): """Tests passing a circuit parameter into the QNode.""" - theta = Parameter('θ') + theta = Parameter("θ") rotation_angle = 0.5 qc = QuantumCircuit(2) @@ -934,15 +952,16 @@ def circuit_native_pennylane(angle): qml.RZ(angle, wires=0) return qml.expval(qml.PauliZ(0)) - assert circuit_loaded_qiskit_circuit(rotation_angle) == \ - circuit_native_pennylane(rotation_angle) + assert circuit_loaded_qiskit_circuit(rotation_angle) == circuit_native_pennylane( + rotation_angle + ) def test_one_parameter_in_qc_one_passed_into_qnode(self, qubit_device_2_wires): """Tests passing a parameter by pre-defining it and then - passing another to the QNode.""" + passing another to the QNode.""" - theta = Parameter('θ') - phi = Parameter('φ') + theta = Parameter("θ") + phi = Parameter("φ") rotation_angle1 = 0.5 rotation_angle2 = 0.3 @@ -961,13 +980,14 @@ def circuit_native_pennylane(angle): qml.RX(rotation_angle2, wires=0) return qml.expval(qml.PauliZ(0)) - assert circuit_loaded_qiskit_circuit(rotation_angle1) == \ - circuit_native_pennylane(rotation_angle1) + assert circuit_loaded_qiskit_circuit(rotation_angle1) == circuit_native_pennylane( + rotation_angle1 + ) def test_initialize_with_qubit_state_vector(self, qubit_device_single_wire): """Tests the QuantumCircuit.initialize method in a QNode.""" - prob_amplitudes = [1/np.sqrt(2), 1/np.sqrt(2)] + prob_amplitudes = [1 / np.sqrt(2), 1 / np.sqrt(2)] qreg = QuantumRegister(2) qc = QuantumCircuit(qreg) @@ -985,9 +1005,9 @@ def circuit_native_pennylane(): assert circuit_loaded_qiskit_circuit() == circuit_native_pennylane() - @pytest.mark.parametrize("analytic", [True]) + @pytest.mark.parametrize("shots", [None]) @pytest.mark.parametrize("theta,phi,varphi", list(zip(THETA, PHI, VARPHI))) - def test_gradient(self, theta, phi, varphi, analytic, tol): + def test_gradient(self, theta, phi, varphi, shots, tol): """Test that the gradient works correctly""" qc = QuantumCircuit(3) qiskit_params = [Parameter("param_{}".format(i)) for i in range(3)] @@ -1001,7 +1021,7 @@ def test_gradient(self, theta, phi, varphi, analytic, tol): # convert to a PennyLane circuit qc_pl = qml.from_qiskit(qc) - dev = qml.device("default.qubit", wires=3, analytic=analytic) + dev = qml.device("default.qubit", wires=3, shots=shots) @qml.qnode(dev) def circuit(params): @@ -1014,13 +1034,13 @@ def circuit(params): expected = [ np.cos(theta) * np.sin(phi) * np.sin(varphi), np.sin(theta) * np.cos(phi) * np.sin(varphi), - np.sin(theta) * np.sin(phi) * np.cos(varphi) + np.sin(theta) * np.sin(phi) * np.cos(varphi), ] assert np.allclose(res, expected, **tol) - @pytest.mark.parametrize("analytic", [True]) - def test_differentiable_param_is_array(self, analytic, tol): + @pytest.mark.parametrize("shots", [None]) + def test_differentiable_param_is_array(self, shots, tol): """Test that extracting the differentiable parameters works correctly for arrays""" qc = QuantumCircuit(3) @@ -1040,7 +1060,7 @@ def test_differentiable_param_is_array(self, analytic, tol): # convert to a PennyLane circuit qc_pl = qml.from_qiskit(qc) - dev = qml.device("default.qubit", wires=3, analytic=analytic) + dev = qml.device("default.qubit", wires=3, shots=shots) @qml.qnode(dev) def circuit(params): @@ -1053,7 +1073,7 @@ def circuit(params): expected = [ np.cos(theta) * np.sin(phi) * np.sin(varphi), np.sin(theta) * np.cos(phi) * np.sin(varphi), - np.sin(theta) * np.sin(phi) * np.cos(varphi) + np.sin(theta) * np.sin(phi) * np.cos(varphi), ] assert np.allclose(res, expected, **tol) diff --git a/tests/test_expval.py b/tests/test_expval.py index df396e799..d81dd682c 100644 --- a/tests/test_expval.py +++ b/tests/test_expval.py @@ -14,8 +14,7 @@ @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) -@pytest.mark.parametrize("analytic", [True, False]) -@pytest.mark.parametrize("shots", [8192]) +@pytest.mark.parametrize("shots", [None, 8192]) @pytest.mark.usefixtures("skip_unitary") class TestExpval: """Test expectation values""" @@ -28,12 +27,8 @@ def test_identity_expectation(self, theta, phi, device, shots, tol): O2 = qml.Identity(wires=[1]) dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.CNOT(wires=[0, 1]) - ], - rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()] + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], ) dev._samples = dev.generate_samples() @@ -48,12 +43,8 @@ def test_pauliz_expectation(self, theta, phi, device, shots, tol): O2 = qml.PauliZ(wires=[1]) dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.CNOT(wires=[0, 1]) - ], - rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()] + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], ) dev._samples = dev.generate_samples() @@ -68,12 +59,8 @@ def test_paulix_expectation(self, theta, phi, device, shots, tol): O2 = qml.PauliX(wires=[1]) dev.apply( - [ - qml.RY(theta, wires=[0]), - qml.RY(phi, wires=[1]), - qml.CNOT(wires=[0, 1]) - ], - rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()] + [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], ) dev._samples = dev.generate_samples() @@ -88,12 +75,8 @@ def test_pauliy_expectation(self, theta, phi, device, shots, tol): O2 = qml.PauliY(wires=[1]) dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.CNOT(wires=[0, 1]) - ], - rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()] + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], ) dev._samples = dev.generate_samples() @@ -108,12 +91,8 @@ def test_hadamard_expectation(self, theta, phi, device, shots, tol): O2 = qml.Hadamard(wires=[1]) dev.apply( - [ - qml.RY(theta, wires=[0]), - qml.RY(phi, wires=[1]), - qml.CNOT(wires=[0, 1]) - ], - rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()] + [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], ) dev._samples = dev.generate_samples() @@ -131,12 +110,8 @@ def test_hermitian_expectation(self, theta, phi, device, shots, tol): O2 = qml.Hermitian(A, wires=[1]) dev.apply( - [ - qml.RY(theta, wires=[0]), - qml.RY(phi, wires=[1]), - qml.CNOT(wires=[0, 1]) - ], - rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()] + [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], ) dev._samples = dev.generate_samples() @@ -167,12 +142,8 @@ def test_multi_qubit_hermitian_expectation(self, theta, phi, device, shots, tol) O1 = qml.Hermitian(A, wires=[0, 1]) dev.apply( - [ - qml.RY(theta, wires=[0]), - qml.RY(phi, wires=[1]), - qml.CNOT(wires=[0, 1]) - ], - rotations=[*O1.diagonalizing_gates()] + [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + rotations=[*O1.diagonalizing_gates()], ) dev._samples = dev.generate_samples() @@ -192,8 +163,7 @@ def test_multi_qubit_hermitian_expectation(self, theta, phi, device, shots, tol) @pytest.mark.parametrize("theta,phi,varphi", list(zip(THETA, PHI, VARPHI))) -@pytest.mark.parametrize("analytic", [True, False]) -@pytest.mark.parametrize("shots", [8192]) +@pytest.mark.parametrize("shots", [None, 8192]) class TestTensorExpval: """Test tensor expectation values""" @@ -209,9 +179,9 @@ def test_paulix_pauliy(self, theta, phi, varphi, device, shots, tol): qml.RX(phi, wires=[1]), qml.RX(varphi, wires=[2]), qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]) + qml.CNOT(wires=[1, 2]), ], - rotations=obs.diagonalizing_gates() + rotations=obs.diagonalizing_gates(), ) dev._samples = dev.generate_samples() @@ -233,15 +203,15 @@ def test_pauliz_identity(self, theta, phi, varphi, device, shots, tol): qml.RX(phi, wires=[1]), qml.RX(varphi, wires=[2]), qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]) + qml.CNOT(wires=[1, 2]), ], - rotations=obs.diagonalizing_gates() + rotations=obs.diagonalizing_gates(), ) dev._samples = dev.generate_samples() res = dev.expval(obs) - expected = np.cos(varphi)*np.cos(phi) + expected = np.cos(varphi) * np.cos(phi) assert np.allclose(res, expected, **tol) @@ -257,9 +227,9 @@ def test_pauliz_hadamard_pauliy(self, theta, phi, varphi, device, shots, tol): qml.RX(phi, wires=[1]), qml.RX(varphi, wires=[2]), qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]) + qml.CNOT(wires=[1, 2]), ], - rotations=obs.diagonalizing_gates() + rotations=obs.diagonalizing_gates(), ) dev._samples = dev.generate_samples() @@ -289,9 +259,9 @@ def test_hermitian(self, theta, phi, varphi, device, shots, tol): qml.RX(phi, wires=[1]), qml.RX(varphi, wires=[2]), qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]) + qml.CNOT(wires=[1, 2]), ], - rotations=obs.diagonalizing_gates() + rotations=obs.diagonalizing_gates(), ) dev._samples = dev.generate_samples() @@ -309,8 +279,7 @@ def test_hermitian_hermitian(self, theta, phi, varphi, device, shots, tol): """Test that a tensor product involving two Hermitian matrices works correctly""" dev = device(3) - A1 = np.array([[1, 2], - [2, 4]]) + A1 = np.array([[1, 2], [2, 4]]) A2 = np.array( [ @@ -328,9 +297,9 @@ def test_hermitian_hermitian(self, theta, phi, varphi, device, shots, tol): qml.RX(phi, wires=[1]), qml.RX(varphi, wires=[2]), qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]) + qml.CNOT(wires=[1, 2]), ], - rotations=obs.diagonalizing_gates() + rotations=obs.diagonalizing_gates(), ) dev._samples = dev.generate_samples() @@ -340,7 +309,8 @@ def test_hermitian_hermitian(self, theta, phi, varphi, device, shots, tol): + 4 * np.cos(phi) * np.sin(theta) + 3 * np.cos(varphi) * (-10 + 4 * np.cos(phi) * np.sin(theta) - 3 * np.sin(phi)) - 3 * np.sin(phi) - - 2 * (5 + np.cos(phi) * (6 + 4 * np.sin(theta)) + (-3 + 8 * np.sin(theta)) * np.sin(phi)) + - 2 + * (5 + np.cos(phi) * (6 + 4 * np.sin(theta)) + (-3 + 8 * np.sin(theta)) * np.sin(phi)) * np.sin(varphi) + np.cos(theta) * ( @@ -366,7 +336,7 @@ def test_hermitian_identity_expectation(self, theta, phi, varphi, device, shots, qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1]), ], - rotations=obs.diagonalizing_gates() + rotations=obs.diagonalizing_gates(), ) dev._samples = dev.generate_samples() diff --git a/tests/test_ibmq.py b/tests/test_ibmq.py index 2c4eb2486..c3ab7558d 100644 --- a/tests/test_ibmq.py +++ b/tests/test_ibmq.py @@ -30,6 +30,7 @@ def test_load_from_env(token, monkeypatch): dev = IBMQDevice(wires=1) assert dev.provider.credentials.is_ibmq() + def test_load_kwargs_takes_precedence(token, monkeypatch): """Test that with a potentially valid token stored as an environment variable, passing the token as a keyword argument takes precedence.""" @@ -37,6 +38,7 @@ def test_load_kwargs_takes_precedence(token, monkeypatch): dev = IBMQDevice(wires=1, ibmqx_token=token) assert dev.provider.credentials.is_ibmq() + def test_account_already_loaded(token): """Test loading an IBMQ device using an already loaded account""" @@ -44,6 +46,7 @@ def test_account_already_loaded(token): dev = IBMQDevice(wires=1) assert dev.provider.credentials.is_ibmq() + class MockQiskitDeviceInit: """A mocked version of the QiskitDevice __init__ method which is called on by the IBMQDevice""" @@ -53,6 +56,7 @@ def mocked_init(self, wires, provider, backend, shots, **kwargs): called with.""" self.provider = provider + def test_custom_provider(monkeypatch): """Tests that a custom provider can be passed when creating an IBMQ device.""" @@ -60,20 +64,22 @@ def test_custom_provider(monkeypatch): mock_qiskit_device = MockQiskitDeviceInit() with monkeypatch.context() as m: - m.setattr(ibmq.QiskitDevice, "__init__",mock_qiskit_device.mocked_init) + m.setattr(ibmq.QiskitDevice, "__init__", mock_qiskit_device.mocked_init) m.setattr(ibmq.IBMQ, "enable_account", lambda *args, **kwargs: None) # Here mocking to a value such that it is not None m.setattr(ibmq.IBMQ, "active_account", lambda *args, **kwargs: True) - dev = IBMQDevice(wires=2, backend='ibmq_qasm_simulator', provider=mock_provider) + dev = IBMQDevice(wires=2, backend="ibmq_qasm_simulator", provider=mock_provider) assert mock_qiskit_device.provider == mock_provider + def mock_get_provider(*args, **kwargs): """A mock function for the get_provider Qiskit function to record the arguments which it was called with.""" return (args, kwargs) + def test_default_provider(monkeypatch): """Tests that the default provider is used when no custom provider was specified.""" @@ -86,10 +92,11 @@ def test_default_provider(monkeypatch): # Here mocking to a value such that it is not None m.setattr(ibmq.IBMQ, "active_account", lambda *args, **kwargs: True) - dev = IBMQDevice(wires=2, backend='ibmq_qasm_simulator') + dev = IBMQDevice(wires=2, backend="ibmq_qasm_simulator") assert mock_qiskit_device.provider[0] == () - assert mock_qiskit_device.provider[1] == {'hub': 'ibm-q', 'group': 'open', 'project': 'main'} + assert mock_qiskit_device.provider[1] == {"hub": "ibm-q", "group": "open", "project": "main"} + def test_custom_provider_hub_group_project(monkeypatch): """Tests that the custom arguments passed during device instantiation are @@ -107,10 +114,20 @@ def test_custom_provider_hub_group_project(monkeypatch): # Here mocking to a value such that it is not None m.setattr(ibmq.IBMQ, "active_account", lambda *args, **kwargs: True) - dev = IBMQDevice(wires=2, backend='ibmq_qasm_simulator', hub=custom_hub, group=custom_group, project=custom_project) + dev = IBMQDevice( + wires=2, + backend="ibmq_qasm_simulator", + hub=custom_hub, + group=custom_group, + project=custom_project, + ) assert mock_qiskit_device.provider[0] == () - assert mock_qiskit_device.provider[1] == {'hub': custom_hub, 'group': custom_group, 'project': custom_project} + assert mock_qiskit_device.provider[1] == { + "hub": custom_hub, + "group": custom_group, + "project": custom_project, + } def test_load_from_disk(token): @@ -129,7 +146,6 @@ def test_account_error(monkeypatch): IBMQDevice(wires=1) -@pytest.mark.parametrize("analytic", [False]) @pytest.mark.parametrize("shots", [1000]) def test_simple_circuit(token, tol, shots): IBMQ.enable_account(token) @@ -150,13 +166,12 @@ def circuit(theta, phi): assert np.allclose(res, expected, **tol) -@pytest.mark.parametrize("analytic", [False]) @pytest.mark.parametrize("shots", [1000]) def test_probability(token, tol, shots): """Test that the probs function works.""" IBMQ.enable_account(token) dev = IBMQDevice(wires=2, backend="ibmq_qasm_simulator", shots=shots) - dev_analytic = qml.device("default.qubit", wires=2, analytic=True) + dev_analytic = qml.device("default.qubit", wires=2, shots=None) x = [0.2, 0.5] diff --git a/tests/test_integration.py b/tests/test_integration.py index f4f6ceb1d..3c91380f4 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -41,18 +41,19 @@ def test_args(self): with pytest.raises(TypeError, match="missing 1 required positional argument"): qml.device("qiskit.aer") - with pytest.raises(qml.DeviceError, match="specified number of shots needs to be at least 1"): + with pytest.raises( + qml.DeviceError, match="specified number of shots needs to be at least 1" + ): qml.device("qiskit.aer", backend="qasm_simulator", wires=1, shots=0) @pytest.mark.parametrize("d", pldevices) - @pytest.mark.parametrize("analytic", [True, False]) - @pytest.mark.parametrize("shots", [8192]) - def test_one_qubit_circuit(self, shots, analytic, d, backend, tol): + @pytest.mark.parametrize("shots", [None, 8192]) + def test_one_qubit_circuit(self, shots, d, backend, tol): """Test that devices provide correct result for a simple circuit""" - if backend not in state_backends and analytic: + if backend not in state_backends and shots is None: pytest.skip("Hardware simulators do not support analytic mode") - dev = qml.device(d[0], wires=1, backend=backend, shots=shots, analytic=analytic) + dev = qml.device(d[0], wires=1, backend=backend, shots=shots) a = 0.543 b = 0.123 @@ -69,12 +70,10 @@ def circuit(x, y, z): assert np.allclose(circuit(a, b, c), np.cos(a) * np.sin(b), **tol) @pytest.mark.parametrize("d", pldevices) - @pytest.mark.parametrize("analytic", [False]) @pytest.mark.parametrize("shots", [8192]) - def test_one_qubit_circuit(self, shots, analytic, d, backend, tol): - """Integration test for the Basisstate and Rot operations for when analytic - is False""" - dev = qml.device(d[0], wires=1, backend=backend, shots=shots, analytic=analytic) + def test_one_qubit_circuit(self, shots, d, backend, tol): + """Integration test for the BasisState and Rot operations for non-analytic mode.""" + dev = qml.device(d[0], wires=1, backend=backend, shots=shots) a = 0 b = 0 @@ -105,11 +104,11 @@ def ansatz(weights): return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1)) dev_qsk = qml.device( - "qiskit.aer", - wires=n_qubits, - shots=1000, - backend="qasm_simulator", - ) + "qiskit.aer", + wires=n_qubits, + shots=1000, + backend="qasm_simulator", + ) weights = np.random.random((depth, n_qubits)).flatten() @@ -119,6 +118,7 @@ def ansatz(weights): exp_sampled(weights) grad_shift(weights) + class TestKeywordArguments: """Test keyword argument logic is correct""" @@ -135,9 +135,11 @@ def test_noise_model_qasm_simulator(self, monkeypatch): cache = [] with monkeypatch.context() as m: - m.setattr(aer.QasmSimulator, "set_options", lambda *args, **kwargs: cache.append(kwargs)) + m.setattr( + aer.QasmSimulator, "set_options", lambda *args, **kwargs: cache.append(kwargs) + ) dev = qml.device("qiskit.aer", wires=2, noise_model="test value") - assert cache[0] == {'noise_model': 'test value'} + assert cache[0] == {"noise_model": "test value"} def test_invalid_noise_model(self): """Test that the noise model argument causes an exception to be raised @@ -147,7 +149,7 @@ def test_invalid_noise_model(self): def test_overflow_kwargs(self): """Test all overflow kwargs are extracted for the AerDevice""" - dev = qml.device('qiskit.aer', wires=2, k1="v1", k2="v2") + dev = qml.device("qiskit.aer", wires=2, k1="v1", k2="v2") assert dev.run_args["k1"] == "v1" assert dev.run_args["k2"] == "v2" @@ -156,23 +158,20 @@ class TestLoadIntegration: """Integration tests for the PennyLane load function. This test ensures that the PennyLane-Qiskit specific load functions integrate properly with the PennyLane-Qiskit plugin.""" - hadamard_qasm = 'OPENQASM 2.0;' \ - 'include "qelib1.inc";' \ - 'qreg q[1];' \ - 'h q[0];' + hadamard_qasm = "OPENQASM 2.0;" 'include "qelib1.inc";' "qreg q[1];" "h q[0];" def test_load_qiskit_circuit(self): """Test that the default load function works correctly.""" - theta = qiskit.circuit.Parameter('θ') + theta = qiskit.circuit.Parameter("θ") qc = qiskit.QuantumCircuit(2) qc.rx(theta, 0) - my_template = qml.load(qc, format='qiskit') + my_template = qml.load(qc, format="qiskit") - dev = qml.device('default.qubit', wires=2) + dev = qml.device("default.qubit", wires=2) - angles = np.array([0.53896774, 0.79503606, 0.27826503, 0.]) + angles = np.array([0.53896774, 0.79503606, 0.27826503, 0.0]) @qml.qnode(dev) def loaded_quantum_circuit(angle): @@ -190,7 +189,7 @@ def quantum_circuit(angle): def test_load_from_qasm_string(self): """Test that quantum circuits can be loaded from a qasm string.""" - dev = qml.device('default.qubit', wires=2) + dev = qml.device("default.qubit", wires=2) @qml.qnode(dev) def loaded_quantum_circuit(): @@ -214,7 +213,7 @@ def test_load_qasm_from_file(self, tmpdir): hadamard = qml.from_qasm_file(apply_hadamard) - dev = qml.device('default.qubit', wires=2) + dev = qml.device("default.qubit", wires=2) @qml.qnode(dev) def loaded_quantum_circuit(): @@ -232,9 +231,8 @@ def quantum_circuit(): class TestPLOperations: """Integration tests for checking certain PennyLane specific operations.""" - @pytest.mark.parametrize("shots", [1000]) - @pytest.mark.parametrize("analytic", [True, False]) - def test_rotation(self, init_state, state_vector_device, shots, analytic, tol): + @pytest.mark.parametrize("shots", [None, 1000]) + def test_rotation(self, init_state, state_vector_device, shots, tol): """Test that the QubitStateVector and Rot operations are decomposed using a Qiskit device with statevector backend""" @@ -267,11 +265,12 @@ def qubitstatevector_and_rot(): qubitstatevector_and_rot() - assert np.allclose(np.abs(dev.state) ** 2, np.abs(rz(c) @ ry(b) @ rz(a) @ state) ** 2, **tol) + assert np.allclose( + np.abs(dev.state) ** 2, np.abs(rz(c) @ ry(b) @ rz(a) @ state) ** 2, **tol + ) - @pytest.mark.parametrize("shots", [1000]) - @pytest.mark.parametrize("analytic", [True, False]) - def test_basisstate(self, init_state, state_vector_device, shots, analytic, tol): + @pytest.mark.parametrize("shots", [None, 1000]) + def test_basisstate(self, init_state, state_vector_device, shots, tol): """Test that the Basisstate is decomposed using a Qiskit device with statevector backend""" @@ -285,14 +284,13 @@ def basisstate(): basisstate() - expected_state = np.zeros(2**dev.num_wires) + expected_state = np.zeros(2 ** dev.num_wires) expected_state[2] = 1 assert np.allclose(np.abs(dev.state) ** 2, np.abs(expected_state) ** 2, **tol) - @pytest.mark.parametrize("shots", [1000]) - @pytest.mark.parametrize("analytic", [True, False]) - def test_basisstate_init_all_zero_states(self, init_state, state_vector_device, shots, analytic, tol): + @pytest.mark.parametrize("shots", [None, 1000]) + def test_basisstate_init_all_zero_states(self, init_state, state_vector_device, shots, tol): """Test that the Basisstate that receives the all zero state is decomposed using a Qiskit device with statevector backend""" @@ -306,7 +304,7 @@ def basisstate(): basisstate() - expected_state = np.zeros(2**dev.num_wires) + expected_state = np.zeros(2 ** dev.num_wires) expected_state[0] = 1 assert np.allclose(np.abs(dev.state) ** 2, np.abs(expected_state) ** 2, **tol) @@ -386,7 +384,7 @@ def circuit(phi=None): phi = tensor([[0.04439891, 0.14490549, 3.29725643, 2.51240058]]) - with qml._queuing.OperationRecorder() as rec: + with qml.tape.OperationRecorder() as rec: circuit(phi=phi) for i in range(phi.shape[1]): @@ -410,8 +408,7 @@ def circuit(phi=None): phi = tensor([[0.04439891, 0.14490549, 3.29725643]]) - - with qml._queuing.OperationRecorder() as rec: + with qml.tape.OperationRecorder() as rec: circuit(phi=phi) # Test the rotation applied @@ -425,17 +422,18 @@ def circuit(phi=None): assert isinstance(rec.queue[0].parameters[2], tensor) + class TestInverses: """Integration tests checking that the inverse of the operations are applied.""" def test_inverse_of_operation(self): """Test that the inverse of operations works as expected by comparing a simple circuit with default.qubit.""" - dev = qml.device('default.qubit', wires=2) + dev = qml.device("default.qubit", wires=2) - dev2 = qml.device('qiskit.aer', backend='statevector_simulator', shots=5, wires=2, analytic=True) + dev2 = qml.device("qiskit.aer", backend="statevector_simulator", shots=None, wires=2) - angles = np.array([0.53896774, 0.79503606, 0.27826503, 0.]) + angles = np.array([0.53896774, 0.79503606, 0.27826503, 0.0]) @qml.qnode(dev) def circuit_with_inverses(angle): @@ -452,18 +450,19 @@ def circuit_with_inverses_default_qubit(angle): for x in angles: assert np.allclose(circuit_with_inverses(x), circuit_with_inverses_default_qubit(x)) + class TestNoise: """Integration test for the noise models.""" def test_noise_applied(self): """Test that the qiskit noise model is applied correctly""" noise_model = aer.noise.NoiseModel() - bit_flip = aer.noise.pauli_error([('X', 1), ('I', 0)]) + bit_flip = aer.noise.pauli_error([("X", 1), ("I", 0)]) # Create a noise model where the RX operation always flips the bit noise_model.add_all_qubit_quantum_error(bit_flip, ["rx"]) - dev = qml.device('qiskit.aer', wires=2, noise_model=noise_model) + dev = qml.device("qiskit.aer", wires=2, noise_model=noise_model) @qml.qnode(dev) def circuit(): diff --git a/tests/test_inverses.py b/tests/test_inverses.py index 621a500bf..5edfe3a10 100644 --- a/tests/test_inverses.py +++ b/tests/test_inverses.py @@ -13,21 +13,24 @@ class TestInverses: """Tests that the inverse of the operations are applied.""" # This test is ran against the state |0> with one Z expval - @pytest.mark.parametrize("name,expected_output", [ - ("PauliX", -1), - ("PauliY", -1), - ("PauliZ", 1), - ("Hadamard", 0), - ("S", 1), - ("T", 1), - ]) + @pytest.mark.parametrize( + "name,expected_output", + [ + ("PauliX", -1), + ("PauliY", -1), + ("PauliZ", 1), + ("Hadamard", 0), + ("S", 1), + ("T", 1), + ], + ) def test_supported_gate_inverse_single_wire_no_parameters(self, name, expected_output): """Tests the inverse of supported gates that act on a single wire that are not parameterized""" op = getattr(qml.ops, name) - dev = qml.device('qiskit.aer', backend='statevector_simulator', wires=2, analytic=True) + dev = qml.device("qiskit.aer", backend="statevector_simulator", wires=2, shots=None) @qml.qnode(dev) def circuit(): @@ -37,37 +40,43 @@ def circuit(): assert np.isclose(circuit(), expected_output, atol=tol, rtol=0) # This test is ran against the state |Phi+> with two Z expvals - @pytest.mark.parametrize("name,expected_output", [ - ("CNOT", [-1/2, 1]), - ("SWAP", [-1/2, -1/2]), - ("CZ", [-1/2, -1/2]), - ]) + @pytest.mark.parametrize( + "name,expected_output", + [ + ("CNOT", [-1 / 2, 1]), + ("SWAP", [-1 / 2, -1 / 2]), + ("CZ", [-1 / 2, -1 / 2]), + ], + ) def test_supported_gate_inverse_two_wires_no_parameters(self, name, expected_output): """Tests the inverse of supported gates that act on two wires that are not parameterized""" op = getattr(qml.ops, name) - dev = qml.device('qiskit.aer', backend='statevector_simulator', wires=2, analytic=True) + dev = qml.device("qiskit.aer", backend="statevector_simulator", wires=2, shots=None) assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): - qml.QubitStateVector(np.array([1/2, 0, 0, math.sqrt(3)/2]), wires=[0, 1]) + qml.QubitStateVector(np.array([1 / 2, 0, 0, math.sqrt(3) / 2]), wires=[0, 1]) op(wires=[0, 1]).inv() return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)) assert np.allclose(circuit(), expected_output, atol=tol, rtol=0) - @pytest.mark.parametrize("name,expected_output", [ - ("CSWAP", [-1, -1, 1]), - ]) + @pytest.mark.parametrize( + "name,expected_output", + [ + ("CSWAP", [-1, -1, 1]), + ], + ) def test_supported_gate_inverse_three_wires_no_parameters(self, name, expected_output): """Tests the inverse of supported gates that act on three wires that are not parameterized""" op = getattr(qml.ops, name) - dev = qml.device('qiskit.aer', backend='statevector_simulator', wires=3, analytic=True) + dev = qml.device("qiskit.aer", backend="statevector_simulator", wires=3, shots=None) assert dev.supports_operation(name) @@ -80,22 +89,47 @@ def circuit(): assert np.allclose(circuit(), expected_output, atol=tol, rtol=0) # This test is ran on the state |0> with one Z expvals - @pytest.mark.parametrize("name,par,expected_output", [ - ("PhaseShift", [math.pi/2], 1), - ("PhaseShift", [-math.pi/4], 1), - ("RX", [math.pi/2], 0), - ("RX", [-math.pi/4], 1/math.sqrt(2)), - ("RY", [math.pi/2], 0), - ("RY", [-math.pi/4], 1/math.sqrt(2)), - ("RZ", [math.pi/2], 1), - ("RZ", [-math.pi/4], 1), - ("QubitUnitary", [np.array([[1j/math.sqrt(2), 1j/math.sqrt(2)], [1j/math.sqrt(2), -1j/math.sqrt(2)]])], 0), - ("QubitUnitary", [np.array([[-1j/math.sqrt(2), 1j/math.sqrt(2)], [1j/math.sqrt(2), 1j/math.sqrt(2)]])], 0), - ]) + @pytest.mark.parametrize( + "name,par,expected_output", + [ + ("PhaseShift", [math.pi / 2], 1), + ("PhaseShift", [-math.pi / 4], 1), + ("RX", [math.pi / 2], 0), + ("RX", [-math.pi / 4], 1 / math.sqrt(2)), + ("RY", [math.pi / 2], 0), + ("RY", [-math.pi / 4], 1 / math.sqrt(2)), + ("RZ", [math.pi / 2], 1), + ("RZ", [-math.pi / 4], 1), + ( + "QubitUnitary", + [ + np.array( + [ + [1j / math.sqrt(2), 1j / math.sqrt(2)], + [1j / math.sqrt(2), -1j / math.sqrt(2)], + ] + ) + ], + 0, + ), + ( + "QubitUnitary", + [ + np.array( + [ + [-1j / math.sqrt(2), 1j / math.sqrt(2)], + [1j / math.sqrt(2), 1j / math.sqrt(2)], + ] + ) + ], + 0, + ), + ], + ) def test_supported_gate_inverse_single_wire_with_parameters(self, name, par, expected_output): """Test the inverse of single gates with parameters""" - dev = qml.device('qiskit.aer', backend='statevector_simulator', wires=2, analytic=True) + dev = qml.device("qiskit.aer", backend="statevector_simulator", wires=2, shots=None) op = getattr(qml.ops, name) @@ -109,17 +143,46 @@ def circuit(): assert np.isclose(circuit(), expected_output, atol=tol, rtol=0) # This test is ran against the state 1/2|00>+sqrt(3)/2|11> with two Z expvals - @pytest.mark.parametrize("name,par,expected_output", [ - ("CRZ", [0], [-1/2, -1/2]), - ("CRZ", [-math.pi], [-1/2, -1/2]), - ("CRZ", [math.pi/2], [-1/2, -1/2]), - ("QubitUnitary", [np.array([[1, 0, 0, 0], [0, 1/math.sqrt(2), 1/math.sqrt(2), 0], [0, 1/math.sqrt(2), -1/math.sqrt(2), 0], [0, 0, 0, 1]])], [-1/2, -1/2]), - ("QubitUnitary", [np.array([[-1, 0, 0, 0], [0, 1/math.sqrt(2), 1/math.sqrt(2), 0], [0, 1/math.sqrt(2), -1/math.sqrt(2), 0], [0, 0, 0, -1]])], [-1/2, -1/2]), - ]) + @pytest.mark.parametrize( + "name,par,expected_output", + [ + ("CRZ", [0], [-1 / 2, -1 / 2]), + ("CRZ", [-math.pi], [-1 / 2, -1 / 2]), + ("CRZ", [math.pi / 2], [-1 / 2, -1 / 2]), + ( + "QubitUnitary", + [ + np.array( + [ + [1, 0, 0, 0], + [0, 1 / math.sqrt(2), 1 / math.sqrt(2), 0], + [0, 1 / math.sqrt(2), -1 / math.sqrt(2), 0], + [0, 0, 0, 1], + ] + ) + ], + [-1 / 2, -1 / 2], + ), + ( + "QubitUnitary", + [ + np.array( + [ + [-1, 0, 0, 0], + [0, 1 / math.sqrt(2), 1 / math.sqrt(2), 0], + [0, 1 / math.sqrt(2), -1 / math.sqrt(2), 0], + [0, 0, 0, -1], + ] + ) + ], + [-1 / 2, -1 / 2], + ), + ], + ) def test_supported_gate_inverse_two_wires_with_parameters(self, name, par, expected_output): """Tests the inverse of supported gates that act on two wires that are parameterized""" - dev = qml.device('qiskit.aer', backend='statevector_simulator', wires=2, analytic=True) + dev = qml.device("qiskit.aer", backend="statevector_simulator", wires=2, shots=None) op = getattr(qml.ops, name) @@ -127,24 +190,27 @@ def test_supported_gate_inverse_two_wires_with_parameters(self, name, par, expec @qml.qnode(dev) def circuit(): - qml.QubitStateVector(np.array([1/2, 0, 0, math.sqrt(3)/2]), wires=[0, 1]) + qml.QubitStateVector(np.array([1 / 2, 0, 0, math.sqrt(3) / 2]), wires=[0, 1]) op(*np.negative(par), wires=[0, 1]).inv() return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)) assert np.allclose(circuit(), expected_output, atol=tol, rtol=0) - @pytest.mark.parametrize("name,par,expected_output", [ - ("Rot", [math.pi/2, 0, 0], 1), - ("Rot", [0, math.pi/2, 0], 0), - ("Rot", [0, 0, math.pi/2], 1), - ("Rot", [math.pi/2, -math.pi/4, -math.pi/4], 1/math.sqrt(2)), - ("Rot", [-math.pi/4, math.pi/2, math.pi/4], 0), - ("Rot", [-math.pi/4, math.pi/4, math.pi/2], 1/math.sqrt(2)), - ]) + @pytest.mark.parametrize( + "name,par,expected_output", + [ + ("Rot", [math.pi / 2, 0, 0], 1), + ("Rot", [0, math.pi / 2, 0], 0), + ("Rot", [0, 0, math.pi / 2], 1), + ("Rot", [math.pi / 2, -math.pi / 4, -math.pi / 4], 1 / math.sqrt(2)), + ("Rot", [-math.pi / 4, math.pi / 2, math.pi / 4], 0), + ("Rot", [-math.pi / 4, math.pi / 4, math.pi / 2], 1 / math.sqrt(2)), + ], + ) def test_unsupported_gate_inverses(self, name, par, expected_output): """Test the inverse of single gates with parameters""" - dev = qml.device('qiskit.aer', backend='statevector_simulator', wires=2, analytic=True) + dev = qml.device("qiskit.aer", backend="statevector_simulator", wires=2, shots=None) op = getattr(qml.ops, name) @@ -155,13 +221,13 @@ def circuit(): assert np.isclose(circuit(), expected_output, atol=tol, rtol=0) - @pytest.mark.parametrize("par", [np.pi/i for i in range(1, 5)]) + @pytest.mark.parametrize("par", [np.pi / i for i in range(1, 5)]) def test_s_gate_inverses(self, par): """Tests the inverse of the S gate""" - dev = qml.device('qiskit.aer', backend='statevector_simulator', wires=2, analytic=True) + dev = qml.device("qiskit.aer", backend="statevector_simulator", wires=2, shots=None) - expected_output = -0.5 * 1j * cmath.exp(-1j*par)*(-1 + cmath.exp(2j*par)) + expected_output = -0.5 * 1j * cmath.exp(-1j * par) * (-1 + cmath.exp(2j * par)) @qml.qnode(dev) def circuit(): @@ -172,11 +238,11 @@ def circuit(): assert np.allclose(circuit(), expected_output, atol=tol, rtol=0) - @pytest.mark.parametrize("par", [np.pi/i for i in range(1, 5)]) + @pytest.mark.parametrize("par", [np.pi / i for i in range(1, 5)]) def test_t_gate_inverses(self, par): """Tests the inverse of the T gate""" - dev = qml.device('qiskit.aer', backend='statevector_simulator', wires=2, analytic=True) + dev = qml.device("qiskit.aer", backend="statevector_simulator", wires=2, shots=None) expected_output = -math.sin(par) / math.sqrt(2) diff --git a/tests/test_qiskit_device.py b/tests/test_qiskit_device.py index 51fb48898..bf8714985 100644 --- a/tests/test_qiskit_device.py +++ b/tests/test_qiskit_device.py @@ -7,15 +7,12 @@ test_transpile_options = [ {}, - {'optimization_level':2}, - {'optimization_level':2, 'seed_transpiler':22} + {"optimization_level": 2}, + {"optimization_level": 2, "seed_transpiler": 22}, ] -test_device_options = [ - {}, - {'optimization_level':3}, - {'optimization_level':1} -] +test_device_options = [{}, {"optimization_level": 3}, {"optimization_level": 1}] + class TestProbabilities: """Tests for the probability function""" @@ -23,16 +20,16 @@ class TestProbabilities: def test_probability_no_results(self): """Test that the probabilities function returns None if no job has yet been run.""" - dev = AerDevice(backend="statevector_simulator", wires=1, analytic=True) + dev = AerDevice(backend="statevector_simulator", wires=1, shots=None) assert dev.probability() is None -@pytest.mark.parametrize("analytic", [True]) -@pytest.mark.parametrize("wires", [1,2,3]) -@pytest.mark.parametrize("shots", [2]) + +@pytest.mark.parametrize("wires", [1, 2, 3]) +@pytest.mark.parametrize("shots", [None]) @pytest.mark.parametrize("device_options", test_device_options) @pytest.mark.transpile_args_test class TestTranspilationOptionInitialization: - """Tests for passing the transpilation options to qiskit at time of device + """Tests for passing the transpilation options to qiskit at time of device initialization.""" def test_device_with_transpilation_options(self, device, wires, device_options): @@ -48,33 +45,39 @@ def test_transpilation_option_update(self, device, wires, device_options, transp dev.set_transpile_args(**transpile_options) assert dev.transpile_args == transpile_options + class TestAnalyticWarningHWSimulator: """Tests the warnings for when the analytic attribute of a device is set to true""" def test_warning_raised_for_hardware_backend_analytic_expval(self, hardware_backend, recorder): """Tests that a warning is raised if the analytic attribute is true on - hardware simulators when calculating the expectation""" + hardware simulators when calculating the expectation""" with pytest.warns(UserWarning) as record: - dev = qml.device("qiskit.basicaer", backend=hardware_backend, wires=2, analytic=True) + dev = qml.device("qiskit.basicaer", backend=hardware_backend, wires=2, shots=None) # check that only one warning was raised assert len(record) == 1 # check that the message matches - assert record[0].message.args[0] == "The analytic calculation of "\ - "expectations, variances and probabilities is only supported on "\ - "statevector backends, not on the {}. Such statistics obtained from this "\ - "device are estimates based on samples.".format(dev.backend) - - def test_no_warning_raised_for_software_backend_analytic_expval(self, statevector_backend, recorder, recwarn): + assert ( + record[0].message.args[0] == "The analytic calculation of " + "expectations, variances and probabilities is only supported on " + "statevector backends, not on the {}. Such statistics obtained from this " + "device are estimates based on samples.".format(dev.backend) + ) + + def test_no_warning_raised_for_software_backend_analytic_expval( + self, statevector_backend, recorder, recwarn + ): """Tests that no warning is raised if the analytic attribute is true on - statevector simulators when calculating the expectation""" + statevector simulators when calculating the expectation""" - dev = qml.device("qiskit.basicaer", backend=statevector_backend, wires=2, analytic=True) + dev = qml.device("qiskit.basicaer", backend=statevector_backend, wires=2, shots=None) # check that no warnings were raised assert len(recwarn) == 0 + class TestAerBackendOptions: """Test the backend options of Aer backends.""" @@ -82,13 +85,13 @@ def test_backend_options_cleaned(self): """Test that the backend options are cleared upon new Aer device initialization.""" noise_model = noise.NoiseModel() - bit_flip = noise.pauli_error([('X', 1), ('I', 0)]) + bit_flip = noise.pauli_error([("X", 1), ("I", 0)]) # Create a noise model where the RX operation always flips the bit noise_model.add_all_qubit_quantum_error(bit_flip, ["rx"]) - dev = qml.device('qiskit.aer', wires=2, noise_model=noise_model) - assert 'noise_model' in dev.backend.options + dev = qml.device("qiskit.aer", wires=2, noise_model=noise_model) + assert "noise_model" in dev.backend.options - dev2 = qml.device('qiskit.aer', wires=2) - assert 'noise_model' not in dev2.backend.options + dev2 = qml.device("qiskit.aer", wires=2) + assert "noise_model" not in dev2.backend.options diff --git a/tests/test_sample.py b/tests/test_sample.py index c7dd9a1ef..fee7286d1 100644 --- a/tests/test_sample.py +++ b/tests/test_sample.py @@ -15,7 +15,6 @@ VARPHI = np.linspace(0.02, 1, 3) -@pytest.mark.parametrize("analytic", [False]) @pytest.mark.parametrize("shots", [8192]) class TestSample: """Tests for the sample return type""" @@ -33,7 +32,7 @@ def test_sample_values(self, device, shots, tol): [ qml.RX(par, wires=[0]), ], - rotations=[*observable.diagonalizing_gates()] + rotations=[*observable.diagonalizing_gates()], ) dev._samples = dev.generate_samples() @@ -58,7 +57,7 @@ def test_sample_values_hermitian(self, theta, device, shots, tol): [ qml.RX(theta, wires=[0]), ], - rotations=[*observable.diagonalizing_gates()] + rotations=[*observable.diagonalizing_gates()], ) dev._samples = dev.generate_samples() @@ -95,12 +94,8 @@ def test_sample_values_hermitian_multi_qubit(self, theta, device, shots, tol): observable = qml.Hermitian(A, wires=[0, 1]) dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RY(2 * theta, wires=[1]), - qml.CNOT(wires=[0, 1]) - ], - rotations=[*observable.diagonalizing_gates()] + [qml.RX(theta, wires=[0]), qml.RY(2 * theta, wires=[1]), qml.CNOT(wires=[0, 1])], + rotations=[*observable.diagonalizing_gates()], ) dev._samples = dev.generate_samples() @@ -126,7 +121,6 @@ def test_sample_values_hermitian_multi_qubit(self, theta, device, shots, tol): @pytest.mark.parametrize("theta, phi, varphi", list(zip(THETA, PHI, VARPHI))) -@pytest.mark.parametrize("analytic", [False]) @pytest.mark.parametrize("shots", [8192]) class TestTensorSample: """Test tensor expectation values""" @@ -143,9 +137,9 @@ def test_paulix_pauliy(self, theta, phi, varphi, device, shots, tol): qml.RX(phi, wires=[1]), qml.RX(varphi, wires=[2]), qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]) + qml.CNOT(wires=[1, 2]), ], - rotations=[*observable.diagonalizing_gates()] + rotations=[*observable.diagonalizing_gates()], ) dev._samples = dev.generate_samples() @@ -182,9 +176,9 @@ def test_pauliz_hadamard_pauliy(self, theta, phi, varphi, device, shots, tol): qml.RX(phi, wires=[1]), qml.RX(varphi, wires=[2]), qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]) + qml.CNOT(wires=[1, 2]), ], - rotations=[*observable.diagonalizing_gates()] + rotations=[*observable.diagonalizing_gates()], ) dev._samples = dev.generate_samples() @@ -227,9 +221,9 @@ def test_hermitian(self, theta, phi, varphi, device, shots, tol): qml.RX(phi, wires=[1]), qml.RX(varphi, wires=[2]), qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]) + qml.CNOT(wires=[1, 2]), ], - rotations=[*observable.diagonalizing_gates()] + rotations=[*observable.diagonalizing_gates()], ) dev._samples = dev.generate_samples() diff --git a/tests/test_var.py b/tests/test_var.py index 6b680275d..e82bbef2a 100644 --- a/tests/test_var.py +++ b/tests/test_var.py @@ -16,8 +16,7 @@ @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) -@pytest.mark.parametrize("analytic", [True, False]) -@pytest.mark.parametrize("shots", [8192]) +@pytest.mark.parametrize("shots", [None, 8192]) class TestVar: """Tests for the variance""" @@ -33,7 +32,7 @@ def test_var(self, theta, phi, device, shots, tol): qml.RX(phi, wires=[0]), qml.RY(theta, wires=[0]), ], - rotations=[*observable.diagonalizing_gates()] + rotations=[*observable.diagonalizing_gates()], ) dev._samples = dev.generate_samples() @@ -57,7 +56,7 @@ def test_var_hermitian(self, theta, phi, device, shots, tol): qml.RX(phi, wires=[0]), qml.RY(theta, wires=[0]), ], - rotations=[*observable.diagonalizing_gates()] + rotations=[*observable.diagonalizing_gates()], ) dev._samples = dev.generate_samples() @@ -74,8 +73,7 @@ def test_var_hermitian(self, theta, phi, device, shots, tol): @pytest.mark.parametrize("theta, phi, varphi", list(zip(THETA, PHI, VARPHI))) -@pytest.mark.parametrize("analytic", [True, False]) -@pytest.mark.parametrize("shots", [8192]) +@pytest.mark.parametrize("shots", [None, 8192]) class TestTensorVar: """Tests for variance of tensor observables""" @@ -90,9 +88,9 @@ def test_paulix_pauliy(self, theta, phi, varphi, device, shots, tol): qml.RX(phi, wires=[1]), qml.RX(varphi, wires=[2]), qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]) + qml.CNOT(wires=[1, 2]), ], - rotations=obs.diagonalizing_gates() + rotations=obs.diagonalizing_gates(), ) dev._samples = dev.generate_samples() @@ -120,9 +118,9 @@ def test_pauliz_hadamard_pauliy(self, theta, phi, varphi, device, shots, tol): qml.RX(phi, wires=[1]), qml.RX(varphi, wires=[2]), qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]) + qml.CNOT(wires=[1, 2]), ], - rotations=obs.diagonalizing_gates() + rotations=obs.diagonalizing_gates(), ) dev._samples = dev.generate_samples() @@ -157,9 +155,9 @@ def test_hermitian(self, theta, phi, varphi, device, shots, tol): qml.RX(phi, wires=[1]), qml.RX(varphi, wires=[2]), qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]) + qml.CNOT(wires=[1, 2]), ], - rotations=obs.diagonalizing_gates() + rotations=obs.diagonalizing_gates(), ) dev._samples = dev.generate_samples()