diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d22976e3..b2467594 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,15 +2,18 @@ name: Tests on: push: + branches: + - main + - dev + pull_request: workflow_dispatch: inputs: python_v: description: "python version" required: true - default: "3.9" + default: "3.10" type: choice options: - - "3.9" - "3.10" - "3.11" commit_ref: @@ -23,13 +26,24 @@ on: required: false default: false type: boolean - + providers: + description: "Providers to enable (space-separated) e.g., 'qiskit cirq myqlm braket')" + required: false + default: "all" + type: choice + options: + - "all" + - "qiskit" + - "cirq" + - "braket" + - "myqlm" jobs: tests: runs-on: ubuntu-latest strategy: matrix: - python-version: ${{ fromJSON(github.event_name == 'workflow_dispatch' && format('["{0}"]', github.event.inputs.python_v) || '["3.9", "3.10", "3.11"]') }} + python-version: ${{ fromJSON(github.event_name == 'workflow_dispatch' && format('["{0}"]', github.event.inputs.python_v) || '["3.10", "3.11"]') }} + provider: ${{ fromJSON(github.event_name == 'workflow_dispatch' && format('["{0}"]', github.event.inputs.providers) || (github.ref_name == 'main' && '["all", "qiskit", "cirq", "braket", "myqlm"]' || '["all"]')) }} steps: - name: Checkout repository uses: actions/checkout@v4 @@ -44,25 +58,27 @@ jobs: run: | pip install --upgrade pip pip install -r requirements-dev.txt + providers_comma=$(echo "${{ matrix.provider }}" | sed 's/ /,/g') + echo $providers_comma + pip install .[$providers_comma] - name: Install os specific dependencies if: ${{ github.event.inputs.long == 'true' || github.ref_name == 'main' }} run: | - pip install . sudo apt-get update sudo apt install -y poppler-utils sudo apt-get install -y texlive-latex-base texlive-pictures texlive-latex-extra - name: Run tests run: | - if [ "${{ github.event.inputs.long }}" == "true" ] || [ "${{ github.ref_name }}" == "main" ]; then - python -m pytest --long-local + if [[ "${{ github.event.inputs.long }}" == "true" || "${{ github.ref_name }}" == "main" ]]; then + python -m pytest --long-local else - python -m pytest + python -m pytest --providers ${{ matrix.provider }} fi typechecks: runs-on: ubuntu-latest strategy: matrix: - python-version: ${{ fromJSON(github.event_name == 'workflow_dispatch' && format('["{0}"]', github.event.inputs.python_v) || '["3.9", "3.10", "3.11"]') }} + python-version: ${{ fromJSON(github.event_name == 'workflow_dispatch' && format('["{0}"]', github.event.inputs.python_v) || '["3.10", "3.11"]') }} steps: - name: Checkout repository uses: actions/checkout@v4 @@ -77,5 +93,6 @@ jobs: run: | pip install --upgrade pip pip install -r requirements-dev.txt + pip install -r requirements-all.txt - name: Run type checker run: pyright \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 9b08c8a9..5175b29f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Stage 1: Build dependencies -FROM python:3.9 AS builder +FROM python:3.10 AS builder WORKDIR /usr/src/app @@ -7,7 +7,7 @@ COPY requirements.txt requirements-dev.txt ./ RUN pip install --upgrade pip && \ pip install --no-cache-dir -r requirements-dev.txt -FROM python:3.9 +FROM python:3.10 RUN apt update && \ apt install -y \ @@ -27,7 +27,7 @@ RUN chmod +x linux_awscli_install.sh && ./linux_awscli_install.sh WORKDIR /usr/src/app/mpqp -COPY --from=builder /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages +COPY --from=builder /usr/local/lib/python3.10/site-packages /usr/local/lib/python3.10/site-packages COPY .. /usr/src/app/mpqp/ COPY requirements.txt requirements-dev.txt /usr/src/app/ diff --git a/README.md b/README.md index ac82e448..8de12d0c 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ On this page, you will find: ## Install -For now, we support python versions 3.9 to 3.11, and every major OS (Windows, +For now, we support python versions 3.10 to 3.12, and every major OS (Windows, Linux and MacOS). We are dependant on the SDKs we support to enable various python versions and OS support, for instance, MPQP was validated on Ubuntu LTS 20.04, while Ubuntu 18.04 is not supported because myQLM does not support it. diff --git a/conftest.py b/conftest.py index abd3aa5a..7cfefe58 100644 --- a/conftest.py +++ b/conftest.py @@ -23,6 +23,13 @@ def pytest_addoption(parser: pytest.Parser): type=int, help="Set a global random seed for tests (default is None for random behavior).", ) + parser.addoption( + "--providers", + action="store", + nargs="*", + type=str, + help="List of providers to enable (e.g. --providers cirq qiskit azure)", + ) def pytest_configure(config: Any): @@ -31,11 +38,15 @@ def pytest_configure(config: Any): This hook is called for every plugin and initial conftest file after command line options have been parsed. """ + if ( + not config.getoption("--long") + or not config.getoption("--long-costly") + or not config.getoption("--long-local") + ): + from tests.local_storage.test_local_storage import create_test_local_storage - from tests.local_storage.test_local_storage import create_test_local_storage - - print("Creating local storage for tests") - create_test_local_storage() + print("Creating local storage for tests") + create_test_local_storage() @pytest.fixture(autouse=True) @@ -52,3 +63,20 @@ def stable_random(*args: Any, **kwargs: Any): return default_rng(user_seed or seed) monkeypatch.setattr('numpy.random.default_rng', stable_random) + + +def pytest_runtest_setup(item: pytest.Function): + + providers = item.config.getoption("--providers") + if TYPE_CHECKING: + assert isinstance(providers, list) or isinstance(providers, type(None)) + if providers is None: + providers = ["all"] + elif not providers: + providers = [] + + provider_marker = item.get_closest_marker("provider") + if provider_marker: + required = provider_marker.args[0] + if "all" not in providers and required not in providers: + pytest.skip(f"Skipping test: provider '{required}' not active") diff --git a/docs/conf.py b/docs/conf.py index 0098fac1..98fe9bc8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -342,9 +342,7 @@ def __init__(self, **options): # type: ignore PygmentsBridge.latex_formatter = CustomLatexFormatter -latex_elements[ - "preamble" -] += r""" +latex_elements["preamble"] += r""" % One-column index \makeatletter \renewenvironment{theindex}{ diff --git a/docs/getting-started.rst b/docs/getting-started.rst index b2cde8a7..ce644c54 100644 --- a/docs/getting-started.rst +++ b/docs/getting-started.rst @@ -7,7 +7,7 @@ Installation .. TODO: grab the compatibility matrix from MyQLM and relax our requirements .. when possible, test on many different configurations (tox or other ?) -For now, we support Python versions 3.9 to 3.11, and all of Windows, Linux and +For now, we support Python versions 3.10 to 3.11, and all of Windows, Linux and MacOS (specifically, Linux was validated on Ubuntu LTS 20.04, while Ubuntu 18.04 is not supported, so your milage may vary). @@ -36,7 +36,7 @@ version, run instead $ curl -L https://raw.githubusercontent.com/ColibrITD-SAS/mpqp/main/mac-install.sh | bash -s -- where ```` is the binary you use to invoke python. For instance, it could - be ``python``, ``python3``, or ``python3.9``. + be ``python``, ``python3``, or ``python3.10``. .. warning:: The migration from ``qiskit`` version ``0.x`` to ``1.x`` caused a few issues. diff --git a/examples/scripts/noisy_simulations.py b/examples/scripts/noisy_simulations.py index ece91262..a66e099f 100644 --- a/examples/scripts/noisy_simulations.py +++ b/examples/scripts/noisy_simulations.py @@ -4,7 +4,6 @@ from mpqp.noise import * from mpqp.execution import * - circuit = QCircuit( [Rx(0.3, 2), H(0), CNOT(1, 0), SWAP(2, 1), U(0.9, 0.2, 1, 1), BasisMeasure()] ) diff --git a/mpqp/core/circuit.py b/mpqp/core/circuit.py index 674627c3..398f9004 100644 --- a/mpqp/core/circuit.py +++ b/mpqp/core/circuit.py @@ -812,18 +812,14 @@ def to_matrix(self) -> npt.NDArray[np.complex128]: [0.70711, 0 , -0.70711, 0 ]] """ - from qiskit import QuantumCircuit from qiskit.quantum_info.operators import Operator - qiskit_circuit = self.to_other_language(Language.QISKIT) + qiskit_circuit = self.to_other_language(Language.QISKIT).reverse_bits() if TYPE_CHECKING: assert isinstance(qiskit_circuit, QuantumCircuit) matrix = Operator.from_circuit(qiskit_circuit).to_matrix() if TYPE_CHECKING: assert isinstance(matrix, np.ndarray) - gphase = self.input_g_phase + self._generated_g_phase - if gphase != 0: - matrix *= np.exp(1j * gphase) return matrix def inverse(self) -> QCircuit: @@ -1209,7 +1205,7 @@ def to_other_language( >>> type(qc) >>> circuit2 = QCircuit([H(0), CZ(0,1), Depolarizing(0.6, [0]), BasisMeasure()]) - >>> print(circuit2.to_other_language(Language.BRAKET)) # doctest: +NORMALIZE_WHITESPACE + >>> print(circuit2.to_other_language(Language.BRAKET)) # doctest: +NORMALIZE_WHITESPACE, +BRAKET T : │ 0 │ 1 │ ┌───┐ ┌───────────┐ ┌───────────┐ q0 : ─┤ H ├─┤ DEPO(0.6) ├───●───┤ DEPO(0.6) ├─ @@ -1282,17 +1278,18 @@ def to_other_language( if isinstance(instruction, CustomGate): if TYPE_CHECKING: assert isinstance(qiskit_inst, Operator) - qargs = [self.nb_qubits - 1 - q for q in instruction.targets] new_circ.unitary( qiskit_inst, - list(reversed(qargs)), + list(reversed(instruction.targets)), instruction.label, ) else: if isinstance(instruction, ControlledGate): - qargs = instruction.targets + instruction.controls + qargs = list(reversed(instruction.controls)) + list( + reversed(instruction.targets) + ) elif isinstance(instruction, Gate): - qargs = instruction.targets + qargs = list(reversed(instruction.targets)) elif isinstance(instruction, Barrier): qargs = range(self.nb_qubits) else: @@ -1300,10 +1297,9 @@ def to_other_language( if TYPE_CHECKING: assert not isinstance(qiskit_inst, Operator) - qargs = [self.nb_qubits - 1 - q for q in qargs] new_circ.append( qiskit_inst, - list(reversed(qargs)), + list(qargs), cargs, ) @@ -1317,7 +1313,7 @@ def to_other_language( ) new_circ.append( qiskit_pre_measure, - pre_measure.targets, + list(reversed(pre_measure.targets)), cargs=cargs, ) if not skip_measurements: @@ -1329,10 +1325,6 @@ def to_other_language( if isinstance(measurement, BasisMeasure): if TYPE_CHECKING: assert measurement.c_targets is not None - qargs = [[self.nb_qubits - 1 - q for q in measurement.targets]] - cargs = [ - [self.nb_qubits - 1 - q for q in measurement.c_targets] - ] else: raise ValueError(f"measurement not handled: {measurement}") @@ -1340,10 +1332,11 @@ def to_other_language( assert not isinstance(qiskit_inst, Operator) new_circ.append( qiskit_inst, - list(reversed(qargs)), - cargs, + [measurement.targets], + [measurement.c_targets], ) + new_circ.global_phase += self.input_g_phase + self._generated_g_phase return new_circ elif language == Language.MY_QLM: @@ -1832,23 +1825,23 @@ def from_other_language( q_1: ─────┤ X ├ └───┘ - >>> import cirq - >>> q0, q1 = cirq.LineQubit.range(2) - >>> cirq_circuit = cirq.Circuit() - >>> cirq_circuit.append(cirq.H(q0)) - >>> cirq_circuit.append(cirq.CNOT(q0, q1)) - >>> qcircuit2 = QCircuit.from_other_language(cirq_circuit) - >>> print(qcircuit2) # doctest: +NORMALIZE_WHITESPACE + >>> import cirq # doctest: +CIRQ + >>> q0, q1 = cirq.LineQubit.range(2) # doctest: +CIRQ + >>> cirq_circuit = cirq.Circuit() # doctest: +CIRQ + >>> cirq_circuit.append(cirq.H(q0)) # doctest: +CIRQ + >>> cirq_circuit.append(cirq.CNOT(q0, q1)) # doctest: +CIRQ + >>> qcircuit2 = QCircuit.from_other_language(cirq_circuit) # doctest: +CIRQ + >>> print(qcircuit2) # doctest: +NORMALIZE_WHITESPACE, +CIRQ ┌───┐ q_0: ┤ H ├──■── └───┘┌─┴─┐ q_1: ─────┤ X ├ └───┘ - >>> from braket.circuits import Circuit - >>> braket_circuit = Circuit().h(0).cnot(0, 1) - >>> qcircuit3 = QCircuit.from_other_language(braket_circuit) - >>> print(qcircuit3) # doctest: +NORMALIZE_WHITESPACE + >>> from braket.circuits import Circuit # doctest: +BRAKET + >>> braket_circuit = Circuit().h(0).cnot(0, 1) # doctest: +BRAKET + >>> qcircuit3 = QCircuit.from_other_language(braket_circuit) # doctest: +BRAKET + >>> print(qcircuit3) # doctest: +NORMALIZE_WHITESPACE, +BRAKET ┌───┐ q_0: ┤ H ├──■── └───┘┌─┴─┐ @@ -1856,14 +1849,14 @@ def from_other_language( └───┘ c: 2/══════════ - >>> from qat.lang.AQASM import Program, H, CNOT - >>> prog = Program() - >>> qbits = prog.qalloc(2) - >>> _ = H(qbits[0]) - >>> _ = CNOT(qbits[0], qbits[1]) - >>> myqlm_circuit = prog.to_circ() - >>> qcircuit4 = QCircuit.from_other_language(myqlm_circuit) - >>> print(qcircuit4) # doctest: +NORMALIZE_WHITESPACE + >>> from qat.lang.AQASM import Program, H, CNOT # doctest: +MYQLM + >>> prog = Program() # doctest: +MYQLM + >>> qbits = prog.qalloc(2) # doctest: +MYQLM + >>> _ = H(qbits[0]) # doctest: +MYQLM + >>> _ = CNOT(qbits[0], qbits[1]) # doctest: +MYQLM + >>> myqlm_circuit = prog.to_circ() # doctest: +MYQLM + >>> qcircuit4 = QCircuit.from_other_language(myqlm_circuit) # doctest: +MYQLM + >>> print(qcircuit4) # doctest: +NORMALIZE_WHITESPACE, +MYQLM ┌───┐ q_0: ┤ H ├──■── └───┘┌─┴─┐ @@ -1897,76 +1890,88 @@ def from_other_language( q_1: ─────┤ X ├ └───┘ """ - from braket.circuits import Circuit as braket_Circuit - from cirq.circuits.circuit import Circuit as cirq_Circuit - from cirq.circuits.moment import Moment - from qat.core.wrappers.circuit import Circuit as myQLM_Circuit - from qiskit import QuantumCircuit - - from mpqp.qasm.qasm_to_mpqp import qasm2_parse - - if isinstance(qcircuit, QuantumCircuit): - from qiskit import qasm3 - - from mpqp.qasm import open_qasm_3_to_2 - - qasm3_code = qasm3.dumps(qcircuit) - qasm2_code, phase = open_qasm_3_to_2( - str(qasm3_code), language=Language.QISKIT - ) - - qc = qasm2_parse(qasm2_code) - qc.input_g_phase = phase - - return qc - - elif isinstance(qcircuit, cirq_Circuit) or isinstance(qcircuit, Moment): - from mpqp.qasm.qasm_to_mpqp import parse_qasm2_gates - - if isinstance(qcircuit, Moment): - qcircuit = cirq_Circuit([qcircuit]) - - qasm2_code, gphase = parse_qasm2_gates(qcircuit.to_qasm()) - qc = qasm2_parse(qasm2_code) - qc.input_g_phase = gphase - return qc - - elif isinstance(qcircuit, braket_Circuit): - from braket.circuits.serialization import IRType - from braket.ir.openqasm.program_v1 import Program - - from mpqp.qasm.open_qasm_2_and_3 import open_qasm_3_to_2 - from mpqp.qasm.qasm_to_braket import ( - braket_custom_gates_to_mpqp, - braket_noise_to_mpqp, - ) + try: + from qiskit.circuit import QuantumCircuit + except ImportError: + pass + else: + if isinstance(qcircuit, QuantumCircuit): + from qiskit import qasm3 + + from mpqp.qasm import open_qasm_3_to_2 + from mpqp.qasm.qasm_to_mpqp import qasm2_parse + + qasm3_code = qasm3.dumps(qcircuit) + qasm2_code = open_qasm_3_to_2(str(qasm3_code), language=Language.QISKIT) + + qc = qasm2_parse(qasm2_code) + return qc + try: + from cirq.circuits.circuit import Circuit as cirq_Circuit + from cirq.circuits.moment import Moment + except ImportError: + pass + else: + if isinstance(qcircuit, cirq_Circuit) or isinstance(qcircuit, Moment): + from mpqp.qasm.qasm_to_mpqp import parse_qasm2_gates + from mpqp.qasm.qasm_to_mpqp import qasm2_parse + + if isinstance(qcircuit, Moment): + qcircuit = cirq_Circuit([qcircuit]) + + qasm2_code, gphase = parse_qasm2_gates(qcircuit.to_qasm()) + qc = qasm2_parse(qasm2_code) + qc.input_g_phase = gphase + + return qc + try: + from braket.circuits import Circuit as braket_Circuit + except ImportError: + pass + else: + if isinstance(qcircuit, braket_Circuit): + from braket.circuits.serialization import IRType + from braket.ir.openqasm.program_v1 import Program + + from mpqp.qasm.open_qasm_2_and_3 import open_qasm_3_to_2 + from mpqp.qasm.qasm_to_mpqp import qasm2_parse + from mpqp.qasm.qasm_to_braket import ( + braket_custom_gates_to_mpqp, + braket_noise_to_mpqp, + ) - qasm3_code = qcircuit.to_ir(IRType.OPENQASM) - if TYPE_CHECKING: - assert isinstance(qasm3_code, Program) + qasm3_code = qcircuit.to_ir(IRType.OPENQASM) + if TYPE_CHECKING: + assert isinstance(qasm3_code, Program) - custom_gates = braket_custom_gates_to_mpqp(qasm3_code.source) - noises = braket_noise_to_mpqp(qasm3_code.source) + custom_gates = braket_custom_gates_to_mpqp(qasm3_code.source) + noises = braket_noise_to_mpqp(qasm3_code.source) - qasm2_code, phase = open_qasm_3_to_2( - str(qasm3_code.source), language=Language.BRAKET - ) - qc = qasm2_parse(qasm2_code) - qc.input_g_phase = phase - qc = qc.without_measurements() - if len(custom_gates) != 0: - qc.add(custom_gates) - if len(noises) != 0: - qc.add(noises) - return qc + qasm2_code = open_qasm_3_to_2( + str(qasm3_code.source), language=Language.BRAKET + ) + qc = qasm2_parse(qasm2_code) + qc = qc.without_measurements() + if len(custom_gates) != 0: + qc.add(custom_gates) + if len(noises) != 0: + qc.add(noises) + return qc + + try: + from qat.core.wrappers.circuit import Circuit as myQLM_Circuit + except ImportError: + pass + else: + if isinstance(qcircuit, myQLM_Circuit): + from mpqp.qasm.myqlm_to_mpqp import from_myqlm_to_mpqp - elif isinstance(qcircuit, myQLM_Circuit): - from mpqp.qasm.myqlm_to_mpqp import from_myqlm_to_mpqp + return from_myqlm_to_mpqp(qcircuit) - return from_myqlm_to_mpqp(qcircuit) + if isinstance(qcircuit, str): + from mpqp.qasm.qasm_to_mpqp import qasm2_parse - elif isinstance(qcircuit, str): for line in qcircuit.split('\n'): if not line.startswith("//") and line != '': OPENQASM_VERSIONS = ("OPENQASM 2.0", "OPENQASM 3.0") @@ -1981,22 +1986,23 @@ def from_other_language( qasm2_code, gphase = parse_qasm2_gates(qcircuit) qc = qasm2_parse(qasm2_code) - qc.input_g_phase = gphase + qc.input_g_phase += gphase return qc elif line.startswith("OPENQASM 3.0"): from mpqp.qasm import open_qasm_3_to_2 - qasm2_code, phase = open_qasm_3_to_2(qcircuit) + qasm2_code = open_qasm_3_to_2(qcircuit) qc = qasm2_parse(qasm2_code) - qc.input_g_phase = phase return qc break return qasm2_parse(qcircuit) else: - raise NotImplementedError(f"Error: {type(qcircuit)} is not supported.") + raise NotImplementedError( + f"Error: {type(qcircuit)} is not supported, or sdk not installed." + ) def subs( self, values: dict[Expr | str, Complex], remove_symbolic: bool = False @@ -2072,15 +2078,13 @@ def pretty_print(self): for noise in self.noises: print(noise.info()) - qiskit_circuit = self.to_other_language(Language.QISKIT).reverse_bits() + qiskit_circuit = self.to_other_language(Language.QISKIT) if TYPE_CHECKING: assert isinstance(qiskit_circuit, QuantumCircuit) print(qiskit_circuit.draw(output="text", fold=0)) def __str__(self) -> str: - qiskit_circ = self.to_other_language( - Language.QISKIT, printing=True - ).reverse_bits() + qiskit_circ = self.to_other_language(Language.QISKIT, printing=True) if TYPE_CHECKING: from qiskit import QuantumCircuit diff --git a/mpqp/core/instruction/gates/custom_gate.py b/mpqp/core/instruction/gates/custom_gate.py index 55c80218..7632dd38 100644 --- a/mpqp/core/instruction/gates/custom_gate.py +++ b/mpqp/core/instruction/gates/custom_gate.py @@ -72,9 +72,6 @@ def matrix(self) -> Matrix: # TODO: move this to `to_canonical_matrix` and check for the usages return self.definition.matrix - def to_matrix(self, desired_gate_size: int = 0): - return self.matrix - def to_canonical_matrix(self): return self.matrix @@ -131,12 +128,13 @@ def to_other_language( qiskit_circ = QuantumCircuit(nb_qubits) instr = self.to_other_language(Language.QISKIT) if TYPE_CHECKING: - from qiskit.quantum_info.operators import Operator as QiskitOperator + from qiskit.circuit.library import UnitaryGate + + assert isinstance(instr, UnitaryGate) - assert isinstance(instr, QiskitOperator) qiskit_circ.unitary( instr, - list(reversed(self.targets)), # dang qiskit qubits order + list(reversed(self.targets)), self.label, ) @@ -172,6 +170,7 @@ def decompose(self) -> "QCircuit": >>> U = np.array([[0,1], [1,0]]) >>> gate = CustomGate(U, [0]) >>> print(gate.decompose()) # doctest: +NORMALIZE_WHITESPACE + global phase: π/2 ┌─────────┐┌───────┐┌──────────┐ q: ┤ Rz(π/2) ├┤ Ry(π) ├┤ Rz(-π/2) ├ └─────────┘└───────┘└──────────┘ diff --git a/mpqp/core/instruction/gates/gate.py b/mpqp/core/instruction/gates/gate.py index fef2c85f..5477adec 100644 --- a/mpqp/core/instruction/gates/gate.py +++ b/mpqp/core/instruction/gates/gate.py @@ -202,6 +202,16 @@ def to_dict(self) -> dict[str, int | str | list[str] | float | None]: """ result = {} for attr_name in dir(self): + if attr_name == "cirq_gate": + try: + import cirq # pyright: ignore[reportUnusedImport] + except ImportError: + continue + if attr_name == "braket_gate": + try: + import braket # pyright: ignore[reportUnusedImport] + except ImportError: + continue if ( attr_name not in {'_abc_impl'} and not attr_name.startswith("__") diff --git a/mpqp/core/instruction/measurement/expectation_value.py b/mpqp/core/instruction/measurement/expectation_value.py index 908ad45f..bb48a257 100644 --- a/mpqp/core/instruction/measurement/expectation_value.py +++ b/mpqp/core/instruction/measurement/expectation_value.py @@ -290,7 +290,7 @@ def to_other_language( >>> obs = Observable([0.7, -1, 1, 1]) >>> obs_qiskit = obs.to_other_language(Language.QISKIT) >>> obs_qiskit.to_list() # doctest: +NORMALIZE_WHITESPACE - [('II', (0.425+0j)), ('IZ', (0.425+0j)), ('ZI', (-0.575+0j)), ('ZZ', (0.425+0j))] + [('II', (0.425+0j)), ('IZ', (-0.575+0j)), ('ZI', (0.425+0j)), ('ZZ', (0.425+0j))] """ # TODO: use PauliString instead of matrix @@ -300,7 +300,9 @@ def to_other_language( if self._pauli_string: return self.pauli_string.to_other_language(Language.QISKIT) else: - return SparsePauliOp.from_operator(Operator(self.matrix)) + return SparsePauliOp.from_operator( + Operator(self.matrix).reverse_qargs() + ) elif language == Language.MY_QLM: from qat.core.wrappers.observable import Observable as QLMObservable diff --git a/mpqp/core/instruction/measurement/pauli_string.py b/mpqp/core/instruction/measurement/pauli_string.py index 1182423e..ce39f3dd 100644 --- a/mpqp/core/instruction/measurement/pauli_string.py +++ b/mpqp/core/instruction/measurement/pauli_string.py @@ -693,21 +693,21 @@ def from_other_language( # TODO: better typing using overloads output will be a list of :class:`PauliString`. Examples: - >>> from cirq import LineQubit, PauliSum, X as Cirq_X, Y as Cirq_Y, Z as Cirq_Z - >>> a, b, c = LineQubit.range(3) - >>> cirq_ps = 0.5 * Cirq_Z(a) * 0.5 * Cirq_Y(b) + 2 * Cirq_X(c) - >>> PauliString.from_other_language(cirq_ps) + >>> from cirq import LineQubit, PauliSum, X as Cirq_X, Y as Cirq_Y, Z as Cirq_Z # doctest: +CIRQ + >>> a, b, c = LineQubit.range(3) # doctest: +CIRQ + >>> cirq_ps = 0.5 * Cirq_Z(a) * 0.5 * Cirq_Y(b) + 2 * Cirq_X(c) # doctest: +CIRQ + >>> PauliString.from_other_language(cirq_ps) # doctest: +CIRQ 0.25*pZ@pY@pI + 2*pI@pI@pX - >>> from braket.circuits.observables import ( + >>> from braket.circuits.observables import ( # doctest: +BRAKET ... Sum as BraketSum, ... I as Braket_I, ... X as Braket_X, ... Y as Braket_Y, ... Z as Braket_Z, ... ) - >>> braket_ps = 0.25 * Braket_Z() @ Braket_Y() @ Braket_I() + 2 * Braket_I() @ Braket_I() @ Braket_X() - >>> PauliString.from_other_language(braket_ps) + >>> braket_ps = 0.25 * Braket_Z() @ Braket_Y() @ Braket_I() + 2 * Braket_I() @ Braket_I() @ Braket_X() # doctest: +BRAKET + >>> PauliString.from_other_language(braket_ps) # doctest: +BRAKET 0.25*pZ@pY@pI + 2*pI@pI@pX >>> from qiskit.quantum_info import SparsePauliOp @@ -716,9 +716,9 @@ def from_other_language( # TODO: better typing using overloads 2*pI@pI@pX + 0.25*pZ@pY@pI - >>> from qat.core.wrappers.observable import Term - >>> my_qml_ps = [Term(0.25, "ZY", [0, 1]), Term(2, "X", [2])] - >>> PauliString.from_other_language(my_qml_ps) + >>> from qat.core.wrappers.observable import Term # doctest: +MYQLM + >>> my_qml_ps = [Term(0.25, "ZY", [0, 1]), Term(2, "X", [2])] # doctest: +MYQLM + >>> PauliString.from_other_language(my_qml_ps) # doctest: +MYQLM 0.25*pZ@pY@pI + 2*pI@pI@pX """ @@ -729,42 +729,60 @@ def from_other_language( # TODO: better typing using overloads "Cannot parse non-homogeneous types when `pauli` is a `list`." ) - from braket.circuits.observables import Observable as BraketObservable - from cirq.ops.gate_operation import GateOperation as CirqGateOperation - from cirq.ops.linear_combinations import PauliSum as CirqPauliSum - from cirq.ops.pauli_string import PauliString as CirqPauliString - from qat.core.wrappers.observable import Term from qiskit.quantum_info import SparsePauliOp if isinstance(pauli, SparsePauliOp): return PauliString._from_qiskit(pauli) - elif isinstance(pauli, BraketObservable): - return PauliString._from_braket(pauli) - elif isinstance(pauli, Term): - return PauliString._from_my_qml(pauli) - elif isinstance(pauli, list) and isinstance(pauli[0], Term): - for term in pauli: - min_dimension = ( - max(max(term.qbits) + 1, min_dimension) - if len(term.qbits) > 0 - else min_dimension + + try: + from braket.circuits.observables import Observable as BraketObservable + except ImportError: + pass + else: + if isinstance(pauli, BraketObservable): + return PauliString._from_braket(pauli) + + try: + from qat.core.wrappers.observable import Term + except ImportError: + pass + else: + if isinstance(pauli, Term): + return PauliString._from_my_qml(pauli) + elif isinstance(pauli, list) and isinstance(pauli[0], Term): + for term in pauli: + min_dimension = ( + max(max(term.qbits) + 1, min_dimension) + if len(term.qbits) > 0 + else min_dimension + ) + pauli_string = PauliString() + for term in pauli: + pauli_string += PauliString._from_my_qml(term, min_dimension) + return pauli_string + + try: + from cirq.ops.gate_operation import GateOperation as CirqGateOperation + from cirq.ops.linear_combinations import PauliSum as CirqPauliSum + from cirq.ops.pauli_string import PauliString as CirqPauliString + except ImportError: + pass + else: + if isinstance(pauli, (CirqPauliSum, CirqPauliString, CirqGateOperation)): + return PauliString._from_cirq(pauli, min_dimension) + elif isinstance(pauli, list) and isinstance(pauli[0], CirqPauliString): + min_dimension = max( + max(map(PauliString._get_dimension_cirq_pauli, pauli)), + min_dimension, ) - pauli_string = PauliString() - for term in pauli: - pauli_string += PauliString._from_my_qml(term, min_dimension) - return pauli_string - elif isinstance(pauli, (CirqPauliSum, CirqPauliString, CirqGateOperation)): - return PauliString._from_cirq(pauli, min_dimension) - elif isinstance(pauli, list) and isinstance(pauli[0], CirqPauliString): - min_dimension = max( - max(map(PauliString._get_dimension_cirq_pauli, pauli)), min_dimension - ) - return [ - PauliString._from_cirq(pauli_mono, min_dimension) - for pauli_mono in pauli - ] + return [ + PauliString._from_cirq(pauli_mono, min_dimension) + for pauli_mono in pauli + ] - raise NotImplementedError(f"Unsupported input type: {type(pauli)}.") + raise NotImplementedError( + f"Unsupported input type: {type(pauli)}, or sdk not installed." + ) def to_other_language( self, language: Language, circuit: Optional[CirqCircuit] = None @@ -790,9 +808,9 @@ def to_other_language( Example: >>> ps = pX@ pX@ pI + pI @ pY@ pI + pI @ pI @ pZ - >>> print(ps.to_other_language(Language.CIRQ)) + >>> print(ps.to_other_language(Language.CIRQ)) # doctest: +CIRQ 1.000*X(q(0))*X(q(1))+1.000*Y(q(1))+1.000*Z(q(2)) - >>> for term in ps.to_other_language(Language.MY_QLM): + >>> for term in ps.to_other_language(Language.MY_QLM): # doctest: +MYQLM ... print(term.op, term.qbits) XX [0, 1] Y [1] @@ -800,7 +818,7 @@ def to_other_language( >>> ps.to_other_language(Language.QISKIT) SparsePauliOp(['XXI', 'IYI', 'IIZ'], coeffs=[1.+0.j, 1.+0.j, 1.+0.j]) - >>> for tensor in ps.to_other_language(Language.BRAKET).summands: + >>> for tensor in ps.to_other_language(Language.BRAKET).summands: # doctest: +BRAKET ... print(tensor.coefficient, "".join(a.name for a in tensor.factors)) 1 XXI 1 IYI @@ -865,7 +883,7 @@ def to_dict(self) -> dict[str, str]: else: coef: "Coef" = ( result_dict[atom_str] + mono.coef - ) # pyright: ignore[reportOperatorIssue, reportAssignmentType] + ) # pyright: ignore[reportOperatorIssue] result_dict[atom_str] = coef return {k: str(result_dict[k]) for k in sorted(result_dict)} @@ -1013,9 +1031,7 @@ def __add__(self, other: "PauliString") -> PauliString: return res def __imul__(self, other: "Coef") -> PauliStringMonomial: - new_coef: "Coef" = ( - self.coef * other - ) # pyright: ignore[reportOperatorIssue, reportAssignmentType] + new_coef: "Coef" = self.coef * other # pyright: ignore[reportOperatorIssue] self.coef = new_coef return self @@ -1024,9 +1040,7 @@ def __imul__(self, other: "Coef") -> PauliStringMonomial: return res def __itruediv__(self, other: "Coef") -> PauliStringMonomial: - new_coef: "Coef" = ( - self.coef / other - ) # pyright: ignore[reportOperatorIssue, reportAssignmentType] + new_coef: "Coef" = self.coef / other # pyright: ignore[reportOperatorIssue] self.coef = new_coef return self @@ -1042,7 +1056,7 @@ def __imatmul__(self, other: PauliString) -> PauliString: elif isinstance(other, PauliStringMonomial): new_coef: "Coef" = ( self.coef * other.coef - ) # pyright: ignore[reportOperatorIssue, reportAssignmentType] + ) # pyright: ignore[reportOperatorIssue] self.coef = new_coef self.atoms.extend(other.atoms) return self @@ -1303,7 +1317,7 @@ def __itruediv__(self, other: "Coef") -> PauliStringMonomial: def __truediv__(self, other: "Coef") -> PauliStringMonomial: return PauliStringMonomial( - 1 / other, # pyright: ignore[reportArgumentType, reportOperatorIssue] + 1 / other, # pyright: ignore[reportOperatorIssue] [self], ) diff --git a/mpqp/execution/connection/aws_connection.py b/mpqp/execution/connection/aws_connection.py index bdc0aaad..bb840b3d 100644 --- a/mpqp/execution/connection/aws_connection.py +++ b/mpqp/execution/connection/aws_connection.py @@ -200,6 +200,56 @@ def configure_account_iam() -> tuple[str, list[Any]]: return "IAM configuration successful.", [] +def delete_aws_braket_account() -> tuple[str, list[Any]]: + """Deletes the locally stored AWS Braket configuration.""" + from pathlib import Path + import configparser + + decision = input( + colored( + "This will delete the local AWS Braket configuration (default profile). Continue? [y/N] ", + "yellow", + ) + ) + if decision.lower().strip() != "y": + return "Canceled.", [] + + credentials_file = Path.home() / ".aws" / "credentials" + config_file = Path.home() / ".aws" / "config" + + try: + if credentials_file.exists(): + parser = configparser.ConfigParser() + parser.read(credentials_file) + + if parser.has_section("default"): + parser.remove_section("default") + with open(credentials_file, "w") as f: + parser.write(f) + except Exception as err: + print(colored(f"Failed to update AWS credentials file: {err}", "red")) + + try: + if config_file.exists(): + parser = configparser.ConfigParser() + parser.read(config_file) + + if parser.has_section("default"): + parser.remove_section("default") + with open(config_file, "w") as f: + parser.write(f) + except Exception as err: + print(colored(f"Failed to update AWS config file: {err}", "red")) + + save_env_variable("BRAKET_CONFIGURED", "False") + save_env_variable("BRAKET_AUTH_METHOD", "") + save_env_variable("AWS_DEFAULT_REGION", "") + + print(colored("AWS Braket account deleted.", "green")) + input("Press 'Enter' to continue") + return "AWS Braket account deleted.", [] + + def get_user_sso_credentials() -> Union[dict[str, str], None]: from getpass import getpass diff --git a/mpqp/execution/connection/azure_connection.py b/mpqp/execution/connection/azure_connection.py index 42b91dc1..f3ed77a4 100644 --- a/mpqp/execution/connection/azure_connection.py +++ b/mpqp/execution/connection/azure_connection.py @@ -81,6 +81,26 @@ def get_azure_account_info() -> str: return " AZURE_RESOURCE_ID: " + display + "\n AZURE_LOCATION: " + azure_location +def delete_azure_account(): + """Deletes the locally stored Azure Quantum account configuration.""" + decision = input( + colored( + "This will delete the local Azure Quantum configuration. Continue? [y/N] ", + "yellow", + ) + ) + if decision.lower().strip() != "y": + return "Canceled.", [] + + save_env_variable("AZURE_RESOURCE_ID", "") + save_env_variable("AZURE_LOCATION", "") + save_env_variable("AZURE_CONFIGURED", "False") + + print(colored("Azure Quantum account deleted.", "green")) + input("Press 'Enter' to continue") + return "Azure account deleted.", [] + + def test_connection(resource_id: str, Location: str) -> bool: """Test the connection to Azure service. diff --git a/mpqp/execution/connection/ibm_connection.py b/mpqp/execution/connection/ibm_connection.py index aa1718dd..80c7d4c2 100644 --- a/mpqp/execution/connection/ibm_connection.py +++ b/mpqp/execution/connection/ibm_connection.py @@ -15,12 +15,12 @@ Runtime_Service = None -def config_ibm_account(token: str): +def config_ibm_account(token: str, channel: str = "ibm_quantum"): """Configure and save locally IBM Quantum account's information. Args: token: IBM Quantum API token. - + channel: The channel to use for the account (default is "ibm_quantum"). Raises: IBMRemoteExecutionError: If the account could not be saved. """ @@ -28,10 +28,13 @@ def config_ibm_account(token: str): try: QiskitRuntimeService.save_account( - channel="ibm_quantum", token=token, overwrite=True + channel=channel, + token=token, + overwrite=True, # pyright: ignore[reportArgumentType] ) save_env_variable("IBM_CONFIGURED", "True") save_env_variable("IBM_TOKEN", token) + save_env_variable("IBM_CHANNEL", channel) except Exception as err: # if an error occurred, we put False in the mpqp config file save_env_variable("IBM_CONFIGURED", "False") @@ -58,12 +61,24 @@ def setup_ibm_account(): getpass("Press 'Enter' to continue") return "", [] old_token = get_env_variable("IBM_TOKEN") - config_ibm_account(token) + + from qiskit_ibm_runtime.accounts.management import ( + _DEFAULT_CHANNEL_TYPE, + ) # pyright: ignore[ reportPrivateUsage] + + channel = input(f"Enter the channel (default {_DEFAULT_CHANNEL_TYPE}): ") + if channel == "": + channel = _DEFAULT_CHANNEL_TYPE + print(colored(f"set to {_DEFAULT_CHANNEL_TYPE}", "yellow")) + old_channel = get_env_variable("IBM_CHANNEL") + + assert channel is not None + config_ibm_account(token, channel) if test_connection(): return "IBMQ account correctly configured", [] else: if was_configured: - config_ibm_account(old_token) + config_ibm_account(old_token, old_channel) else: save_env_variable("IBM_CONFIGURED", "False") getpass("Press 'Enter' to continue") @@ -81,7 +96,7 @@ def test_connection() -> bool: global Runtime_Service try: - Runtime_Service = QiskitRuntimeService(channel="ibm_quantum") + Runtime_Service = QiskitRuntimeService() except IBMNotAuthorizedError as err: if "Login failed" in str(err): print(colored("Wrong credentials", "red")) @@ -118,7 +133,7 @@ def get_QiskitRuntimeService() -> "QiskitRuntimeService": "Error when instantiating QiskitRuntimeService. No IBM account configured." ) try: - Runtime_Service = QiskitRuntimeService(channel="ibm_quantum") + Runtime_Service = QiskitRuntimeService() except Exception as err: raise IBMRemoteExecutionError( "Error when instantiating QiskitRuntimeService (probably wrong token saved " @@ -152,6 +167,38 @@ def get_active_account_info() -> str: Verify: {account["verify"]}""" +def delete_ibm_account(): + """Deletes the locally stored IBM Quantum account configuration.""" + from qiskit_ibm_runtime import QiskitRuntimeService + + global Runtime_Service + + decision = input( + colored( + "This will delete the local IBM Quantum configuration. Continue? [y/N] ", + "yellow", + ) + ) + if decision.lower().strip() != "y": + return "Canceled.", [] + + try: + QiskitRuntimeService.delete_account() + except Exception: + pass + + save_env_variable("IBM_CONFIGURED", "False") + save_env_variable("IBM_TOKEN", "") + save_env_variable("IBM_CHANNEL", "") + + Runtime_Service = None + + print(colored("IBM Quantum account deleted.", "green")) + input("Press 'Enter' to continue") + + return "IBM account deleted", [] + + def get_backend(device: IBMDevice) -> "BackendV2": """Retrieves the corresponding ``qiskit`` remote device. diff --git a/mpqp/execution/connection/ionq_connection.py b/mpqp/execution/connection/ionq_connection.py index 7be02f2f..d84b69bd 100644 --- a/mpqp/execution/connection/ionq_connection.py +++ b/mpqp/execution/connection/ionq_connection.py @@ -37,6 +37,26 @@ def test_ionq_connection(key: Optional[str] = None) -> bool: return False +def delete_ionq_account(): + decision = input( + colored( + "This will delete the local IonQ API key configuration. Continue? [y/N] ", + "yellow", + ) + ) + if decision.lower().strip() != "y": + return "Canceled.", [] + + from mpqp.environment.env_manager import save_env_variable + + save_env_variable("IONQ_API_KEY", "") + save_env_variable("IONQ_API_KEY_CONFIGURED", "False") + + print(colored("IonQ account deleted.", "green")) + input("Press 'Enter' to continue") + return "IonQ account deleted.", [] + + def get_ionq_account_info() -> str: """Get the IonQ API key from the environment variables. diff --git a/mpqp/execution/connection/qlm_connection.py b/mpqp/execution/connection/qlm_connection.py index f9dce628..1894b724 100644 --- a/mpqp/execution/connection/qlm_connection.py +++ b/mpqp/execution/connection/qlm_connection.py @@ -51,12 +51,10 @@ def config_qlm_account(username: str, password: str, global_config: bool) -> boo if global_config: print("we are in the global part") with open(netrc_path, "w") as file: - file.write( - f"""\ + file.write(f"""\ machine qlm35e.neasqc.eu login {username} -password {password}""" - ) +password {password}""") # Set the permissions to read and right for user only os.chmod(netrc_path, 0o600) else: @@ -150,6 +148,41 @@ def get_all_job_ids() -> list[str]: return [] +def delete_qlm_account() -> tuple[str, list[Any]]: + """Deletes the locally stored QLM account configuration.""" + global QLM_connection + + decision = input( + colored( + "This will delete the local QLM account configuration. Continue? [y/N] ", + "yellow", + ) + ) + if decision.lower().strip() != "y": + return "Canceled.", [] + + netrc_path = os.path.expanduser("~") + "/.netrc" + try: + if os.path.exists(netrc_path): + remove_decision = input( + "'~/.netrc' exists. Do you want to remove it? [y/N] " + ) + if remove_decision.lower().strip() == "y": + os.remove(netrc_path) + except Exception as err: + print(colored(f"Could not remove ~/.netrc: {err}", "red")) + + save_env_variable("QLM_USER", "") + save_env_variable("QLM_PASSWD", "") + save_env_variable("QLM_CONFIGURED", "False") + + QLM_connection = None + + print(colored("QLM account deleted.", "green")) + input("Press 'Enter' to continue") + return "QLM account deleted", [] + + def get_QLMaaSConnection(): """Connects to the QLM and returns the QLMaaSConnection. If the connection was already established, we only return the one stored in global variable, diff --git a/mpqp/execution/providers/azure.py b/mpqp/execution/providers/azure.py index f3cfa848..87641603 100644 --- a/mpqp/execution/providers/azure.py +++ b/mpqp/execution/providers/azure.py @@ -183,7 +183,7 @@ def extract_samples(job: Job, result: QiskitResult) -> list[Sample]: job_data = result.data() return [ Sample( - bin_str="".join(map(str, state)), + bin_str="".join(map(str, state))[::-1], nb_qubits=job.circuit.nb_qubits, count=int(count), ) diff --git a/mpqp/execution/providers/ibm.py b/mpqp/execution/providers/ibm.py index 147043e2..684d574d 100644 --- a/mpqp/execution/providers/ibm.py +++ b/mpqp/execution/providers/ibm.py @@ -704,7 +704,7 @@ def extract_result( counts = counts.get_counts() if counts else {} data = [ Sample( - bin_str=item, + bin_str=item[::-1], count=counts[item], nb_qubits=job.circuit.nb_qubits, ) @@ -746,17 +746,18 @@ def extract_result( shots = result.metadata[0]["shots"] if "shots" in result.metadata[0] else 0 for i in range(len(result.values)): + qiskit_order = len(result.values) - i - 1 label = ( job.measure.observables[i].label if isinstance(job.measure, ExpectationMeasure) else f"ibm_obs_{i}" ) variance = ( - result.metadata[i]["variance"] - if "variance" in result.metadata[i] + result.metadata[qiskit_order]["variance"] + if "variance" in result.metadata[qiskit_order] else None ) - exp_values_dict[label] = result.values[i] + exp_values_dict[label] = result.values[qiskit_order] errors_dict[label] = variance return Result(job, exp_values_dict, errors_dict, shots) @@ -794,12 +795,12 @@ def extract_result( ) if job.job_type == JobType.STATE_VECTOR: - vector = np.array(result.get_statevector()) + vector = np.array(result.get_statevector().reverse_qargs()) # type: ignore[reportUnnecessaryIsInstance] state_vector = StateVector( - vector, # pyright: ignore[reportArgumentType] + vector, job.circuit.nb_qubits, ) - return Result(job, state_vector, 0, 0) + return Result(job, state_vector, 0, 0, False) elif job.job_type == JobType.SAMPLE: if TYPE_CHECKING: assert job.measure is not None @@ -830,7 +831,7 @@ def get_result_from_ibm_job_id(job_id: str) -> Result: Returns: The result (or batch of result) converted to our format. """ - from qiskit.providers import BackendV1, BackendV2 + from qiskit.providers import BackendV2 connector = get_QiskitRuntimeService() ibm_job = ( @@ -854,7 +855,7 @@ def get_result_from_ibm_job_id(job_id: str) -> Result: result = ibm_job.result() backend = ibm_job.backend() if TYPE_CHECKING: - assert isinstance(backend, (BackendV1, BackendV2)) + assert isinstance(backend, BackendV2) ibm_device = IBMDevice(backend.name) return extract_result(result, None, ibm_device) @@ -875,7 +876,7 @@ def extract_samples(job: Job, result: QiskitResult) -> list[Sample]: job_data = result.data() return [ Sample( - bin_str=item, + bin_str=item[::-1], count=counts[item], nb_qubits=job.circuit.nb_qubits, probability=( diff --git a/mpqp/execution/result.py b/mpqp/execution/result.py index 4f679741..f433b446 100644 --- a/mpqp/execution/result.py +++ b/mpqp/execution/result.py @@ -291,6 +291,7 @@ def __init__( data: float | dict["str", float] | StateVector | list[Sample], errors: Optional[float | dict[Any, Any]] = None, shots: int = 0, + g_phase_handling: bool = True, ): self.job = job """See parameter description.""" @@ -326,7 +327,7 @@ def __init__( job.circuit.input_g_phase + job.circuit._generated_g_phase # pyright: ignore[reportPrivateUsage] ) - if gphase != 0: + if g_phase_handling and gphase != 0: # Reverse the global phase introduced when using CustomGate, due to Qiskit decomposition in QASM2 self._state_vector.vector *= np.exp(1j * gphase) self._probabilities = data.probabilities diff --git a/mpqp/execution/runner.py b/mpqp/execution/runner.py index 7755d13d..b07a4e7e 100644 --- a/mpqp/execution/runner.py +++ b/mpqp/execution/runner.py @@ -337,19 +337,19 @@ def run( ... [X(0), CNOT(0, 1), BasisMeasure([0, 1], shots=1000)], ... label="X CNOT circuit", ... ) - >>> result = run(c, IBMDevice.AER_SIMULATOR) - >>> print(result) + >>> result = run(c, IBMDevice.AER_SIMULATOR) # doctest: +QISKIT + >>> print(result) # doctest: +QISKIT Result: X CNOT circuit, IBMDevice, AER_SIMULATOR Counts: [0, 0, 0, 1000] Probabilities: [0, 0, 0, 1] Samples: State: 11, Index: 3, Count: 1000, Probability: 1 Error: None - >>> batch_result = run( + >>> batch_result = run( # doctest: +MYQLM, +BRAKET ... c, ... [ATOSDevice.MYQLM_PYLINALG, AWSDevice.BRAKET_LOCAL_SIMULATOR] ... ) - >>> print(batch_result) + >>> print(batch_result) # doctest: +MYQLM, +BRAKET BatchResult: 2 results Result: X CNOT circuit, ATOSDevice, MYQLM_PYLINALG Counts: [0, 0, 0, 1000] @@ -367,8 +367,8 @@ def run( ... [X(0), X(1), BasisMeasure([0, 1], shots=1000)], ... label="X circuit", ... ) - >>> result = run([c,c2], IBMDevice.AER_SIMULATOR) - >>> print(result) + >>> result = run([c,c2], IBMDevice.AER_SIMULATOR) # doctest: +QISKIT + >>> print(result) # doctest: +QISKIT BatchResult: 2 results Result: X CNOT circuit, IBMDevice, AER_SIMULATOR Counts: [0, 0, 0, 1000] diff --git a/mpqp/execution/simulated_devices.py b/mpqp/execution/simulated_devices.py index b1adcd94..7997a2f2 100644 --- a/mpqp/execution/simulated_devices.py +++ b/mpqp/execution/simulated_devices.py @@ -63,8 +63,12 @@ def to_noise_model(self) -> "Qiskit_NoiseModel": @staticmethod def get_ibm_fake_providers() -> list[tuple[str, type["FakeBackendV2"]]]: - from qiskit_ibm_runtime import fake_provider - from qiskit_ibm_runtime.fake_provider.fake_backend import FakeBackendV2 + try: + from qiskit_ibm_runtime import fake_provider + from qiskit_ibm_runtime.fake_provider.fake_backend import FakeBackendV2 + except ImportError: + # TODO: if qiskit_ibm_runtime not install, do we want to return [] ? + return [] fake_imports = fake_provider.__dict__ return [ diff --git a/mpqp/execution/vqa/vqa.py b/mpqp/execution/vqa/vqa.py index 031caaf7..b9c9719f 100644 --- a/mpqp/execution/vqa/vqa.py +++ b/mpqp/execution/vqa/vqa.py @@ -19,7 +19,7 @@ T1 = TypeVar("T1") T2 = TypeVar("T2") -OptimizerInput = Union[list[float], npt.NDArray[np.float_]] +OptimizerInput = Union[list[float], npt.NDArray[np.float64]] OptimizableFunc = Union[partial[float], Callable[[OptimizerInput], float]] OptimizerOptions = dict[str, Any] OptimizerCallable = Callable[ @@ -28,7 +28,7 @@ ] OptimizerCallback = Union[ Callable[[OptimizeResult], None], - Callable[[Union[list[float], npt.NDArray[np.float_], tuple[float, ...]]], None], + Callable[[Union[list[float], npt.NDArray[np.float64], tuple[float, ...]]], None], ] @@ -97,7 +97,7 @@ def minimize( ... shots=0, ... ), ... ]) - >>> minimize( + >>> minimize( # doctest: +MYQLM ... circuit, ... Optimizer.BFGS, ... ATOSDevice.MYQLM_PYLINALG, @@ -106,14 +106,14 @@ def minimize( (-0.9999999999999996, array([0., 0.])) - >>> def cost_func(params): + >>> def cost_func(params): # doctest: +MYQLM ... run_res = run( ... circuit, ... ATOSDevice.MYQLM_PYLINALG, ... {alpha: params[0], beta: params[1]} ... ) ... return 1 - run_res.expectation_values ** 2 - >>> minimize( + >>> minimize( # doctest: +MYQLM ... cost_func, ... Optimizer.BFGS, ... nb_params=2, diff --git a/mpqp/local_storage/queries.py b/mpqp/local_storage/queries.py index 7b1befbf..a377c354 100644 --- a/mpqp/local_storage/queries.py +++ b/mpqp/local_storage/queries.py @@ -335,12 +335,10 @@ def fetch_jobs_with_result_and_job( json.dumps(repr(res.job.measure)) if res.job.measure else None ) - job_filters.append( - """ + job_filters.append(""" (results.data is ? AND results.error is ? AND results.shots is ? AND jobs.type is ? AND jobs.circuit is ? AND jobs.device is ? AND jobs.measure is ?) - """ - ) + """) params.extend( [ data_json, @@ -413,11 +411,9 @@ def fetch_jobs_with_result(result: Result | BatchResult | list[Result]) -> list[ json.dumps(repr(res.error)) if res.error is not None else None ) - job_filters.append( - """ + job_filters.append(""" (results.data is ? AND results.error is ? AND results.shots is ?) - """ - ) + """) params.extend( [ data_json, @@ -497,12 +493,10 @@ def fetch_results_with_result_and_job( json.dumps(repr(res.job.measure)) if res.job.measure else None ) - result_filters.append( - """ + result_filters.append(""" (results.data is ? AND results.error is ? AND results.shots is ? AND jobs.type is ? AND jobs.circuit is ? AND jobs.device is ? AND jobs.measure is ?) - """ - ) + """) params.extend( [ data_json, @@ -571,11 +565,9 @@ def fetch_results_with_job(jobs: Job | list[Job]) -> list[DictDB]: circuit_json = json.dumps(repr(job.circuit)) measure_json = json.dumps(repr(job.measure)) if job.measure else None - result_filters.append( - """ + result_filters.append(""" (jobs.type is ? AND jobs.circuit is ? AND jobs.device is ? AND jobs.measure is ?) - """ - ) + """) params.extend( [ job.job_type.name, @@ -647,11 +639,9 @@ def fetch_results_with_result( json.dumps(repr(res.error)) if res.error is not None else None ) - result_filters.append( - """ + result_filters.append(""" (results.data is ? AND results.error is ? AND results.shots is ?) - """ - ) + """) params.extend([data_json, error_json, res.shots]) query = f""" diff --git a/mpqp/local_storage/setup.py b/mpqp/local_storage/setup.py index aa20a830..2dd4d502 100644 --- a/mpqp/local_storage/setup.py +++ b/mpqp/local_storage/setup.py @@ -49,12 +49,10 @@ def wrapper(*args: Any, **kwargs: dict[str, Any]) -> T: db_version = get_database_version() if db_version != DATABASE_VERSION: - raise RuntimeError( - f"""\ + raise RuntimeError(f"""\ Database version {db_version} is outdated. Current supported version: {DATABASE_VERSION}. Automated migration is not yet supported, please contact library authors to get\ - help for the migration.""" - ) + help for the migration.""") return func(*args, **kwargs) @@ -90,8 +88,7 @@ def setup_local_storage(path: Optional[str] = None): cursor = connection.cursor() # Create the jobs table - cursor.execute( - ''' + cursor.execute(''' CREATE TABLE IF NOT EXISTS jobs ( id INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT NOT NULL, @@ -102,12 +99,10 @@ def setup_local_storage(path: Optional[str] = None): status TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP ) - ''' - ) + ''') # Create the results table - cursor.execute( - ''' + cursor.execute(''' CREATE TABLE IF NOT EXISTS results ( id INTEGER PRIMARY KEY AUTOINCREMENT, job_id INTEGER NOT NULL, @@ -116,17 +111,14 @@ def setup_local_storage(path: Optional[str] = None): shots INTEGER DEFAULT 0, created_at TEXT DEFAULT CURRENT_TIMESTAMP ) - ''' - ) + ''') - cursor.execute( - ''' + cursor.execute(''' CREATE TABLE IF NOT EXISTS version ( id INTEGER PRIMARY KEY CHECK (id = 1), -- Ensures only one row exists version VARCHAR ) - ''' - ) + ''') cursor.execute( "INSERT OR IGNORE INTO version (id, version) VALUES (1, ?)", (DATABASE_VERSION,) diff --git a/mpqp/noise/noise_model.py b/mpqp/noise/noise_model.py index 3cba9d4a..b6661680 100644 --- a/mpqp/noise/noise_model.py +++ b/mpqp/noise/noise_model.py @@ -342,7 +342,7 @@ class Depolarizing(DimensionalNoiseModel): Depolarizing(0.05, [0, 1], dimension=2) Depolarizing(0.12, [2], gates=[H, Rx, Ry, Rz]) Depolarizing(0.05, [0, 1, 2], dimension=2, gates=[CNOT, CZ]) - >>> print(circuit.to_other_language(Language.BRAKET)) # doctest: +NORMALIZE_WHITESPACE + >>> print(circuit.to_other_language(Language.BRAKET)) # doctest: +NORMALIZE_WHITESPACE +BRAKET T : │ 0 │ ┌───┐ ┌────────────┐ ┌────────────┐ q0 : ─┤ H ├─┤ DEPO(0.01) ├─┤ DEPO(0.32) ├──────────────── @@ -354,7 +354,7 @@ class Depolarizing(DimensionalNoiseModel): q2 : ─┤ H ├─┤ DEPO(0.12) ├─┤ DEPO(0.01) ├─┤ DEPO(0.32) ├─ └───┘ └────────────┘ └────────────┘ └────────────┘ T : │ 0 │ - >>> print(circuit.to_other_language(Language.CIRQ)) # doctest: +NORMALIZE_WHITESPACE + >>> print(circuit.to_other_language(Language.CIRQ)) # doctest: +NORMALIZE_WHITESPACE +CIRQ q_0: ───I───H───D(0.01)───D(0.32)───────────── q_1: ───I───H───D(0.01)───D(0.32)───────────── q_2: ───I───H───D(0.12)───D(0.01)───D(0.32)─── @@ -402,17 +402,17 @@ def to_other_language( language: Enum representing the target language. Examples: - >>> Depolarizing(0.3, [0,1], dimension=1).to_other_language(Language.BRAKET) + >>> Depolarizing(0.3, [0,1], dimension=1).to_other_language(Language.BRAKET) # doctest: +BRAKET Depolarizing('probability': 0.3, 'qubit_count': 1) - >>> Depolarizing(0.3, [0,1], dimension=1).to_other_language(Language.QISKIT).to_quantumchannel() + >>> Depolarizing(0.3, [0,1], dimension=1).to_other_language(Language.QISKIT).to_quantumchannel() # doctest: +QISKIT SuperOp([[0.85+0.j, 0. +0.j, 0. +0.j, 0.15+0.j], [0. +0.j, 0.7 +0.j, 0. +0.j, 0. +0.j], [0. +0.j, 0. +0.j, 0.7 +0.j, 0. +0.j], [0.15+0.j, 0. +0.j, 0. +0.j, 0.85+0.j]], input_dims=(2,), output_dims=(2,)) - >>> print(Depolarizing(0.3, [0,1], dimension=1).to_other_language(Language.MY_QLM)) # doctest: +NORMALIZE_WHITESPACE + >>> print(Depolarizing(0.3, [0,1], dimension=1).to_other_language(Language.MY_QLM)) # doctest: +NORMALIZE_WHITESPACE +MYQLM Depolarizing channel, p = 0.3: [[0.83666003 0. ] [0. 0.83666003]] @@ -423,7 +423,7 @@ def to_other_language( [[ 0.31622777+0.j 0. +0.j] [ 0. +0.j -0.31622777+0.j]] - >>> Depolarizing(0.3, [0,1]).to_other_language(Language.CIRQ) + >>> Depolarizing(0.3, [0,1]).to_other_language(Language.CIRQ) # doctest: +CIRQ cirq.depolarize(p=0.3) """ @@ -520,7 +520,7 @@ class BitFlip(NoiseModel): BitFlip(0.3, [1, 2]) BitFlip(0.05, [0], gates=[H]) BitFlip(0.3) - >>> print(circuit.to_other_language(Language.BRAKET)) # doctest: +NORMALIZE_WHITESPACE + >>> print(circuit.to_other_language(Language.BRAKET)) # doctest: +NORMALIZE_WHITESPACE +BRAKET T : │ 0 │ ┌───┐ ┌─────────┐ ┌──────────┐ ┌─────────┐ q0 : ─┤ H ├─┤ BF(0.3) ├─┤ BF(0.05) ├─┤ BF(0.1) ├─ @@ -532,7 +532,7 @@ class BitFlip(NoiseModel): q2 : ─┤ H ├─┤ BF(0.3) ├─┤ BF(0.3) ├────────────── └───┘ └─────────┘ └─────────┘ T : │ 0 │ - >>> print(circuit.to_other_language(Language.CIRQ)) # doctest: +NORMALIZE_WHITESPACE + >>> print(circuit.to_other_language(Language.CIRQ)) # doctest: +NORMALIZE_WHITESPACE +CIRQ q_0: ───I───H───BF(0.3)───BF(0.05)───BF(0.1)─── q_1: ───I───H───BF(0.3)───BF(0.3)────────────── q_2: ───I───H───BF(0.3)───BF(0.3)────────────── @@ -572,17 +572,17 @@ def to_other_language( language: Enum representing the target language. Examples: - >>> BitFlip(0.3, [0,1]).to_other_language(Language.BRAKET) + >>> BitFlip(0.3, [0,1]).to_other_language(Language.BRAKET) # doctest: +BRAKET BitFlip('probability': 0.3, 'qubit_count': 1) - >>> BitFlip(0.3, [0,1]).to_other_language(Language.QISKIT).to_quantumchannel() + >>> BitFlip(0.3, [0,1]).to_other_language(Language.QISKIT).to_quantumchannel() # doctest: +QISKIT SuperOp([[0.7+0.j, 0. +0.j, 0. +0.j, 0.3+0.j], [0. +0.j, 0.7+0.j, 0.3+0.j, 0. +0.j], [0. +0.j, 0.3+0.j, 0.7+0.j, 0. +0.j], [0.3+0.j, 0. +0.j, 0. +0.j, 0.7+0.j]], input_dims=(2,), output_dims=(2,)) - >>> BitFlip(0.3, [0,1]).to_other_language(Language.CIRQ) + >>> BitFlip(0.3, [0,1]).to_other_language(Language.CIRQ) # doctest: +CIRQ cirq.bit_flip(p=0.3) """ @@ -663,7 +663,7 @@ class AmplitudeDamping(NoiseModel): AmplitudeDamping(0.1, targets=[0, 1, 2]) AmplitudeDamping(0.1) AmplitudeDamping(0.7, targets=[0, 1]) - >>> print(circuit.to_other_language(Language.BRAKET)) # doctest: +NORMALIZE_WHITESPACE + >>> print(circuit.to_other_language(Language.BRAKET)) # doctest: +NORMALIZE_WHITESPACE +BRAKET T : │ 0 │ ┌───┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌────────────┐ q0 : ─┤ H ├─┤ AD(0.7) ├─┤ AD(0.1) ├───┤ AD(0.1) ├─────┤ GAD(0.2,0) ├── @@ -675,7 +675,7 @@ class AmplitudeDamping(NoiseModel): q2 : ─┤ H ├─┤ AD(0.1) ├─┤ AD(0.1) ├─┤ GAD(0.4,0.1) ├────────────────── └───┘ └─────────┘ └─────────┘ └──────────────┘ T : │ 0 │ - >>> print(circuit.to_other_language(Language.CIRQ)) # doctest: +NORMALIZE_WHITESPACE + >>> print(circuit.to_other_language(Language.CIRQ)) # doctest: +NORMALIZE_WHITESPACE +CIRQ q_0: ───I───H───AD(0.7)───AD(0.1)───AD(0.1)────────GAD(0,0.2)───── q_1: ───I───H───AD(0.7)───AD(0.1)───AD(0.1)────────GAD(0.1,0.4)─── q_2: ───I───H───AD(0.1)───AD(0.1)───GAD(0.1,0.4)────────────────── @@ -726,23 +726,23 @@ def to_other_language( language: Enum representing the target language. Examples: - >>> AmplitudeDamping(0.4, targets=[0, 1]).to_other_language(Language.BRAKET) + >>> AmplitudeDamping(0.4, targets=[0, 1]).to_other_language(Language.BRAKET) # doctest: +BRAKET AmplitudeDamping('gamma': 0.4, 'qubit_count': 1) - >>> AmplitudeDamping(0.4, 0.2, [1]).to_other_language(Language.BRAKET) + >>> AmplitudeDamping(0.4, 0.2, [1]).to_other_language(Language.BRAKET) # doctest: +BRAKET GeneralizedAmplitudeDamping('gamma': 0.4, 'probability': 0.2, 'qubit_count': 1) - >>> AmplitudeDamping(0.2, 0.4, [0, 1]).to_other_language(Language.QISKIT).to_quantumchannel() + >>> AmplitudeDamping(0.2, 0.4, [0, 1]).to_other_language(Language.QISKIT).to_quantumchannel() # doctest: +QISKIT SuperOp([[0.88 +0.j, 0. +0.j, 0. +0.j, 0.08 +0.j], [0. +0.j, 0.89442719+0.j, 0. +0.j, 0. +0.j], [0. +0.j, 0. +0.j, 0.89442719+0.j, 0. +0.j], [0.12 +0.j, 0. +0.j, 0. +0.j, 0.92 +0.j]], input_dims=(2,), output_dims=(2,)) - >>> AmplitudeDamping(0.4, targets=[0, 1]).to_other_language(Language.CIRQ) + >>> AmplitudeDamping(0.4, targets=[0, 1]).to_other_language(Language.CIRQ) # doctest: +CIRQ cirq.amplitude_damp(gamma=0.4) - >>> AmplitudeDamping(0.4, 0.2, [1]).to_other_language(Language.CIRQ) + >>> AmplitudeDamping(0.4, 0.2, [1]).to_other_language(Language.CIRQ) # doctest: +CIRQ cirq.generalized_amplitude_damp(p=0.2,gamma=0.4) """ @@ -823,7 +823,7 @@ class PhaseDamping(NoiseModel): PhaseDamping(0.32, [0, 1, 2]) PhaseDamping(0.01) PhaseDamping(0.45, [0, 1]) - >>> print(circuit.to_other_language(Language.BRAKET)) # doctest: +NORMALIZE_WHITESPACE + >>> print(circuit.to_other_language(Language.BRAKET)) # doctest: +NORMALIZE_WHITESPACE +BRAKET T : │ 0 │ ┌───┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ q0 : ─┤ H ├─┤ PD(0.45) ├─┤ PD(0.01) ├─┤ PD(0.32) ├─ @@ -835,7 +835,7 @@ class PhaseDamping(NoiseModel): q2 : ─┤ H ├─┤ PD(0.01) ├─┤ PD(0.32) ├────────────── └───┘ └──────────┘ └──────────┘ T : │ 0 │ - >>> print(circuit.to_other_language(Language.CIRQ)) # doctest: +NORMALIZE_WHITESPACE + >>> print(circuit.to_other_language(Language.CIRQ)) # doctest: +NORMALIZE_WHITESPACE +CIRQ q_0: ───I───H───PD(0.45)───PD(0.01)───PD(0.32)─── q_1: ───I───H───PD(0.45)───PD(0.01)───PD(0.32)─── q_2: ───I───H───PD(0.01)───PD(0.32)────────────── @@ -878,24 +878,24 @@ def to_other_language( language: Enum representing the target language. Examples: - >>> PhaseDamping(0.4, [0, 1]).to_other_language(Language.BRAKET) + >>> PhaseDamping(0.4, [0, 1]).to_other_language(Language.BRAKET) # doctest: +BRAKET PhaseDamping('gamma': 0.4, 'qubit_count': 1) - >>> PhaseDamping(0.4, [0, 1]).to_other_language(Language.QISKIT).to_quantumchannel() + >>> PhaseDamping(0.4, [0, 1]).to_other_language(Language.QISKIT).to_quantumchannel() # doctest: +QISKIT SuperOp([[1. +0.j, 0. +0.j, 0. +0.j, 0. +0.j], [0. +0.j, 0.77459667+0.j, 0. +0.j, 0. +0.j], [0. +0.j, 0. +0.j, 0.77459667+0.j, 0. +0.j], [0. +0.j, 0. +0.j, 0. +0.j, 1. +0.j]], input_dims=(2,), output_dims=(2,)) - >>> print(PhaseDamping(0.4, [0, 1]).to_other_language(Language.MY_QLM)) # doctest: +NORMALIZE_WHITESPACE + >>> print(PhaseDamping(0.4, [0, 1]).to_other_language(Language.MY_QLM)) # doctest: +NORMALIZE_WHITESPACE +MYQLM Phase Damping channel, gamma = 0.4: [[1. 0. ] [0. 0.77459667]] [[0. 0. ] [0. 0.77459667]] - >>> print(PhaseDamping(0.4, [0, 1]).to_other_language(Language.CIRQ)) + >>> print(PhaseDamping(0.4, [0, 1]).to_other_language(Language.CIRQ)) # doctest: +CIRQ phase_damp(gamma=0.4) """ diff --git a/mpqp/qasm/lexer_utils.py b/mpqp/qasm/lexer_utils.py index b1dd35b1..331a3720 100644 --- a/mpqp/qasm/lexer_utils.py +++ b/mpqp/qasm/lexer_utils.py @@ -111,7 +111,6 @@ def t_error(t): # pyright: ignore[reportMissingParameterType] from mpqp.gates import * - single_qubits_gate_qasm = { "h": H, "x": X, diff --git a/mpqp/qasm/mpqp_to_qasm.py b/mpqp/qasm/mpqp_to_qasm.py index 516f626e..8d502af1 100644 --- a/mpqp/qasm/mpqp_to_qasm.py +++ b/mpqp/qasm/mpqp_to_qasm.py @@ -211,6 +211,8 @@ def mpqp_to_qasm2( if previous: qasm_str += _simplify_instruction_to_qasm(previous, targets, c_targets) + if gphase != 0: + qasm_str += f"\n// gphase:{gphase}" qasm_str += qasm_measure return qasm_str, gphase diff --git a/mpqp/qasm/myqlm_to_mpqp.py b/mpqp/qasm/myqlm_to_mpqp.py index f81f0848..5ef7eb56 100644 --- a/mpqp/qasm/myqlm_to_mpqp.py +++ b/mpqp/qasm/myqlm_to_mpqp.py @@ -1,13 +1,16 @@ from __future__ import annotations -from typing import List, Tuple +from typing import TYPE_CHECKING, List, Tuple import numpy as np -from qat.core.wrappers.circuit import Circuit as my_QLM_Circuit from mpqp.core import QCircuit from mpqp.gates import * +if TYPE_CHECKING: + from qat.core.wrappers.circuit import Circuit as my_QLM_Circuit + + MyQLM_Gate = Tuple[str, List[int], List[int]] diff --git a/mpqp/qasm/open_qasm_2_and_3.py b/mpqp/qasm/open_qasm_2_and_3.py index e265ab12..b68271fb 100644 --- a/mpqp/qasm/open_qasm_2_and_3.py +++ b/mpqp/qasm/open_qasm_2_and_3.py @@ -886,7 +886,7 @@ def replace(match: re.Match[str]): return qasm_code -def remove_include_and_comment(qasm_code: str) -> str: +def remove_include_and_comment(qasm_code: str) -> tuple[str, float]: r""" Removes lines that start with 'include' or comments (starting with '\\') from a given OpenQASM code string. @@ -901,20 +901,27 @@ def remove_include_and_comment(qasm_code: str) -> str: >>> qasm_code = '''include "stdgates.inc"; ... qreg q[2]; ... // This is a comment + ... // gphase: 1.57 ... H q[0];''' - >>> print(remove_include_and_comment(qasm_code)) + >>> qasm, gphase = remove_include_and_comment(qasm_code) + >>> print(qasm) qreg q[2]; H q[0]; + >>> gphase + 1.57 """ replaced_code = [] + gphase = 0.00 for line in qasm_code.split("\n"): line = line.lstrip() - if line.startswith("include") or line.startswith("//"): + if line.startswith("// gphase:"): + gphase += float(line.split(":")[1].strip()) + elif line.startswith("include") or line.startswith("//"): pass else: replaced_code.append(line) - return "\n".join(replaced_code) + return "\n".join(replaced_code), gphase def parse_gphase_instruction( @@ -1047,8 +1054,8 @@ def add_qe_lib(): ): with open(f"{path_to_main}/{path}", "r") as f: child = Node(path, parent=included_tree_current) - converted_content, gphase = open_qasm_3_to_2( - f.read(), child, path_to_main, defined_gates, gphase + converted_content = open_qasm_3_to_2( + f.read(), child, path_to_main, defined_gates ) new_path = splitext(path)[0] + "_converted" + splitext(path)[1] with open(f"{path_to_main}/{new_path}", "w") as f: @@ -1188,7 +1195,7 @@ def open_qasm_3_to_2( defined_gates: Optional[set[str]] = None, gphase: float = 0.0, language: Language = Language.QASM3, -) -> tuple[str, float]: +) -> str: """Converts an OpenQASM 3.0 code back to OpenQASM 2.0. This function will also recursively go through the imported files to @@ -1217,7 +1224,7 @@ def open_qasm_3_to_2( ... c[0] = measure q[0]; ... c[1] = measure q[1]; ... ''' - >>> qasm_2, gphase = open_qasm_3_to_2(qasm3_str) + >>> qasm_2 = open_qasm_3_to_2(qasm3_str) >>> print(qasm_2) # doctest: +NORMALIZE_WHITESPACE OPENQASM 2.0; include "qelib1.inc"; @@ -1277,10 +1284,10 @@ def open_qasm_3_to_2( ) header_code += h_code instructions_code += i_code - gphase_code = f"// gphase {gphase}\n" if gphase != 0 else "" + gphase_code = f"// gphase:{gphase}\n" if gphase != 0 else "" target_code = header_code + gphase_code + instructions_code - return target_code, gphase + return target_code def parse_openqasm_3_file(code: str) -> list[str]: @@ -1326,7 +1333,7 @@ def parse_openqasm_3_file(code: str) -> list[str]: return list(filter(lambda i: i.strip() != "", instructions)) -def open_qasm_file_conversion_3_to_2(path: str) -> tuple[str, float]: +def open_qasm_file_conversion_3_to_2(path: str) -> str: """Converts an OpenQASM code in a file from version 3.0 and 2.0. This function is a shorthand to initialize :func:`open_qasm_3_to_2` with the @@ -1355,7 +1362,7 @@ def open_qasm_file_conversion_3_to_2(path: str) -> tuple[str, float]: gate3 q[0], q[1]; c[0] = measure q[0]; c[1] = measure q[1]; - >>> qasm_2, gphase = open_qasm_file_conversion_3_to_2(example_dir + "main_converted.qasm") + >>> qasm_2 = open_qasm_file_conversion_3_to_2(example_dir + "main_converted.qasm") >>> print(qasm_2) # doctest: +NORMALIZE_WHITESPACE OPENQASM 2.0; include 'include1_converted_converted.qasm'; diff --git a/mpqp/qasm/qasm_to_braket.py b/mpqp/qasm/qasm_to_braket.py index 306de2c5..6c9dddfb 100644 --- a/mpqp/qasm/qasm_to_braket.py +++ b/mpqp/qasm/qasm_to_braket.py @@ -57,8 +57,8 @@ def qasm3_to_braket_Program(qasm3_str: str) -> "Program": ... qubit[2] q; ... h q[0]; ... ''' - >>> program = qasm3_to_braket_Program(qasm_code) - >>> print(program) + >>> program = qasm3_to_braket_Program(qasm_code) # doctest: +MYQLM + >>> print(program) # doctest: +MYQLM braketSchemaHeader=BraketSchemaHeader(name='braket.ir.openqasm.program', version='1') source='\nOPENQASM 3.0;\nqubit[2] q;\nh q[0];\n' inputs=None """ @@ -89,8 +89,8 @@ def qasm3_to_braket_Circuit(qasm3_str: str) -> "Circuit": ... qubit[2] q; ... h q[0]; ... ''' - >>> circuit = qasm3_to_braket_Circuit(qasm_code) - >>> print(circuit) # doctest: +NORMALIZE_WHITESPACE + >>> circuit = qasm3_to_braket_Circuit(qasm_code) # doctest: +MYQLM + >>> print(circuit) # doctest: +NORMALIZE_WHITESPACE, +MYQLM T : │ 0 │ ┌───┐ q0 : ─┤ H ├─ diff --git a/mpqp/qasm/qasm_to_cirq.py b/mpqp/qasm/qasm_to_cirq.py index 38ab3e59..96b846fd 100644 --- a/mpqp/qasm/qasm_to_cirq.py +++ b/mpqp/qasm/qasm_to_cirq.py @@ -39,8 +39,8 @@ def qasm2_to_cirq_Circuit(qasm_str: str) -> "cirq_circuit": ... h q[0]; ... cx q[0], q[1]; ... ''' - >>> circuit = qasm2_to_cirq_Circuit(qasm_code) - >>> print(circuit) # doctest: +NORMALIZE_WHITESPACE + >>> circuit = qasm2_to_cirq_Circuit(qasm_code) # doctest: +CIRQ + >>> print(circuit) # doctest: +NORMALIZE_WHITESPACE, +CIRQ q_0: ───I───H───@─── │ q_1: ───I───────X─── @@ -218,6 +218,12 @@ def __str__(self): num_args=1, cirq_gate=(lambda params: MyQasmUGate(*[p for p in params])), ), + "u3": QasmGateStatement( + qasm_gate="u3", + num_params=3, + num_args=1, + cirq_gate=(lambda params: MyQasmUGate(*[p for p in params])), + ), "rxx": QasmGateStatement( qasm_gate="rxx", num_params=1, @@ -231,7 +237,7 @@ def __str__(self): cirq_gate=(lambda params: Rzz(params[0])), ), } - qasm_parser.all_gates |= qs_dict + qasm_parser.qelib_gates |= qs_dict def p_new_reg2(self, p): # pyright: ignore[reportMissingParameterType] """new_reg : QREG ID '[' NATURAL_NUMBER ']' ';' diff --git a/mpqp/qasm/qasm_to_mpqp.py b/mpqp/qasm/qasm_to_mpqp.py index 201ba549..80069955 100644 --- a/mpqp/qasm/qasm_to_mpqp.py +++ b/mpqp/qasm/qasm_to_mpqp.py @@ -70,7 +70,8 @@ def qasm2_parse(input_string: str) -> QCircuit: from mpqp.core.circuit import QCircuit input_string = remove_user_gates(input_string, skip_qelib1=True) - input_string = remove_include_and_comment(input_string) + input_string, gphase = remove_include_and_comment(input_string) + tokens = lex_openqasm(input_string) if ( @@ -83,6 +84,7 @@ def qasm2_parse(input_string: str) -> QCircuit: idx = 3 circuit = QCircuit() + circuit.input_g_phase = gphase i_max = len(tokens) while idx < i_max: logger.debug(circuit) @@ -302,10 +304,19 @@ def _eval_expr(tokens: list[LexToken], idx: int) -> tuple[Any, int]: import numpy as np # pyright: ignore[reportUnusedImport] expr = "" - while tokens[idx].type != 'COMMA' and tokens[idx].type != 'RPAREN': - if check_num_expr(tokens[idx].type): + open_paren = 0 + while tokens[idx].type != 'COMMA' and ( + tokens[idx].type != 'RPAREN' or open_paren > 0 + ): + if tokens[idx].type == 'LPAREN': + open_paren += 1 + expr += "(" + elif tokens[idx].type == 'RPAREN': + open_paren -= 1 + expr += ")" + elif check_num_expr(tokens[idx].type): raise SyntaxError(f"not a nb or expr: {idx}, {tokens[idx]}") - if tokens[idx].type == 'PI': + elif tokens[idx].type == 'PI': expr += "np.pi" else: expr += str(tokens[idx].value) @@ -365,7 +376,7 @@ def parse_qasm2_gates(code: str) -> tuple[str, float]: ) import re - code = remove_include_and_comment(code) + code, gphase = remove_include_and_comment(code) lines = code.split(";") lines.insert(1, qasm_code(Instr.QISKIT_CUSTOM_INCLUDE)) @@ -373,7 +384,6 @@ def parse_qasm2_gates(code: str) -> tuple[str, float]: code = remove_user_gates(code) - gphase = 0 clean_code = [] to_add = True diff --git a/mpqp/tools/circuit.py b/mpqp/tools/circuit.py index 3f4f5017..4f2b6db5 100644 --- a/mpqp/tools/circuit.py +++ b/mpqp/tools/circuit.py @@ -294,7 +294,7 @@ def compute_expected_matrix(qcircuit: QCircuit): def replace_custom_gate( - custom_unitary: "CircuitInstruction", nb_qubits: int # type: ignore[reportInvalidTypeForm] + custom_unitary: "CircuitInstruction", nb_qubits: int ) -> "tuple[QuantumCircuit, float]": """Decompose and replace the (custom) qiskit unitary given in parameter by a qiskit `QuantumCircuit` composed of ``U`` and ``CX`` gates. @@ -321,7 +321,9 @@ def replace_custom_gate( transpilation_circuit = QuantumCircuit(nb_qubits) transpilation_circuit.append(custom_unitary) try: - transpiled = transpile(transpilation_circuit, basis_gates=['u', 'cx']) + transpiled = transpile( + transpilation_circuit, basis_gates=['u3', 'cx'], optimization_level=0 + ) except QiskitError as e: # if the error is arising from TwoQubitWeylDecomposition, we replace the # matrix by the closest unitary @@ -329,7 +331,9 @@ def replace_custom_gate( custom_unitary.operation.params[0] = closest_unitary( custom_unitary.operation.params[0] ) - transpiled = transpile(transpilation_circuit, basis_gates=['u', 'cx']) + transpiled = transpile( + transpilation_circuit, basis_gates=['u3', 'cx'], optimization_level=0 + ) else: raise e return transpiled, transpiled.global_phase diff --git a/mpqp/tools/maths.py b/mpqp/tools/maths.py index 6ae2f5d2..7836d10c 100644 --- a/mpqp/tools/maths.py +++ b/mpqp/tools/maths.py @@ -300,16 +300,16 @@ def rand_clifford_matrix( Examples: >>> pprint(rand_clifford_matrix(2)) - [[-0.5 , 0.5 , 0.5j , -0.5j], - [-0.5j, -0.5j, 0.5 , 0.5 ], - [-0.5j, -0.5j, -0.5 , -0.5 ], - [-0.5 , 0.5 , -0.5j, 0.5j ]] + [[0.5 , -0.5j, -0.5 , -0.5j], + [0.5j, 0.5 , 0.5j , -0.5 ], + [0.5j, -0.5 , -0.5j, -0.5 ], + [0.5 , 0.5j , 0.5 , -0.5j]] >>> pprint(rand_clifford_matrix(2, seed=123)) - [[0.70711j, 0 , -0.70711j, 0 ], - [0 , -0.70711j, 0 , -0.70711j], - [0 , 0.70711j , 0 , -0.70711j], - [0.70711j, 0 , 0.70711j , 0 ]] + [[0.70711 , 0 , 0.70711 , 0 ], + [0 , -0.70711, 0 , -0.70711 ], + [-0.70711j, 0 , 0.70711j, 0 ], + [0 , 0.70711j, 0 , -0.70711j]] """ from qiskit.quantum_info import random_clifford diff --git a/mpqp/tools/theoretical_simulation.py b/mpqp/tools/theoretical_simulation.py index 68b11fa5..8779a617 100644 --- a/mpqp/tools/theoretical_simulation.py +++ b/mpqp/tools/theoretical_simulation.py @@ -52,13 +52,10 @@ def amplitude( state = np.zeros((d), dtype=np.complex128) state[0] = 1 gates = circ.gates - print(state) for gate in gates: g = gate.to_matrix(circ.nb_qubits) - print(g) state = g @ state - print(state) for noise in circ.noises: if ( len(noise.gates) == 0 diff --git a/mpqp_scripts/setup_connections.py b/mpqp_scripts/setup_connections.py index 481f067f..3dd1ee7f 100644 --- a/mpqp_scripts/setup_connections.py +++ b/mpqp_scripts/setup_connections.py @@ -26,8 +26,12 @@ def print_config_info(): try: print(ibmqc.get_active_account_info()) except IBMRemoteExecutionError as err: - if "Unable to find account" in str(err): + if "Unable to find account" in str(err) or "No IBM account configured" in str( + err + ): print("Account not configured") + else: + print(f"{err}") print("===== Qaptiva QLMaaS info : ===== ") user_name = env_m.get_env_variable("QLM_USER") @@ -57,6 +61,42 @@ def print_config_info(): return "", [] +def delete_config(): + """Delete stored credentials for a selected provider.""" + from mpqp.execution.connection.aws_connection import delete_aws_braket_account + from mpqp.execution.connection.azure_connection import delete_azure_account + from mpqp.execution.connection.ibm_connection import delete_ibm_account + from mpqp.execution.connection.ionq_connection import delete_ionq_account + from mpqp.execution.connection.qlm_connection import delete_qlm_account + from mpqp.tools.choice_tree import AnswerNode, QuestionNode, run_choice_tree + + def delete_all(): + delete_ibm_account() + delete_qlm_account() + delete_aws_braket_account() + delete_ionq_account() + delete_azure_account() + delete_azure_account() + + return "All accounts deleted", [] + + delete_tree = QuestionNode( + "Select provider to delete configuration:", + [ + AnswerNode("IBM", delete_ibm_account), + AnswerNode("QLM", delete_qlm_account), + AnswerNode("Braket", delete_aws_braket_account), + AnswerNode("IonQ", delete_ionq_account), + AnswerNode("Azure", delete_azure_account), + AnswerNode("All", delete_all), + ], + leaf_loop_to_here=True, + ) + + run_choice_tree(delete_tree) + return "", [] + + def main_setup(): """Main function of the script, triggering the choice selection, and guiding you through the steps needed to configure each provider access. This @@ -78,6 +118,7 @@ def main_setup(): AnswerNode("IonQ", config_ionq_key), AnswerNode("Azure", config_azure_account), AnswerNode("Recap", print_config_info), + AnswerNode("Delete configuration", delete_config), ], leaf_loop_to_here=True, ) diff --git a/pytest.ini b/pytest.ini index 00a432e5..1c20c782 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,4 +1,6 @@ [pytest] +markers = + provider(name): mark test as specific to a given quantum provider (e.g. qiskit, cirq, braket, azure, myqlm) ; culprits packages added as comments filterwarnings = ; pkg_resources diff --git a/requirements-all.txt b/requirements-all.txt new file mode 100644 index 00000000..01c2a633 --- /dev/null +++ b/requirements-all.txt @@ -0,0 +1,6 @@ +-r requirements.txt +-r requirements_providers/azure.txt +-r requirements_providers/braket.txt +-r requirements_providers/cirq.txt +-r requirements_providers/qiskit.txt +-r requirements_providers/myqlm.txt diff --git a/requirements.txt b/requirements.txt index a353e614..5e7c28b3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,31 +1,17 @@ -numpy>=1.26.4 -scipy>=1.11.4 -amazon-braket-sdk==1.79.1 -aws-configure==2.1.8 -boto3==1.35.82 -osqp==1.0.1 -amazon-braket-default-simulator==1.23.2 -myqlm==1.10.6 -myqlm-interop -cvxpy==1.4.3 -qiskit==1.2.4 -qiskit-ibm-runtime==0.31.0 -qiskit-aer==0.15.1 -azure-quantum==3.1.0 -azure-quantum[qiskit] -python_dotenv>=1.0.0 -jsonschema>=4.17.3 -typeguard>=4.2.1 -pylatexenc==2.10 -anytree>=2.12.1 -sympy==1.13.1 -pick==2.2.0 -termcolor==2.4.0 -aenum==3.1.15 -symengine>=0.11.0 -cirq-core==1.3.0 -qsimcirq -cirq-google==1.3.0 -cirq-ionq==1.3.0 -cirq-aqt==1.3.0 -numba==0.60.0 +numpy +scipy +python_dotenv +jsonschema +typeguard +pylatexenc +anytree +sympy +pick +termcolor +symengine +numba +aenum==3.1.16 +ply +matplotlib +networkx +qiskit==2.2.3 \ No newline at end of file diff --git a/requirements_providers/azure.txt b/requirements_providers/azure.txt new file mode 100644 index 00000000..cc4e4b9e --- /dev/null +++ b/requirements_providers/azure.txt @@ -0,0 +1,2 @@ +azure-quantum==3.5.0 +azure-quantum[qiskit] \ No newline at end of file diff --git a/requirements_providers/braket.txt b/requirements_providers/braket.txt new file mode 100644 index 00000000..8517a45c --- /dev/null +++ b/requirements_providers/braket.txt @@ -0,0 +1,4 @@ +amazon-braket-sdk==1.106.5 +amazon-braket-default-simulator==1.32.0 +boto3==1.42.10 +aws-configure==2.1.8 \ No newline at end of file diff --git a/requirements_providers/cirq.txt b/requirements_providers/cirq.txt new file mode 100644 index 00000000..63da3f9e --- /dev/null +++ b/requirements_providers/cirq.txt @@ -0,0 +1,5 @@ +cirq-core==1.6.1 +qsimcirq +cirq-google==1.6.1 +cirq-ionq==1.6.1 +cirq-aqt==1.6.1 \ No newline at end of file diff --git a/requirements_providers/myqlm.txt b/requirements_providers/myqlm.txt new file mode 100644 index 00000000..6a718756 --- /dev/null +++ b/requirements_providers/myqlm.txt @@ -0,0 +1,2 @@ +myqlm==1.12.4 +myqlm-interop \ No newline at end of file diff --git a/requirements_providers/qiskit.txt b/requirements_providers/qiskit.txt new file mode 100644 index 00000000..a8fae138 --- /dev/null +++ b/requirements_providers/qiskit.txt @@ -0,0 +1,2 @@ +qiskit-ibm-runtime==0.43.1 +qiskit-aer==0.17.2 \ No newline at end of file diff --git a/setup.py b/setup.py index 3e81de3b..3d941a3b 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,5 @@ from setuptools import find_packages, setup +from pathlib import Path with open("README.md", "r", encoding="utf-8") as f: long_description = f.read() @@ -14,6 +15,16 @@ with open("requirements.txt", "r") as f: requirements = f.readlines() +providers_dir = Path("requirements_providers") +extras = {} +all = [] +for f in providers_dir.glob("*.txt"): + provider_name = f.stem + with open(f, "r", encoding="utf-8") as fh: + extra = fh.readlines() + extras[provider_name] = extra + all.extend(extra) +extras["all"] = all setup( name="mpqp", @@ -27,6 +38,7 @@ author_email="quantum@colibritd.com", url="https://colibritd.com", install_requires=["wheel"] + requirements, + extras_require=extras, packages=find_packages(include=["mpqp*"]), entry_points={ "console_scripts": [ diff --git a/tests/core/instruction/gates/test_custom_gate.py b/tests/core/instruction/gates/test_custom_gate.py index d8cd11f4..25c862f8 100644 --- a/tests/core/instruction/gates/test_custom_gate.py +++ b/tests/core/instruction/gates/test_custom_gate.py @@ -1,7 +1,6 @@ import contextlib import random from functools import reduce -from itertools import product import numpy as np import pytest @@ -16,22 +15,36 @@ from mpqp.tools.maths import is_unitary, matrix_eq, rand_orthogonal_matrix -def local_simulators(): - return [ - IBMDevice.AER_SIMULATOR, - AWSDevice.BRAKET_LOCAL_SIMULATOR, - ATOSDevice.MYQLM_PYLINALG, - GOOGLEDevice.CIRQ_LOCAL_SIMULATOR, - ] - - def test_custom_gate_is_unitary(): definition = np.array([[1, 0], [0, 1j]]) assert is_unitary(CustomGate(definition, [0]).to_matrix()) -@pytest.mark.parametrize("circ_size, device", product(range(1, 6), local_simulators())) -def test_random_orthogonal_matrix(circ_size: int, device: AvailableDevice): +@pytest.mark.provider("qiskit") +@pytest.mark.parametrize("circ_size", range(1, 6)) +def test_random_orthogonal_matrix_qiskit(circ_size: int): + exec_random_orthogonal_matrix(circ_size, IBMDevice.AER_SIMULATOR) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize("circ_size", range(1, 6)) +def test_random_orthogonal_matrix_braket(circ_size: int): + exec_random_orthogonal_matrix(circ_size, AWSDevice.BRAKET_LOCAL_SIMULATOR) + + +@pytest.mark.provider("cirq") +@pytest.mark.parametrize("circ_size", range(1, 6)) +def test_random_orthogonal_matrix_cirq(circ_size: int): + exec_random_orthogonal_matrix(circ_size, GOOGLEDevice.CIRQ_LOCAL_SIMULATOR) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize("circ_size", range(1, 6)) +def test_random_orthogonal_matrix_myqlm(circ_size: int): + exec_random_orthogonal_matrix(circ_size, ATOSDevice.MYQLM_PYLINALG) + + +def exec_random_orthogonal_matrix(circ_size: int, device: AvailableDevice): gate_size = random.randint(1, circ_size) targets_start = random.randint(0, circ_size - gate_size) m = rand_orthogonal_matrix(2**gate_size) @@ -58,8 +71,27 @@ def test_random_orthogonal_matrix(circ_size: int, device: AvailableDevice): assert matrix_eq(result.amplitudes, exp_state_vector, 1e-5, 1e-5) -@pytest.mark.parametrize("device", local_simulators()) -def test_custom_gate_with_native_gates(device: AvailableDevice): +@pytest.mark.provider("qiskit") +def test_custom_gate_with_native_gates_qiskit(): + exec_custom_gate_with_native_gates(IBMDevice.AER_SIMULATOR) + + +@pytest.mark.provider("braket") +def test_custom_gate_with_native_gates_braket(): + exec_custom_gate_with_native_gates(AWSDevice.BRAKET_LOCAL_SIMULATOR) + + +@pytest.mark.provider("cirq") +def test_custom_gate_with_native_gates_cirq(): + exec_custom_gate_with_native_gates(GOOGLEDevice.CIRQ_LOCAL_SIMULATOR) + + +@pytest.mark.provider("myqlm") +def test_custom_gate_with_native_gates_myqlm(): + exec_custom_gate_with_native_gates(ATOSDevice.MYQLM_PYLINALG) + + +def exec_custom_gate_with_native_gates(device: AvailableDevice): x = np.array([[0, 1], [1, 0]]) h = np.array([[1, 1], [1, -1]]) / np.sqrt(2) cnot = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]) @@ -96,8 +128,31 @@ def test_custom_gate_with_native_gates(device: AvailableDevice): assert matrix_eq(result1.amplitudes, result2.amplitudes, 1e-4, 1e-4) -@pytest.mark.parametrize("circ_size, device", product(range(1, 6), local_simulators())) -def test_custom_gate_with_random_circuit(circ_size: int, device: AvailableDevice): +@pytest.mark.provider("qiskit") +@pytest.mark.parametrize("circ_size", range(1, 6)) +def test_custom_gate_with_random_circuit_qiskit(circ_size: int): + exec_custom_gate_with_random_circuit(circ_size, IBMDevice.AER_SIMULATOR) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize("circ_size", range(1, 6)) +def test_custom_gate_with_random_circuit_braket(circ_size: int): + exec_custom_gate_with_random_circuit(circ_size, AWSDevice.BRAKET_LOCAL_SIMULATOR) + + +@pytest.mark.provider("cirq") +@pytest.mark.parametrize("circ_size", range(1, 6)) +def test_custom_gate_with_random_circuit_cirq(circ_size: int): + exec_custom_gate_with_random_circuit(circ_size, GOOGLEDevice.CIRQ_LOCAL_SIMULATOR) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize("circ_size", range(1, 6)) +def test_custom_gate_with_random_circuit_myqlm(circ_size: int): + exec_custom_gate_with_random_circuit(circ_size, ATOSDevice.MYQLM_PYLINALG) + + +def exec_custom_gate_with_random_circuit(circ_size: int, device: AvailableDevice): random_circ = random_circuit(nb_qubits=circ_size) matrix = random_circ.to_matrix() custom_gate_circ = QCircuit([CustomGate(matrix, list(range(circ_size)))]) @@ -191,29 +246,131 @@ def _test_execution_equivalence( ) -@pytest.mark.parametrize( - "gates_n_positions, device", product(non_ordered_targets(), local_simulators()) -) -def test_non_ordered_targets_execution( +@pytest.mark.provider("qiskit") +@pytest.mark.parametrize("gates_n_positions", non_ordered_targets()) +def test_non_ordered_targets_execution_qiskit( + gates_n_positions: list[tuple[type[SingleQubitGate], int]], +): + exec_non_ordered_targets_execution(gates_n_positions, IBMDevice.AER_SIMULATOR) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize("gates_n_positions", non_ordered_targets()) +def test_non_ordered_targets_execution_braket( + gates_n_positions: list[tuple[type[SingleQubitGate], int]], +): + exec_non_ordered_targets_execution( + gates_n_positions, AWSDevice.BRAKET_LOCAL_SIMULATOR + ) + + +@pytest.mark.provider("cirq") +@pytest.mark.parametrize("gates_n_positions", non_ordered_targets()) +def test_non_ordered_targets_execution_cirq( + gates_n_positions: list[tuple[type[SingleQubitGate], int]], +): + exec_non_ordered_targets_execution( + gates_n_positions, GOOGLEDevice.CIRQ_LOCAL_SIMULATOR + ) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize("gates_n_positions", non_ordered_targets()) +def test_non_ordered_targets_execution_myqlm( + gates_n_positions: list[tuple[type[SingleQubitGate], int]], +): + exec_non_ordered_targets_execution(gates_n_positions, ATOSDevice.MYQLM_PYLINALG) + + +def exec_non_ordered_targets_execution( gates_n_positions: list[tuple[type[SingleQubitGate], int]], device: AvailableDevice ): _test_execution_equivalence(gates_n_positions, device) -@pytest.mark.parametrize( - "gates_n_positions, device", product(non_contiguous_targets(), local_simulators()) -) -def test_non_contiguous_targets_execution( +@pytest.mark.provider("qiskit") +@pytest.mark.parametrize("gates_n_positions", non_contiguous_targets()) +def test_non_contiguous_targets_execution_qiskit( + gates_n_positions: list[tuple[type[SingleQubitGate], int]], +): + exec_non_contiguous_targets_execution(gates_n_positions, IBMDevice.AER_SIMULATOR) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize("gates_n_positions", non_contiguous_targets()) +def test_non_contiguous_targets_execution_braket( + gates_n_positions: list[tuple[type[SingleQubitGate], int]], +): + exec_non_contiguous_targets_execution( + gates_n_positions, AWSDevice.BRAKET_LOCAL_SIMULATOR + ) + + +@pytest.mark.provider("cirq") +@pytest.mark.parametrize("gates_n_positions", non_contiguous_targets()) +def test_non_contiguous_targets_execution_cirq( + gates_n_positions: list[tuple[type[SingleQubitGate], int]], +): + exec_non_contiguous_targets_execution( + gates_n_positions, GOOGLEDevice.CIRQ_LOCAL_SIMULATOR + ) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize("gates_n_positions", non_contiguous_targets()) +def test_non_contiguous_targets_execution_myqlm( + gates_n_positions: list[tuple[type[SingleQubitGate], int]], +): + exec_non_contiguous_targets_execution(gates_n_positions, ATOSDevice.MYQLM_PYLINALG) + + +def exec_non_contiguous_targets_execution( gates_n_positions: list[tuple[type[SingleQubitGate], int]], device: AvailableDevice ): _test_execution_equivalence(gates_n_positions, device) -@pytest.mark.parametrize( - "gates_n_positions, device", - product(non_contiguous_ordered_targets(), local_simulators()), -) -def test_non_contiguous_ordered_targets_execution( +@pytest.mark.provider("qiskit") +@pytest.mark.parametrize("gates_n_positions", non_contiguous_ordered_targets()) +def test_non_contiguous_ordered_targets_execution_qiskit( + gates_n_positions: list[tuple[type[SingleQubitGate], int]], +): + exec_non_contiguous_ordered_targets_execution( + gates_n_positions, IBMDevice.AER_SIMULATOR + ) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize("gates_n_positions", non_contiguous_ordered_targets()) +def test_non_contiguous_ordered_targets_execution_braket( + gates_n_positions: list[tuple[type[SingleQubitGate], int]], +): + exec_non_contiguous_ordered_targets_execution( + gates_n_positions, AWSDevice.BRAKET_LOCAL_SIMULATOR + ) + + +@pytest.mark.provider("cirq") +@pytest.mark.parametrize("gates_n_positions", non_contiguous_ordered_targets()) +def test_non_contiguous_ordered_targets_execution_cirq( + gates_n_positions: list[tuple[type[SingleQubitGate], int]], +): + exec_non_contiguous_ordered_targets_execution( + gates_n_positions, GOOGLEDevice.CIRQ_LOCAL_SIMULATOR + ) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize("gates_n_positions", non_contiguous_ordered_targets()) +def test_non_contiguous_ordered_targets_execution_myqlm( + gates_n_positions: list[tuple[type[SingleQubitGate], int]], +): + exec_non_contiguous_ordered_targets_execution( + gates_n_positions, ATOSDevice.MYQLM_PYLINALG + ) + + +def exec_non_contiguous_ordered_targets_execution( gates_n_positions: list[tuple[type[SingleQubitGate], int]], device: AvailableDevice ): _test_execution_equivalence(gates_n_positions, device) diff --git a/tests/core/instruction/measurement/test_basis.py b/tests/core/instruction/measurement/test_basis.py index f080b162..b8b4bc1d 100644 --- a/tests/core/instruction/measurement/test_basis.py +++ b/tests/core/instruction/measurement/test_basis.py @@ -1,5 +1,4 @@ import contextlib -from itertools import product import numpy as np import numpy.typing as npt @@ -287,25 +286,69 @@ def test_run_with_custom_basis_probas( assert matrix_eq(expected_probabilities, res.probabilities) +list_circuit_expected_vector_index = [ + (QCircuit([X(0), X(0)]), 0), + (QCircuit([X(0)]), 1), +] + + +@pytest.mark.provider("qiskit") @pytest.mark.parametrize( - "circuit, expected_vector_index, device", - [ - (circuit, v_index, device) - for ((circuit, v_index), device) in product( - [ - (QCircuit([X(0), X(0)]), 0), - (QCircuit([X(0)]), 1), - ], - [ - IBMDevice.AER_SIMULATOR, - ATOSDevice.MYQLM_PYLINALG, - AWSDevice.BRAKET_LOCAL_SIMULATOR, - GOOGLEDevice.CIRQ_LOCAL_SIMULATOR, - ], - ) - ], + "circuit, expected_vector_index", + list_circuit_expected_vector_index, +) +def test_valid_run_custom_basis_state_vector_one_qubit_qiskit( + circuit: QCircuit, + expected_vector_index: int, +): + exec_valid_run_custom_basis_state_vector_one_qubit( + circuit, expected_vector_index, IBMDevice.AER_SIMULATOR + ) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize( + "circuit, expected_vector_index", + list_circuit_expected_vector_index, +) +def test_valid_run_custom_basis_state_vector_one_qubit_braket( + circuit: QCircuit, + expected_vector_index: int, +): + exec_valid_run_custom_basis_state_vector_one_qubit( + circuit, expected_vector_index, AWSDevice.BRAKET_LOCAL_SIMULATOR + ) + + +@pytest.mark.provider("cirq") +@pytest.mark.parametrize( + "circuit, expected_vector_index", + list_circuit_expected_vector_index, ) -def test_valid_run_custom_basis_state_vector_one_qubit( +def test_valid_run_custom_basis_state_vector_one_qubit_cirq( + circuit: QCircuit, + expected_vector_index: int, +): + exec_valid_run_custom_basis_state_vector_one_qubit( + circuit, expected_vector_index, GOOGLEDevice.CIRQ_LOCAL_SIMULATOR + ) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize( + "circuit, expected_vector_index", + list_circuit_expected_vector_index, +) +def test_valid_run_custom_basis_state_vector_one_qubit_myqlm( + circuit: QCircuit, + expected_vector_index: int, +): + exec_valid_run_custom_basis_state_vector_one_qubit( + circuit, expected_vector_index, ATOSDevice.MYQLM_PYLINALG + ) + + +def exec_valid_run_custom_basis_state_vector_one_qubit( circuit: QCircuit, expected_vector_index: int, device: AvailableDevice ): vectors = [np.array([np.sqrt(3) / 2, 1 / 2]), np.array([-1 / 2, np.sqrt(3) / 2])] @@ -322,17 +365,31 @@ def test_valid_run_custom_basis_state_vector_one_qubit( assert matrix_eq(vectors[expected_vector_index], result.amplitudes) -def test_run_custom_basis_sampling_one_qubit(): +@pytest.mark.provider("qiskit") +def test_run_custom_basis_sampling_one_qubit_qiskit(): + exec_run_custom_basis_sampling_one_qubit(IBMDevice.AER_SIMULATOR) + + +@pytest.mark.provider("braket") +def test_run_custom_basis_sampling_one_qubit_braket(): + exec_run_custom_basis_sampling_one_qubit(AWSDevice.BRAKET_LOCAL_SIMULATOR) + + +@pytest.mark.provider("cirq") +def test_run_custom_basis_sampling_one_qubit_cirq(): + exec_run_custom_basis_sampling_one_qubit(GOOGLEDevice.CIRQ_LOCAL_SIMULATOR) + + +@pytest.mark.provider("myqlm") +def test_run_custom_basis_sampling_one_qubit_myqlm(): + exec_run_custom_basis_sampling_one_qubit(ATOSDevice.MYQLM_PYLINALG) + + +def exec_run_custom_basis_sampling_one_qubit(device: AvailableDevice): vectors = [np.array([np.sqrt(3) / 2, 1 / 2]), np.array([-1 / 2, np.sqrt(3) / 2])] basis = Basis(vectors) - with pytest.warns(UnsupportedBraketFeaturesWarning): - run( - QCircuit([X(0), X(0), BasisMeasure(basis=basis)]), - [ - IBMDevice.AER_SIMULATOR, - ATOSDevice.MYQLM_PYLINALG, - AWSDevice.BRAKET_LOCAL_SIMULATOR, - GOOGLEDevice.CIRQ_LOCAL_SIMULATOR, - ], - ) + run( + QCircuit([X(0), X(0), BasisMeasure(basis=basis)]), + device, + ) assert True diff --git a/tests/core/instruction/measurement/test_expectation_value.py b/tests/core/instruction/measurement/test_expectation_value.py index 3db18f56..82ae65e3 100644 --- a/tests/core/instruction/measurement/test_expectation_value.py +++ b/tests/core/instruction/measurement/test_expectation_value.py @@ -4,17 +4,11 @@ from typing import TYPE_CHECKING, Union if TYPE_CHECKING: - from qiskit.quantum_info import Operator + from cirq.ops.linear_combinations import PauliSum as CirqPauliSum + from cirq.ops.pauli_string import PauliString as CirqPauliString import numpy as np import pytest -from braket.circuits.observables import Hermitian -from cirq.devices.line_qubit import LineQubit -from cirq.ops.identity import I as Cirq_I -from cirq.ops.linear_combinations import PauliSum as CirqPauliSum -from cirq.ops.pauli_gates import X as Cirq_X -from cirq.ops.pauli_string import PauliString as CirqPauliString -from qat.core.wrappers.observable import Observable as QLMObservable from mpqp import ExpectationMeasure, Language, Observable, pI, pX @@ -47,23 +41,28 @@ def test_expectation_measure_wrong_targets( assert [set(swap.targets) for swap in measure.pre_measure] == expected_swaps -a, b, c = LineQubit.range(3) +# TODO: complete this +@pytest.fixture +def list_to_cirq_pauli() -> ( + list[tuple[Observable, Union[CirqPauliSum, CirqPauliString]]] +): + from cirq.devices.line_qubit import LineQubit + from cirq.ops.identity import I as Cirq_I + from cirq.ops.pauli_gates import X as Cirq_X + a, b = LineQubit.range(2) -# TODO: complete this -@pytest.mark.parametrize( - "obs, translation", - [ + return [ ( Observable(pI @ pI + pI @ pX), sum(1.0 * Cirq_I(a) * Cirq_I(b) + Cirq_X(b)), ), - ], -) -def test_to_other_language( - obs: Observable, - translation: Union[ - "Operator", QLMObservable, Hermitian, CirqPauliSum, CirqPauliString - ], + ] + + +@pytest.mark.provider("cirq") +def test_to_other_language_cirq( + list_to_cirq_pauli: list[tuple[Observable, Union[CirqPauliSum, CirqPauliString]]], ): - assert obs.to_other_language(Language.CIRQ) == translation + for obs, translation in list_to_cirq_pauli: + assert obs.to_other_language(Language.CIRQ) == translation diff --git a/tests/core/instruction/measurement/test_pauli_string.py b/tests/core/instruction/measurement/test_pauli_string.py index 47b5b025..464a40da 100644 --- a/tests/core/instruction/measurement/test_pauli_string.py +++ b/tests/core/instruction/measurement/test_pauli_string.py @@ -21,22 +21,17 @@ if TYPE_CHECKING: from qiskit.quantum_info import SparsePauliOp + from cirq.ops.linear_combinations import PauliSum + from braket.circuits.observables import Sum as BraketSum + from braket.circuits.observables import I as Braket_I + from braket.circuits.observables import X as Braket_X + from braket.circuits.observables import Y as Braket_Y + from braket.circuits.observables import Z as Braket_Z + from qat.core.wrappers.observable import Term import numpy as np import numpy.typing as npt import pytest -from braket.circuits.observables import I as Braket_I -from braket.circuits.observables import Sum as BraketSum -from braket.circuits.observables import X as Braket_X -from braket.circuits.observables import Y as Braket_Y -from braket.circuits.observables import Z as Braket_Z -from cirq.devices.line_qubit import LineQubit -from cirq.ops.identity import I as Cirq_I -from cirq.ops.linear_combinations import PauliSum -from cirq.ops.pauli_gates import X as Cirq_X -from cirq.ops.pauli_gates import Y as Cirq_Y -from cirq.ops.pauli_gates import Z as Cirq_Z -from qat.core.wrappers.observable import Term from sympy import Basic, Expr from mpqp import Language, pI, pX, pY, pZ @@ -222,345 +217,360 @@ def test_subs( assert result_ps == expected_ps -a, b, c = LineQubit.range(3) +@pytest.fixture +def list_pauli_strings() -> list[PauliString]: + return [ + pX @ pI @ pI + pI @ pY @ pI + pI @ pI @ pZ, + pX @ pY @ pZ, + pI @ pI @ pI + pI @ pZ @ pI + pI @ pI @ pX, + pY @ pZ @ pX, + pZ @ pY @ pI + pI @ pI @ pX, + pX @ pI @ pI + pI @ pI @ pY, + pI @ pX @ pI + pI @ pI @ pY, + 2 * pX @ pI @ pI + 3 * pI @ pY @ pI + 4 * pI @ pI @ pZ, + -pX @ (1.5 * pY) @ (0.5 * pZ), + ((0.5 * pZ) @ (0.5 * pY) @ pI) + (2 * pI @ pI @ pX), + (1.5 * pX @ pI @ pI) + (pI @ pI @ (-2.5 * pY)), + ((0.25 * pI) @ (4 * pX) @ pI) + (pI @ pI @ (3 * pY)), + pI, + pX, + pZ, + pY, + pI @ pI, + pI @ pX, + pI @ pZ, + pI @ pY, + pI + pI, + pI + pX, + pZ + pX, + pY + pZ, + pX + pY, + ] + + +@pytest.fixture +def list_pauli_strings_cirq() -> list["PauliSum"]: + from cirq.ops.identity import I as Cirq_I + from cirq.ops.pauli_gates import X as Cirq_X + from cirq.ops.pauli_gates import Y as Cirq_Y + from cirq.ops.pauli_gates import Z as Cirq_Z + from cirq.devices.line_qubit import LineQubit + + a, b, c = LineQubit.range(3) + + return [ + Cirq_X(a) + Cirq_Y(b) + Cirq_Z(c), # pyright: ignore[reportOperatorIssue] + Cirq_X(a) * Cirq_Y(b) * Cirq_Z(c), # pyright: ignore[reportOperatorIssue] + Cirq_I(a) + Cirq_Z(b) + Cirq_X(c), + Cirq_Y(a) * Cirq_Z(b) * Cirq_X(c), # pyright: ignore[reportOperatorIssue] + Cirq_Z(a) * Cirq_Y(b) + Cirq_X(c), # pyright: ignore[reportOperatorIssue] + Cirq_X(a) + Cirq_I(b) * Cirq_Y(c), + Cirq_I(a) * Cirq_X(b) + Cirq_Y(c), + 2 * Cirq_X(a) # pyright: ignore[reportOperatorIssue] + + 3 * Cirq_Y(b) # pyright: ignore[reportOperatorIssue] + + 4 * Cirq_Z(c), # pyright: ignore[reportOperatorIssue] + -Cirq_X(a) # pyright: ignore[reportOperatorIssue] + * (1.5 * Cirq_Y(b)) # pyright: ignore[reportOperatorIssue] + * (0.5 * Cirq_Z(c)), # pyright: ignore[reportOperatorIssue] + 0.5 * Cirq_Z(a) * 0.5 * Cirq_Y(b) # pyright: ignore[reportOperatorIssue] + + 2 * Cirq_X(c), # pyright: ignore[reportOperatorIssue] + 1.5 * Cirq_X(a) # pyright: ignore[reportOperatorIssue] + + Cirq_I(b) * -2.5 * Cirq_Y(c), + 0.25 * Cirq_I(a) * 4 * Cirq_X(b) + + 3 * Cirq_Y(c), # pyright: ignore[reportOperatorIssue] + Cirq_I(a), + Cirq_X(a), + Cirq_Z(a), + Cirq_Y(a), + 1 * Cirq_I(b), + 1 * Cirq_X(b), # pyright: ignore[reportOperatorIssue] + 1 * Cirq_Z(b), # pyright: ignore[reportOperatorIssue] + 1 * Cirq_Y(b), # pyright: ignore[reportOperatorIssue] + 1 * Cirq_I(a) + 1 * Cirq_I(a), + 1 * Cirq_I(a) + 1 * Cirq_X(a), # pyright: ignore[reportOperatorIssue] + 1 * Cirq_X(a) # pyright: ignore[reportOperatorIssue] + + 1 * Cirq_Z(a), # pyright: ignore[reportOperatorIssue] + 1 * Cirq_Y(a) # pyright: ignore[reportOperatorIssue] + + 1 * Cirq_Z(a), # pyright: ignore[reportOperatorIssue] + 1 * Cirq_X(a) # pyright: ignore[reportOperatorIssue] + + 1 * Cirq_Y(a), # pyright: ignore[reportOperatorIssue] + ] -def pauli_strings_in_all_languages() -> list[ - dict[ - Optional[Language], - Union[PauliSum, BraketSum, "SparsePauliOp", Term, PauliString], +@pytest.fixture +def list_pauli_strings_braket() -> ( + list[Union["BraketSum", "Braket_I", "Braket_X", "Braket_Y", "Braket_Z"]] +): + from braket.circuits.observables import I as Braket_I + from braket.circuits.observables import X as Braket_X + from braket.circuits.observables import Y as Braket_Y + from braket.circuits.observables import Z as Braket_Z + + return [ + Braket_X() @ Braket_I() @ Braket_I() + + Braket_I() @ Braket_Y() @ Braket_I() + + Braket_I() @ Braket_I() @ Braket_Z(), + Braket_X() @ Braket_Y() @ Braket_Z(), + Braket_I() @ Braket_I() @ Braket_I() + + Braket_I() @ Braket_Z() @ Braket_I() + + Braket_I() @ Braket_I() @ Braket_X(), + Braket_Y() @ Braket_Z() @ Braket_X(), + Braket_Z() @ Braket_Y() @ Braket_I() + Braket_I() @ Braket_I() @ Braket_X(), + Braket_X() @ Braket_I() @ Braket_I() + Braket_I() @ Braket_I() @ Braket_Y(), + Braket_I() @ Braket_X() @ Braket_I() + Braket_I() @ Braket_I() @ Braket_Y(), + 2 * Braket_X() @ Braket_I() @ Braket_I() # pyright: ignore[reportOperatorIssue] + + 3 + * Braket_I() # pyright: ignore[reportOperatorIssue] + @ Braket_Y() + @ Braket_I() + + 4 + * Braket_I() # pyright: ignore[reportOperatorIssue] + @ Braket_I() + @ Braket_Z(), + (-1 * Braket_X()) # pyright: ignore[reportOperatorIssue] + @ (1.5 * Braket_Y()) # pyright: ignore[reportOperatorIssue] + @ (0.5 * Braket_Z()), # pyright: ignore[reportOperatorIssue] + (0.5 * Braket_Z()) # pyright: ignore[reportOperatorIssue] + @ (0.5 * Braket_Y()) # pyright: ignore[reportOperatorIssue] + @ Braket_I() + + Braket_I() + @ Braket_I() + @ (2 * Braket_X()), # pyright: ignore[reportOperatorIssue] + 1.5 + * Braket_X() # pyright: ignore[reportOperatorIssue] + @ Braket_I() + @ Braket_I() + + Braket_I() + @ Braket_I() + @ (-2.5 * Braket_Y()), # pyright: ignore[reportOperatorIssue] + (0.25 * Braket_I()) # pyright: ignore[reportOperatorIssue] + @ (4 * Braket_X()) # pyright: ignore[reportOperatorIssue] + @ Braket_I() + + Braket_I() + @ Braket_I() + @ (3 * Braket_Y()), # pyright: ignore[reportOperatorIssue] + Braket_I(), + Braket_X(), + Braket_Z(), + Braket_Y(), + Braket_I() @ Braket_I(), + Braket_I() @ Braket_X(), + Braket_I() @ Braket_Z(), + Braket_I() @ Braket_Y(), + Braket_I() + Braket_I(), + Braket_I() + Braket_X(), + Braket_Z() + Braket_X(), + Braket_Y() + Braket_Z(), + Braket_X() + Braket_Y(), ] -]: + + +@pytest.fixture +def list_pauli_strings_qiskit() -> list["SparsePauliOp"]: from qiskit.quantum_info import SparsePauliOp return [ - { - Language.CIRQ: Cirq_X(a) - + Cirq_Y(b) # pyright: ignore[reportOperatorIssue] - + Cirq_Z(c), - Language.BRAKET: Braket_X() @ Braket_I() @ Braket_I() - + Braket_I() @ Braket_Y() @ Braket_I() - + Braket_I() @ Braket_I() @ Braket_Z(), - Language.QISKIT: SparsePauliOp(["XII", "IYI", "IIZ"]), - Language.MY_QLM: [ - Term(1, "X", [0]), - Term(1, "Y", [1]), - Term(1, "Z", [2]), - ], - None: pX @ pI @ pI + pI @ pY @ pI + pI @ pI @ pZ, - }, - { - Language.CIRQ: Cirq_X(a) - * Cirq_Y(b) # pyright: ignore[reportOperatorIssue] - * Cirq_Z(c), - Language.BRAKET: Braket_X() @ Braket_Y() @ Braket_Z(), - Language.QISKIT: SparsePauliOp(["XYZ"]), - Language.MY_QLM: Term(1, "XYZ", [0, 1, 2]), - None: pX @ pY @ pZ, - }, - { - Language.CIRQ: Cirq_I(a) + Cirq_Z(b) + Cirq_X(c), - Language.BRAKET: Braket_I() @ Braket_I() @ Braket_I() - + Braket_I() @ Braket_Z() @ Braket_I() - + Braket_I() @ Braket_I() @ Braket_X(), - Language.QISKIT: SparsePauliOp(["III", "IZI", "IIX"]), - Language.MY_QLM: [ - Term(1, "I", [0]), - Term(1, "Z", [1]), - Term(1, "X", [2]), - ], - None: pI @ pI @ pI + pI @ pZ @ pI + pI @ pI @ pX, - }, - { - Language.CIRQ: Cirq_Y(a) - * Cirq_Z(b) # pyright: ignore[reportOperatorIssue] - * Cirq_X(c), - Language.BRAKET: Braket_Y() @ Braket_Z() @ Braket_X(), - Language.QISKIT: SparsePauliOp(["YZX"]), - Language.MY_QLM: Term(1, "YZX", [0, 1, 2]), - None: pY @ pZ @ pX, - }, - { - Language.CIRQ: Cirq_Z(a) * Cirq_Y(b) # pyright: ignore[reportOperatorIssue] - + Cirq_X(c), - Language.BRAKET: Braket_Z() @ Braket_Y() @ Braket_I() - + Braket_I() @ Braket_I() @ Braket_X(), - Language.QISKIT: SparsePauliOp(["ZYI", "IIX"]), - Language.MY_QLM: [Term(1, "ZY", [0, 1]), Term(1, "X", [2])], - None: pZ @ pY @ pI + pI @ pI @ pX, - }, - { - Language.CIRQ: Cirq_X(a) + Cirq_I(b) * Cirq_Y(c), - Language.BRAKET: Braket_X() @ Braket_I() @ Braket_I() - + Braket_I() @ Braket_I() @ Braket_Y(), - Language.QISKIT: SparsePauliOp(["XII", "IIY"]), - Language.MY_QLM: [Term(1, "X", [0]), Term(1, "Y", [2])], - None: pX @ pI @ pI + pI @ pI @ pY, - }, - { - Language.CIRQ: Cirq_I(a) * Cirq_X(b) + Cirq_Y(c), - Language.BRAKET: Braket_I() @ Braket_X() @ Braket_I() - + Braket_I() @ Braket_I() @ Braket_Y(), - Language.QISKIT: SparsePauliOp(["IXI", "IIY"]), - Language.MY_QLM: [Term(1, "X", [1]), Term(1, "Y", [2])], - None: pI @ pX @ pI + pI @ pI @ pY, - }, - { - Language.CIRQ: 2 * Cirq_X(a) # pyright: ignore[reportOperatorIssue] - + 3 * Cirq_Y(b) # pyright: ignore[reportOperatorIssue] - + 4 * Cirq_Z(c), # pyright: ignore[reportOperatorIssue] - Language.BRAKET: 2 - * Braket_X() # pyright: ignore[reportOperatorIssue] - @ Braket_I() - @ Braket_I() - + 3 - * Braket_I() # pyright: ignore[reportOperatorIssue] - @ Braket_Y() - @ Braket_I() - + 4 - * Braket_I() # pyright: ignore[reportOperatorIssue] - @ Braket_I() - @ Braket_Z(), - Language.QISKIT: SparsePauliOp( - ["XII", "IYI", "IIZ"], coeffs=np.array([2, 3, 4]) - ), - Language.MY_QLM: [ - Term(2, "X", [0]), - Term(3, "Y", [1]), - Term(4, "Z", [2]), - ], - None: 2 * pX @ pI @ pI + 3 * pI @ pY @ pI + 4 * pI @ pI @ pZ, - }, - { - Language.CIRQ: -Cirq_X(a) # pyright: ignore[reportOperatorIssue] - * (1.5 * Cirq_Y(b)) # pyright: ignore[reportOperatorIssue] - * (0.5 * Cirq_Z(c)), # pyright: ignore[reportOperatorIssue] - Language.BRAKET: (-1 * Braket_X()) # pyright: ignore[reportOperatorIssue] - @ (1.5 * Braket_Y()) # pyright: ignore[reportOperatorIssue] - @ (0.5 * Braket_Z()), # pyright: ignore[reportOperatorIssue] - Language.QISKIT: SparsePauliOp(["XYZ"], coeffs=np.array([-1 * 1.5 * 0.5])), - Language.MY_QLM: Term(-0.75, "XYZ", [0, 1, 2]), - None: -pX @ (1.5 * pY) @ (0.5 * pZ), - }, - { - Language.CIRQ: 0.5 - * Cirq_Z(a) # pyright: ignore[reportOperatorIssue] - * 0.5 - * Cirq_Y(b) - + 2 * Cirq_X(c), # pyright: ignore[reportOperatorIssue] - Language.BRAKET: (0.5 * Braket_Z()) # pyright: ignore[reportOperatorIssue] - @ (0.5 * Braket_Y()) # pyright: ignore[reportOperatorIssue] - @ Braket_I() - + Braket_I() - @ Braket_I() - @ (2 * Braket_X()), # pyright: ignore[reportOperatorIssue] - Language.QISKIT: SparsePauliOp( - ["ZYI", "IIX"], coeffs=np.array([0.5 * 0.5, 2]) - ), - Language.MY_QLM: [Term(0.25, "ZY", [0, 1]), Term(2, "X", [2])], - None: ((0.5 * pZ) @ (0.5 * pY) @ pI) + (2 * pI @ pI @ pX), - }, - { - Language.CIRQ: 1.5 * Cirq_X(a) # pyright: ignore[reportOperatorIssue] - + Cirq_I(b) * -2.5 * Cirq_Y(c), - Language.BRAKET: 1.5 - * Braket_X() # pyright: ignore[reportOperatorIssue] - @ Braket_I() - @ Braket_I() - + Braket_I() - @ Braket_I() - @ (-2.5 * Braket_Y()), # pyright: ignore[reportOperatorIssue] - Language.QISKIT: SparsePauliOp( - ["XII", "IIY"], coeffs=np.array([1.5, -2.5]) - ), - Language.MY_QLM: [Term(1.5, "X", [0]), Term(-2.5, "Y", [2])], - None: (1.5 * pX @ pI @ pI) + (pI @ pI @ (-2.5 * pY)), - }, - { - Language.CIRQ: 0.25 * Cirq_I(a) * 4 * Cirq_X(b) - + 3 * Cirq_Y(c), # pyright: ignore[reportOperatorIssue] - Language.BRAKET: (0.25 * Braket_I()) # pyright: ignore[reportOperatorIssue] - @ (4 * Braket_X()) # pyright: ignore[reportOperatorIssue] - @ Braket_I() - + Braket_I() - @ Braket_I() - @ (3 * Braket_Y()), # pyright: ignore[reportOperatorIssue] - Language.QISKIT: SparsePauliOp( - ["IXI", "IIY"], coeffs=np.array([0.25 * 4, 3]) - ), - Language.MY_QLM: [Term(4 * 0.25, "X", [1]), Term(3, "Y", [2])], - None: ((0.25 * pI) @ (4 * pX) @ pI) + (pI @ pI @ (3 * pY)), - }, - { - Language.CIRQ: Cirq_I(a), - Language.BRAKET: Braket_I(), - Language.QISKIT: SparsePauliOp(["I"]), - Language.MY_QLM: Term(1, "I", [0]), - None: pI, - }, - { - Language.CIRQ: Cirq_X(a), - Language.BRAKET: Braket_X(), - Language.QISKIT: SparsePauliOp(["X"]), - Language.MY_QLM: Term(1, "X", [0]), - None: pX, - }, - { - Language.CIRQ: Cirq_Z(a), - Language.BRAKET: Braket_Z(), - Language.QISKIT: SparsePauliOp(["Z"]), - Language.MY_QLM: Term(1, "Z", [0]), - None: pZ, - }, - { - Language.CIRQ: Cirq_Y(a), - Language.BRAKET: Braket_Y(), - Language.QISKIT: SparsePauliOp(["Y"]), - Language.MY_QLM: Term(1, "Y", [0]), - None: pY, - }, - { - Language.CIRQ: 1 * Cirq_I(b), - Language.BRAKET: Braket_I() @ Braket_I(), - Language.QISKIT: SparsePauliOp(["II"]), - Language.MY_QLM: Term(1, "II", [0, 1]), - None: pI @ pI, - }, - { - Language.CIRQ: 1 * Cirq_X(b), # pyright: ignore[reportOperatorIssue] - Language.BRAKET: Braket_I() @ Braket_X(), - Language.QISKIT: SparsePauliOp(["IX"]), - Language.MY_QLM: Term(1, "X", [1]), - None: pI @ pX, - }, - { - Language.CIRQ: 1 * Cirq_Z(b), # pyright: ignore[reportOperatorIssue] - Language.BRAKET: Braket_I() @ Braket_Z(), - Language.QISKIT: SparsePauliOp(["IZ"]), - Language.MY_QLM: Term(1, "Z", [1]), - None: pI @ pZ, - }, - { - Language.CIRQ: 1 * Cirq_Y(b), # pyright: ignore[reportOperatorIssue] - Language.BRAKET: Braket_I() @ Braket_Y(), - Language.QISKIT: SparsePauliOp(["IY"]), - Language.MY_QLM: Term(1, "Y", [1]), - None: pI @ pY, - }, - { - Language.CIRQ: 1 * Cirq_I(a) + 1 * Cirq_I(a), - Language.BRAKET: Braket_I() + Braket_I(), - Language.QISKIT: SparsePauliOp(["I", "I"]), - Language.MY_QLM: [Term(1, "I", [0]), Term(1, "I", [0])], - None: pI + pI, - }, - { - Language.CIRQ: 1 * Cirq_I(a) - + 1 * Cirq_X(a), # pyright: ignore[reportOperatorIssue] - Language.BRAKET: Braket_I() + Braket_X(), - Language.QISKIT: SparsePauliOp(["I", "X"]), - Language.MY_QLM: [Term(1, "I", [0]), Term(1, "X", [0])], - None: pI + pX, - }, - { - Language.CIRQ: 1 * Cirq_X(a) # pyright: ignore[reportOperatorIssue] - + 1 * Cirq_Z(a), # pyright: ignore[reportOperatorIssue] - Language.BRAKET: Braket_Z() + Braket_X(), - Language.QISKIT: SparsePauliOp(["Z", "X"]), - Language.MY_QLM: [Term(1, "Z", [0]), Term(1, "X", [0])], - None: pZ + pX, - }, - { - Language.CIRQ: 1 * Cirq_Y(a) # pyright: ignore[reportOperatorIssue] - + 1 * Cirq_Z(a), # pyright: ignore[reportOperatorIssue] - Language.BRAKET: Braket_Y() + Braket_Z(), - Language.QISKIT: SparsePauliOp(["Y", "Z"]), - Language.MY_QLM: [Term(1, "Y", [0]), Term(1, "Z", [0])], - None: pY + pZ, - }, - { - Language.CIRQ: 1 * Cirq_X(a) # pyright: ignore[reportOperatorIssue] - + 1 * Cirq_Y(a), # pyright: ignore[reportOperatorIssue] - Language.BRAKET: Braket_X() + Braket_Y(), - Language.QISKIT: SparsePauliOp(["X", "Y"]), - Language.MY_QLM: [Term(1, "X", [0]), Term(1, "Y", [0])], - None: pX + pY, - }, + SparsePauliOp(["XII", "IYI", "IIZ"]), + SparsePauliOp(["XYZ"]), + SparsePauliOp(["III", "IZI", "IIX"]), + SparsePauliOp(["YZX"]), + SparsePauliOp(["ZYI", "IIX"]), + SparsePauliOp(["XII", "IIY"]), + SparsePauliOp(["IXI", "IIY"]), + SparsePauliOp( + ["XII", "IYI", "IIZ"], + coeffs=[2.0, 3.0, 4.0], # pyright: ignore[reportArgumentType] + ), + SparsePauliOp(["XYZ"], coeffs=[-0.75]), # pyright: ignore[reportArgumentType] + SparsePauliOp( + ["ZYI", "IIX"], coeffs=[0.25, 2.0] # pyright: ignore[reportArgumentType] + ), + SparsePauliOp( + ["XII", "IIY"], coeffs=[1.5, -2.5] # pyright: ignore[reportArgumentType] + ), + SparsePauliOp( + ["IXI", "IIY"], coeffs=[1.0, 3.0] # pyright: ignore[reportArgumentType] + ), + SparsePauliOp(["I"]), + SparsePauliOp(["X"]), + SparsePauliOp(["Z"]), + SparsePauliOp(["Y"]), + SparsePauliOp(["II"]), + SparsePauliOp(["IX"]), + SparsePauliOp(["IZ"]), + SparsePauliOp(["IY"]), + SparsePauliOp(["I", "I"]), + SparsePauliOp(["I", "X"]), + SparsePauliOp(["Z", "X"]), + SparsePauliOp(["Y", "Z"]), + SparsePauliOp(["X", "Y"]), + ] + + +@pytest.fixture +def list_pauli_strings_my_qlm() -> list[list["Term"]]: + from qat.core.wrappers.observable import Term + + return [ + [ + Term(1, "X", [0]), + Term(1, "Y", [1]), + Term(1, "Z", [2]), + ], + Term(1, "XYZ", [0, 1, 2]), + [ + Term(1, "I", [0]), + Term(1, "Z", [1]), + Term(1, "X", [2]), + ], + Term(1, "YZX", [0, 1, 2]), + [Term(1, "ZY", [0, 1]), Term(1, "X", [2])], + [Term(1, "X", [0]), Term(1, "Y", [2])], + [Term(1, "X", [1]), Term(1, "Y", [2])], + [Term(2, "X", [0]), Term(3, "Y", [1]), Term(4, "Z", [2])], + Term(-0.75, "XYZ", [0, 1, 2]), + [Term(0.25, "ZY", [0, 1]), Term(2, "X", [2])], + [Term(1.5, "X", [0]), Term(-2.5, "Y", [2])], + [Term(4 * 0.25, "X", [1]), Term(3, "Y", [2])], + Term(1, "I", [0]), + Term(1, "X", [0]), + Term(1, "Z", [0]), + Term(1, "Y", [0]), + Term(1, "II", [0, 1]), + Term(1, "X", [1]), + Term(1, "Z", [1]), + Term(1, "Y", [1]), + [Term(1, "I", [0]), Term(1, "I", [0])], + [Term(1, "I", [0]), Term(1, "X", [0])], + [Term(1, "Z", [0]), Term(1, "X", [0])], + [Term(1, "Y", [0]), Term(1, "Z", [0])], + [Term(1, "X", [0]), Term(1, "Y", [0])], ] -@pytest.mark.parametrize( - "pauli_strings", - pauli_strings_in_all_languages(), -) -def test_from_other_language( - pauli_strings: dict[ - Optional[Language], - Union[PauliSum, BraketSum, "SparsePauliOp", Term, PauliString], - ], +@pytest.mark.provider("cirq") +def test_from_other_language_cirq( + list_pauli_strings: list[PauliString], + list_pauli_strings_cirq: list["PauliSum"], +): + for mpqp_ps, cirq_ps in zip(list_pauli_strings, list_pauli_strings_cirq): + assert PauliString.from_other_language(cirq_ps, mpqp_ps.nb_qubits) == mpqp_ps + + +@pytest.mark.provider("braket") +def test_from_other_language_braket( + list_pauli_strings: list[PauliString], + list_pauli_strings_braket: list["BraketSum"], +): + for mpqp_ps, braket_ps in zip(list_pauli_strings, list_pauli_strings_braket): + assert PauliString.from_other_language(braket_ps) == mpqp_ps + + +@pytest.mark.provider("qiskit") +def test_from_other_language_qiskit( + list_pauli_strings: list[PauliString], + list_pauli_strings_qiskit: list["SparsePauliOp"], +): + for mpqp_ps, qiskit_ps in zip(list_pauli_strings, list_pauli_strings_qiskit): + assert PauliString.from_other_language(qiskit_ps) == mpqp_ps + + +@pytest.mark.provider("myqlm") +def test_from_other_language_my_qlm( + list_pauli_strings: list[PauliString], + list_pauli_strings_my_qlm: list["Term"], +): + for mpqp_ps, my_qlm_ps in zip(list_pauli_strings, list_pauli_strings_my_qlm): + assert PauliString.from_other_language(my_qlm_ps) == mpqp_ps + + +@pytest.mark.provider("cirq") +def test_to_other_language_cirq( + list_pauli_strings: list[PauliString], + list_pauli_strings_cirq: list["PauliSum"], +): + for mpqp_ps, cirq_ps in zip(list_pauli_strings, list_pauli_strings_cirq): + assert mpqp_ps.to_other_language(Language.CIRQ) == cirq_ps + + +@pytest.mark.provider("braket") +def test_to_other_language_braket( + list_pauli_strings: list[PauliString], + list_pauli_strings_braket: list["BraketSum"], +): + for mpqp_ps, braket_ps in zip(list_pauli_strings, list_pauli_strings_braket): + assert repr(mpqp_ps.to_other_language(Language.BRAKET)) == repr(braket_ps) + + +@pytest.mark.provider("qiskit") +def test_to_other_language_qiskit( + list_pauli_strings: list[PauliString], + list_pauli_strings_qiskit: list["SparsePauliOp"], +): + for mpqp_ps, qiskit_ps in zip(list_pauli_strings, list_pauli_strings_qiskit): + assert mpqp_ps.to_other_language(Language.QISKIT) == qiskit_ps + + +@pytest.mark.provider("myqlm") +def test_to_other_language_my_qlm( + list_pauli_strings: list[PauliString], + list_pauli_strings_my_qlm: list[list["Term"]], +): + for mpqp_ps, my_qlm_ps in zip(list_pauli_strings, list_pauli_strings_my_qlm): + assert mpqp_ps.to_other_language(Language.MY_QLM) == my_qlm_ps + + +@pytest.mark.provider("cirq") +def test_to_from_other_language_cirq( + list_pauli_strings: list[PauliString], ): - mpqp_ps = pauli_strings[None] - assert isinstance(mpqp_ps, PauliString) - for language, ps in pauli_strings.items(): - if language is not None: - assert ( - PauliString.from_other_language( - ps, mpqp_ps.nb_qubits if language == Language.CIRQ else 1 - ) - == mpqp_ps + for mpqp_ps in list_pauli_strings: + assert ( + PauliString.from_other_language( + mpqp_ps.to_other_language(Language.CIRQ), + mpqp_ps.nb_qubits, ) + == mpqp_ps + ) -@pytest.mark.parametrize( - "pauli_strings", - pauli_strings_in_all_languages(), -) -def test_to_other_language( - pauli_strings: dict[ - Optional[Language], - Union[PauliSum, BraketSum, "SparsePauliOp", Term, PauliString], - ], +@pytest.mark.provider("braket") +def test_to_from_other_language_braket( + list_pauli_strings: list[PauliString], ): - mpqp_ps = pauli_strings[None] - assert isinstance(mpqp_ps, PauliString) - for language, ps in pauli_strings.items(): - if language is not None: - if language == Language.BRAKET: - assert repr(mpqp_ps.to_other_language(language)) == repr(ps) - else: - assert mpqp_ps.to_other_language(language) == ps + for mpqp_ps in list_pauli_strings: + assert ( + PauliString.from_other_language( + mpqp_ps.to_other_language(Language.BRAKET), + ) + == mpqp_ps + ) -@pytest.mark.parametrize( - "mpqp_ps, language", - product( - [all_ps[None] for all_ps in pauli_strings_in_all_languages()], - [Language.BRAKET, Language.CIRQ, Language.MY_QLM, Language.QISKIT], - ), -) -def test_to_from_other_language(mpqp_ps: PauliString, language: Language): - print( - PauliString.from_other_language( - mpqp_ps.to_other_language(language), - mpqp_ps.nb_qubits if language == Language.CIRQ else 1, - ).to_dict() # type: ignore - ) - print( - PauliString.from_other_language( - mpqp_ps.to_other_language(language), - mpqp_ps.nb_qubits if language == Language.CIRQ else 1, +@pytest.mark.provider("qiskit") +def test_to_from_other_language_qiskit( + list_pauli_strings: list[PauliString], +): + for mpqp_ps in list_pauli_strings: + assert ( + PauliString.from_other_language( + mpqp_ps.to_other_language(Language.QISKIT), + ) + == mpqp_ps ) - .monomials[0] # type: ignore - .coef - ) - print(mpqp_ps.to_dict()) - assert ( - PauliString.from_other_language( - mpqp_ps.to_other_language(language), - mpqp_ps.nb_qubits if language == Language.CIRQ else 1, + + +@pytest.mark.provider("myqlm") +def test_to_from_other_language_my_qlm( + list_pauli_strings: list[PauliString], +): + for mpqp_ps in list_pauli_strings: + assert ( + PauliString.from_other_language( + mpqp_ps.to_other_language(Language.MY_QLM), + ) + == mpqp_ps ) - == mpqp_ps - ) @pytest.mark.parametrize( diff --git a/tests/core/instruction/test_breakpoint.py b/tests/core/instruction/test_breakpoint.py index edba527b..bd2fc338 100644 --- a/tests/core/instruction/test_breakpoint.py +++ b/tests/core/instruction/test_breakpoint.py @@ -2,21 +2,25 @@ from pytest import CaptureFixture from mpqp import CNOT, ATOSDevice, Breakpoint, H, QCircuit, Y, run +from mpqp.execution.devices import ( + AvailableDevice, + IBMDevice, + GOOGLEDevice, + ATOSDevice, + AWSDevice, +) - -@pytest.mark.parametrize( - "circuit, expected_out", - [ - ( - QCircuit([H(0), CNOT(0, 1), Breakpoint(), Y(1)]), - """\ +list_circuit_expected_out = [ + ( + QCircuit([H(0), CNOT(0, 1), Breakpoint(), Y(1)]), + """\ DEBUG: After instruction 2, state is 0.707|00⟩ + 0.707|11⟩ """, - ), - ( - QCircuit([H(0), CNOT(0, 1), Breakpoint(draw_circuit=True), Y(1)]), - """\ + ), + ( + QCircuit([H(0), CNOT(0, 1), Breakpoint(draw_circuit=True), Y(1)]), + """\ DEBUG: After instruction 2, state is 0.707|00⟩ + 0.707|11⟩ and circuit is @@ -26,10 +30,10 @@ q_1: ─────┤ X ├ └───┘ """, - ), - ( - QCircuit([H(0), Y(0), Breakpoint(draw_circuit=True), CNOT(0, 1)]), - """\ + ), + ( + QCircuit([H(0), Y(0), Breakpoint(draw_circuit=True), CNOT(0, 1)]), + """\ DEBUG: After instruction 2, state is 0.707j|00⟩ + 0.707j|10⟩ and circuit is @@ -39,30 +43,30 @@ q_1: ────────── """, - ), - ( - QCircuit([H(0), CNOT(0, 1), Breakpoint(enabled=False), Y(1)]), - "", - ), - ( - QCircuit([H(0), CNOT(0, 1), Breakpoint(label="Bell state"), Y(1)]), - """\ + ), + ( + QCircuit([H(0), CNOT(0, 1), Breakpoint(enabled=False), Y(1)]), + "", + ), + ( + QCircuit([H(0), CNOT(0, 1), Breakpoint(label="Bell state"), Y(1)]), + """\ DEBUG: After instruction 2, at breakpoint `Bell state`, state is 0.707|00⟩ + 0.707|11⟩ """, + ), + ( + QCircuit( + [ + H(0), + Breakpoint(enabled=False), + CNOT(0, 1), + Breakpoint(draw_circuit=True, label="Bell state"), + Y(1), + Breakpoint(), + ] ), - ( - QCircuit( - [ - H(0), - Breakpoint(enabled=False), - CNOT(0, 1), - Breakpoint(draw_circuit=True, label="Bell state"), - Y(1), - Breakpoint(), - ] - ), - """\ + """\ DEBUG: After instruction 2, at breakpoint `Bell state`, state is 0.707|00⟩ + 0.707|11⟩ and circuit is @@ -74,10 +78,60 @@ DEBUG: After instruction 3, state is 0.707j|01⟩ - 0.707j|10⟩ """, - ), - ], + ), +] + + +@pytest.mark.provider("qiskit") +@pytest.mark.parametrize( + "circuit, expected_out", + list_circuit_expected_out, +) +def test_capture_qiskit( + circuit: QCircuit, expected_out: str, capsys: CaptureFixture[str] +): + exec_capture(circuit, expected_out, capsys, IBMDevice.AER_SIMULATOR) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize( + "circuit, expected_out", + list_circuit_expected_out, +) +def test_capture_braket( + circuit: QCircuit, expected_out: str, capsys: CaptureFixture[str] +): + exec_capture(circuit, expected_out, capsys, AWSDevice.BRAKET_LOCAL_SIMULATOR) + + +@pytest.mark.provider("cirq") +@pytest.mark.parametrize( + "circuit, expected_out", + list_circuit_expected_out, ) -def test_capture(circuit: QCircuit, expected_out: str, capsys: CaptureFixture[str]): - run(circuit, ATOSDevice.MYQLM_CLINALG) +def test_capture_cirq( + circuit: QCircuit, expected_out: str, capsys: CaptureFixture[str] +): + exec_capture(circuit, expected_out, capsys, GOOGLEDevice.CIRQ_LOCAL_SIMULATOR) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize( + "circuit, expected_out", + list_circuit_expected_out, +) +def test_capture_myqlm( + circuit: QCircuit, expected_out: str, capsys: CaptureFixture[str] +): + exec_capture(circuit, expected_out, capsys, ATOSDevice.MYQLM_CLINALG) + + +def exec_capture( + circuit: QCircuit, + expected_out: str, + capsys: CaptureFixture[str], + device: AvailableDevice, +): + run(circuit, device) captured = capsys.readouterr() assert captured.out == expected_out diff --git a/tests/core/test_circuit.py b/tests/core/test_circuit.py index 4f4ae59f..96e9a3a2 100644 --- a/tests/core/test_circuit.py +++ b/tests/core/test_circuit.py @@ -6,18 +6,25 @@ import numpy as np import numpy.typing as npt import pytest -from braket.circuits import Circuit as BraketCircuit -from braket.circuits import Noise as BraketNoise -from braket.circuits.gate import Gate as BraketGate -from cirq.circuits.circuit import Circuit as cirq_Circuit -from cirq.circuits.moment import Moment -from cirq.testing.random_circuit import random_circuit as random_cirq_circuit -from qat.core.wrappers.circuit import Circuit as myQLM_Circuit from qiskit import ClassicalRegister from qiskit import QuantumCircuit as QiskitCircuit from qiskit import QuantumRegister from qiskit.circuit.random import random_circuit as random_qiskit_circuit +from mpqp.execution.devices import ( + AvailableDevice, + ATOSDevice, + GOOGLEDevice, + IBMDevice, + AWSDevice, +) + +if TYPE_CHECKING: + from braket.circuits import Circuit as BraketCircuit + from cirq.circuits.circuit import Circuit as cirq_Circuit + from cirq.circuits.moment import Moment + from qat.core.wrappers.circuit import Circuit as myQLM_Circuit + from mpqp import ( CNOT, CZ, @@ -110,6 +117,10 @@ def list_qiskit_funky_circuits() -> list[QiskitCircuit]: @pytest.fixture def list_braket_funky_circuits() -> list[BraketCircuit]: + from braket.circuits import Circuit as BraketCircuit + from braket.circuits import Noise as BraketNoise + from braket.circuits.gate import Gate as BraketGate + braket_circuit1 = BraketCircuit().h(0).x(control=0, target=1) # type: ignore[reportAttributeAccessIssue] braket_circuit1.ry(angle=0.13, target=2, control=(0, 1)) braket_circuit1.x(0, power=1 / 5) @@ -407,9 +418,9 @@ def test_tensor( ) -@pytest.mark.parametrize( - "state", - [ +@pytest.fixture +def states() -> list[npt.NDArray[np.complex128]]: + return [ (np.array([1 / np.sqrt(2), 1 / np.sqrt(2)])), (np.array([1, 0, 0, 1]) / np.sqrt(2)), (statevector_from_random_circuit(1)), @@ -418,15 +429,18 @@ def test_tensor( (statevector_from_random_circuit(6)), (np.array([1 / 2, np.sqrt(3) / 2])), (np.array([1, 1j]) / np.sqrt(2)), - ], -) -def test_initializer(state: npt.NDArray[np.complex128]): - qc = QCircuit.initializer(state) - res = run(qc, IBMDevice.AER_SIMULATOR_STATEVECTOR) - if TYPE_CHECKING: - assert isinstance(res, Result) - state_vector_initialized = res.state_vector.vector - assert matrix_eq(state, state_vector_initialized) + ] + + +@pytest.mark.provider("qiskit") +def test_initializer(states: list[npt.NDArray[np.complex128]]): + for state in states: + qc = QCircuit.initializer(state) + res = run(qc, IBMDevice.AER_SIMULATOR_STATEVECTOR) + if TYPE_CHECKING: + assert isinstance(res, Result) + state_vector_initialized = res.state_vector.vector + assert matrix_eq(state, state_vector_initialized) @pytest.mark.parametrize( @@ -488,39 +502,40 @@ def test_without_measurements(circuit: QCircuit, printed_result_filename: str): assert str(circuit.without_measurements()) == f.read() +@pytest.mark.provider("qiskit") @pytest.mark.parametrize( - "circuit, args, result_type, result_repr", + "circuit, result_type, result_repr", [ ( QCircuit([X(0), CNOT(0, 1)]), - (), QiskitCircuit, ( "[CircuitInstruction(operation=Instruction(name='x', num_qubits=1," - " num_clbits=0, params=[]), qubits=(Qubit(QuantumRegister(2, 'q'), 1),)," + " num_clbits=0, params=[]), qubits=(,)," " clbits=()), CircuitInstruction(operation=Instruction(name='cx'," - " num_qubits=2, num_clbits=0, params=[]), qubits=(Qubit(QuantumRegister(2," - " 'q'), 1), Qubit(QuantumRegister(2, 'q'), 0)), clbits=())]" - ), - ), - ( - QCircuit([X(0), CNOT(0, 1)]), - (Language.QISKIT,), - QiskitCircuit, - ( - "[CircuitInstruction(operation=Instruction(name='x', num_qubits=1," - " num_clbits=0, params=[]), qubits=(Qubit(QuantumRegister(2, 'q'), 1),)," - " clbits=()), CircuitInstruction(operation=Instruction(name='cx'," - " num_qubits=2, num_clbits=0, params=[]), qubits=(Qubit(QuantumRegister(2," - " 'q'), 1), Qubit(QuantumRegister(2, 'q'), 0)), clbits=())]" + " num_qubits=2, num_clbits=0, params=[]), qubits=(, ), clbits=())]" ), ), + ], +) +def test_to_other_language_qiskit( + circuit: QCircuit, result_type: type, result_repr: str +): + converted_circuit = circuit.to_other_language(Language.QISKIT) + assert type(converted_circuit) == result_type + assert repr(converted_circuit.data) == result_repr + + +@pytest.fixture +def list_braket_circuit() -> list[tuple[QCircuit, type, str]]: + from braket.circuits import Circuit as BraketCircuit + + return [ ( QCircuit([CNOT(0, 1), Depolarizing(0.5, [0, 1])]), - (Language.BRAKET,), BraketCircuit, - ( - """\ + ("""\ T : │ 0 │ ┌───────────┐ q0 : ───●───┤ DEPO(0.5) ├─ @@ -528,15 +543,12 @@ def test_without_measurements(circuit: QCircuit, printed_result_filename: str): ┌─┴─┐ ┌───────────┐ q1 : ─┤ X ├─┤ DEPO(0.5) ├─ └───┘ └───────────┘ -T : │ 0 │""" - ), +T : │ 0 │"""), ), ( QCircuit([CNOT(0, 1), Depolarizing(0.5, [0, 1], dimension=2)]), - (Language.BRAKET,), BraketCircuit, - ( - """\ + ("""\ T : │ 0 │ ┌───────────┐ q0 : ───●───┤ DEPO(0.5) ├─ @@ -544,17 +556,14 @@ def test_without_measurements(circuit: QCircuit, printed_result_filename: str): ┌─┴─┐ ┌─────┴─────┐ q1 : ─┤ X ├─┤ DEPO(0.5) ├─ └───┘ └───────────┘ -T : │ 0 │""" - ), +T : │ 0 │"""), ), ( QCircuit( [CNOT(0, 1), Depolarizing(0.5, [0, 1], dimension=2, gates=[CNOT])] ), - (Language.BRAKET,), BraketCircuit, - ( - """\ + ("""\ T : │ 0 │ ┌───────────┐ q0 : ───●───┤ DEPO(0.5) ├─ @@ -562,32 +571,26 @@ def test_without_measurements(circuit: QCircuit, printed_result_filename: str): ┌─┴─┐ ┌─────┴─────┐ q1 : ─┤ X ├─┤ DEPO(0.5) ├─ └───┘ └───────────┘ -T : │ 0 │""" - ), +T : │ 0 │"""), ), - ], -) -def test_to_other_language( - circuit: QCircuit, args: tuple[Language], result_type: type, result_repr: str + ] + + +@pytest.mark.provider("braket") +def test_to_other_language_braket( + list_braket_circuit: list[tuple[QCircuit, type, str]], ): - language = Language.QISKIT if len(args) == 0 else args[0] - # TODO: test other languages - if language == Language.BRAKET: + for circuit, result_type, result_repr in list_braket_circuit: with pytest.warns(UnsupportedBraketFeaturesWarning) as record: - converted_circuit = circuit.to_other_language(*args) + converted_circuit = circuit.to_other_language(Language.BRAKET) + assert type(converted_circuit) == result_type assert len(record) == 1 - else: - converted_circuit = circuit.to_other_language(*args) - assert type(converted_circuit) == result_type - if isinstance(converted_circuit, QiskitCircuit): - assert repr(converted_circuit.data) == result_repr - if isinstance(converted_circuit, BraketCircuit): assert str(converted_circuit) == result_repr def _create_large_circuits_for_tests() -> tuple[QiskitCircuit, QiskitCircuit]: from qiskit import ClassicalRegister, QuantumRegister - from qiskit.circuit.library import RC3XGate + from qiskit.circuit.library import RC3XGate, RZZGate qreg_q = QuantumRegister(4, 'q') creg_c = ClassicalRegister(4, 'c') @@ -619,38 +622,46 @@ def _create_large_circuits_for_tests() -> tuple[QiskitCircuit, QiskitCircuit]: circuit.id(qreg_q[3]) circuit_2 = QiskitCircuit(qreg_q, creg_c) - circuit_2.rzz(np.pi / 2, qreg_q[1], qreg_q[2]).c_if(creg_c, 0) + with circuit_2.if_test((creg_c, 0)): + circuit_2.append(RZZGate(np.pi / 2), [qreg_q[1], qreg_q[2]]) return circuit, circuit_2 +@pytest.mark.provider("qiskit") @pytest.mark.parametrize( - "circuit, language, expected_output", + "circuit, expected_output", + [ + (random_qiskit_circuit(2, 5), None), + (random_qiskit_circuit(5, 5), None), + (random_qiskit_circuit(10, 5), None), + (_create_large_circuits_for_tests()[0], None), + (_create_large_circuits_for_tests()[1], "\"If\" instructions aren't handled"), + ], +) +def test_from_qiskit(circuit: QiskitCircuit, expected_output: Optional[str]): + from qiskit.quantum_info import Operator + + if not isinstance(expected_output, str): + qcircuit = QCircuit.from_other_language(circuit) + matrix = Operator(circuit.reverse_bits()).data + assert matrix_eq( + matrix, qcircuit.to_matrix() # pyright: ignore[reportArgumentType] + ) + else: + with pytest.raises(ValueError, match=expected_output): + QCircuit.from_other_language(circuit) + + +@pytest.mark.parametrize( + "circuit, expected_output", [ - (random_qiskit_circuit(2, 5), Language.QISKIT, None), - (random_qiskit_circuit(5, 5), Language.QISKIT, None), - (random_qiskit_circuit(10, 5), Language.QISKIT, None), - ( - _create_large_circuits_for_tests()[0], - Language.QISKIT, - None, - ), - ( - _create_large_circuits_for_tests()[1], - Language.QISKIT, - "\"If\" instructions aren't handled", - ), - (QCircuit([H(0), CNOT(0, 1)]), Language.QASM2, None), - (random_circuit(None, 2), Language.QASM2, None), - (random_circuit(None, 10), Language.QASM2, None), ( "OPENQASM 2.0;\nqreg q[2];\nh q[0];\ncx q[0],q[1];", - Language.QASM2, QCircuit([H(0), CNOT(0, 1)]), ), ( "// Generated from Cirq v1.3.0\n\nOPENQASM 2.0;\n\n// Qubits: [q0, q1]\nqreg q[2];\nh q[0];\ncx q[0],q[1];", - Language.QASM2, QCircuit([H(0), CNOT(0, 1)]), ), ( @@ -675,7 +686,6 @@ def _create_large_circuits_for_tests() -> tuple[QiskitCircuit, QiskitCircuit]: h q[0]; id q[2]; id q[3];""", - Language.QASM2, QCircuit( [ Z(0), @@ -714,71 +724,72 @@ def _create_large_circuits_for_tests() -> tuple[QiskitCircuit, QiskitCircuit]: ] ), ), - (random_cirq_circuit(2, 5, 0.5), Language.CIRQ, None), - (random_cirq_circuit(10, 5, 0.5), Language.CIRQ, None), - (QCircuit([H(0), CNOT(0, 1)]), Language.BRAKET, None), - (random_circuit(None, 2), Language.BRAKET, None), - (random_circuit(None, 10), Language.BRAKET, None), - (QCircuit([H(0), CNOT(0, 1)]), Language.MY_QLM, None), - (random_circuit(None, 2), Language.MY_QLM, None), - (random_circuit(None, 10), Language.MY_QLM, None), ( "OPENQASM 3.0;include \"stdgates.inc\";qubit[2] q;h q[0];cx q[0], q[1];", - Language.QASM3, QCircuit([H(0), CNOT(0, 1)]), ), ( "//Generated with Qiskit\n\nOPENQASM 3.0;include \"stdgates.inc\";\n//Qubits\nqubit[2] q;h q[0];cx q[0], q[1];", - Language.QASM3, QCircuit([H(0), CNOT(0, 1)]), ), ], ) -def test_from_other_language( - circuit: QiskitCircuit | QCircuit | cirq_Circuit | str, - language: Language, - expected_output: Optional[str | QCircuit], -): - if isinstance(circuit, QiskitCircuit): - from qiskit.quantum_info import Operator - - if not isinstance(expected_output, str): - qcircuit = QCircuit.from_other_language(circuit) - matrix = Operator(circuit.reverse_bits()).data - if TYPE_CHECKING: - assert isinstance(matrix, np.ndarray) - assert matrix_eq(matrix, qcircuit.to_matrix()) - else: - with pytest.raises(ValueError, match=expected_output): - QCircuit.from_other_language(circuit) +def test_from_qasm(circuit: str, expected_output: QCircuit): + qcircuit = QCircuit.from_other_language(circuit) + assert matrix_eq(qcircuit.to_matrix(), expected_output.to_matrix()) - elif isinstance(circuit, cirq_Circuit): - from cirq.protocols.unitary_protocol import unitary +@pytest.fixture +def list_random_cirq_circuit() -> list["cirq_Circuit"]: + from cirq.testing.random_circuit import random_circuit as random_cirq_circuit + + return [ + random_cirq_circuit(2, 5, 0.5), + random_cirq_circuit(10, 5, 0.5), + ] + + +@pytest.mark.provider("cirq") +def test_from_cirq(list_random_cirq_circuit: list[cirq_Circuit]): + from cirq.protocols.unitary_protocol import unitary + + for circuit in list_random_cirq_circuit: qcircuit = QCircuit.from_other_language(circuit) - cirq_circuit = qcircuit.to_other_language(language) + cirq_circuit = qcircuit.to_other_language(Language.CIRQ) assert matrix_eq(unitary(cirq_circuit), unitary(circuit)) - elif language == Language.QASM3: - qcircuit = QCircuit.from_other_language(circuit) - if TYPE_CHECKING: - assert isinstance(expected_output, QCircuit) - assert matrix_eq(qcircuit.to_matrix(), expected_output.to_matrix()) - elif isinstance(circuit, str): - qcircuit = QCircuit.from_other_language(circuit) - if TYPE_CHECKING: - assert isinstance(expected_output, QCircuit) - assert matrix_eq(qcircuit.to_matrix(), expected_output.to_matrix()) +@pytest.mark.provider("braket") +@pytest.mark.parametrize( + "circuit", + [ + QCircuit([H(0), CNOT(0, 1)]), + random_circuit(None, 2), + random_circuit(None, 10), + ], +) +def test_from_braket(circuit: QCircuit): + circ_to_test = circuit.to_other_language(Language.BRAKET) + qcircuit = QCircuit.from_other_language(circ_to_test) + assert matrix_eq(qcircuit.to_matrix(), circuit.to_matrix()) - else: - circ_to_test = circuit.to_other_language(language) - if TYPE_CHECKING: - assert isinstance(circ_to_test, (BraketCircuit, str)) - qcircuit = QCircuit.from_other_language(circ_to_test) - assert matrix_eq(qcircuit.to_matrix(), circuit.to_matrix()) + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize( + "circuit", + [ + QCircuit([H(0), CNOT(0, 1)]), + random_circuit(None, 2), + random_circuit(None, 10), + ], +) +def test_from_myqlm(circuit: QCircuit): + circ_to_test = circuit.to_other_language(Language.MY_QLM) + qcircuit = QCircuit.from_other_language(circ_to_test) + assert matrix_eq(qcircuit.to_matrix(), circuit.to_matrix()) +@pytest.mark.provider("qiskit") def test_from_other_language_qiskit_circuits( list_qiskit_funky_circuits: list[QiskitCircuit], ): @@ -786,8 +797,9 @@ def test_from_other_language_qiskit_circuits( QCircuit.from_other_language(qiskit_circuit) +@pytest.mark.provider("braket") def test_from_other_language_braket_circuits( - list_braket_funky_circuits: list[BraketCircuit], + list_braket_funky_circuits: list["BraketCircuit"], ): for i in range(len(list_braket_funky_circuits)): if i == 0: @@ -806,6 +818,7 @@ def test_from_other_language_braket_circuits( QCircuit.from_other_language(list_braket_funky_circuits[i]) +@pytest.mark.provider("cirq") def test_from_other_language_cirq_circuits( list_cirq_funky_circuits: list[cirq_Circuit], ): @@ -813,6 +826,7 @@ def test_from_other_language_cirq_circuits( QCircuit.from_other_language(circ) +@pytest.mark.provider("myqlm") def test_from_other_language_myqlm_circuits( list_myqlm_funky_circuits: list[myQLM_Circuit], ): @@ -820,6 +834,7 @@ def test_from_other_language_myqlm_circuits( QCircuit.from_other_language(circ) +@pytest.mark.provider("braket") @pytest.mark.parametrize( "circuit, expected_str", [ @@ -835,7 +850,7 @@ def test_from_other_language_myqlm_circuits( ) ], ) -def test_from_other_language_noise(circuit: QCircuit, expected_str: str): +def test_from_other_language_noise_braket(circuit: QCircuit, expected_str: str): braket_circuit = circuit.to_other_language(Language.BRAKET) qc = QCircuit.from_other_language(braket_circuit) assert str(qc.noises) == expected_str @@ -896,20 +911,52 @@ def test_to_qasm_3(circuit: QCircuit, printed_result_filename: str): assert qasm3.strip() == f.read().strip() +@pytest.mark.provider("qiskit") +@pytest.mark.parametrize( + "measure", + [BasisMeasure(), ExpectationMeasure(Observable(1 * pI @ pZ + 1 * pI @ pI))], +) +def test_measure_no_target_qiskit(measure: Measure): + exec_measure_no_target(measure, IBMDevice.AER_SIMULATOR) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize( + "measure", + [BasisMeasure(), ExpectationMeasure(Observable(1 * pI @ pZ + 1 * pI @ pI))], +) +def test_measure_no_target_braket(measure: Measure): + exec_measure_no_target(measure, AWSDevice.BRAKET_LOCAL_SIMULATOR) + + +@pytest.mark.provider("cirq") @pytest.mark.parametrize( "measure", [BasisMeasure(), ExpectationMeasure(Observable(1 * pI @ pZ + 1 * pI @ pI))], ) -def test_measure_no_target(measure: Measure): +def test_measure_no_target_cirq(measure: Measure): + exec_measure_no_target(measure, GOOGLEDevice.CIRQ_LOCAL_SIMULATOR) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize( + "measure", + [BasisMeasure(), ExpectationMeasure(Observable(1 * pI @ pZ + 1 * pI @ pI))], +) +def test_measure_no_target_myqlm(measure: Measure): + exec_measure_no_target(measure, ATOSDevice.MYQLM_CLINALG) + + +def exec_measure_no_target(measure: Measure, device: AvailableDevice): circuit = QCircuit(2) circuit.add(H(0)) circuit.add(CNOT(0, 1)) circuit.add(measure) if isinstance(measure, ExpectationMeasure): - isinstance(run(circuit, ATOSDevice.MYQLM_PYLINALG).expectation_values, float) + isinstance(run(circuit, device).expectation_values, float) else: - assert run(circuit, ATOSDevice.MYQLM_PYLINALG).job.measure.nb_qubits == circuit.nb_qubits # type: ignore[AttributeAccessIssue] + assert run(circuit, device).job.measure.nb_qubits == circuit.nb_qubits # type: ignore[AttributeAccessIssue] @pytest.mark.parametrize( diff --git a/tests/examples/test_demonstrations.py b/tests/examples/test_demonstrations.py index 87aa3d83..4e7891cd 100644 --- a/tests/examples/test_demonstrations.py +++ b/tests/examples/test_demonstrations.py @@ -2,11 +2,11 @@ import numpy as np import pytest -from braket.devices import LocalSimulator from mpqp import ( ATOSDevice, AWSDevice, + GOOGLEDevice, BasisMeasure, ExpectationMeasure, IBMDevice, @@ -31,7 +31,41 @@ def warn_guard(device: AvailableDevice, run: Callable[[], Any]): return run() -def test_sample_demo(): +@pytest.mark.provider("qiskit") +def test_sample_demo_qiskit(): + exec_sample_demo( + [ + IBMDevice.AER_SIMULATOR, + IBMDevice.AER_SIMULATOR_MATRIX_PRODUCT_STATE, + # IBMDevice.AER_SIMULATOR_EXTENDED_STABILIZER, + IBMDevice.AER_SIMULATOR_STATEVECTOR, + # IBMDevice.AER_SIMULATOR_STABILIZER, + IBMDevice.AER_SIMULATOR_DENSITY_MATRIX, + ], + ) + + +@pytest.mark.provider("braket") +def test_sample_demo_braket(): + exec_sample_demo([AWSDevice.BRAKET_LOCAL_SIMULATOR]) + + +@pytest.mark.provider("myqlm") +def test_sample_demo_myqlm(): + exec_sample_demo( + [ + ATOSDevice.MYQLM_PYLINALG, + ATOSDevice.MYQLM_CLINALG, + ], + ) + + +@pytest.mark.provider("cirq") +def test_sample_demo_cirq(): + exec_sample_demo([GOOGLEDevice.CIRQ_LOCAL_SIMULATOR]) + + +def exec_sample_demo(devices: list[AvailableDevice]): # Declaration of the circuit with the right size circuit = QCircuit(4) @@ -55,23 +89,7 @@ def test_sample_demo(): # Add measurement circuit.add(BasisMeasure([0, 1, 2, 3], shots=2000)) - # Run the circuit on a selected device - runner = lambda: run( - circuit, - [ - IBMDevice.AER_SIMULATOR, - IBMDevice.AER_SIMULATOR_MATRIX_PRODUCT_STATE, - # IBMDevice.AER_SIMULATOR_EXTENDED_STABILIZER, - IBMDevice.AER_SIMULATOR_STATEVECTOR, - # IBMDevice.AER_SIMULATOR_STABILIZER, - IBMDevice.AER_SIMULATOR_DENSITY_MATRIX, - ATOSDevice.MYQLM_PYLINALG, - ATOSDevice.MYQLM_CLINALG, - AWSDevice.BRAKET_LOCAL_SIMULATOR, - ], - ) - - warn_guard(AWSDevice.BRAKET_LOCAL_SIMULATOR, runner) + run(circuit, devices) assert True @@ -107,7 +125,38 @@ def test_sample_demo_aer_stabilizers(): assert True -def test_statevector_demo(): +@pytest.mark.provider("qiskit") +def test_statevector_demo_qiskit(): + exec_statevector_demo( + [ + IBMDevice.AER_SIMULATOR_STATEVECTOR, + IBMDevice.AER_SIMULATOR, + IBMDevice.AER_SIMULATOR_MATRIX_PRODUCT_STATE, + ], + ) + + +@pytest.mark.provider("braket") +def test_statevector_demo_braket(): + exec_statevector_demo([AWSDevice.BRAKET_LOCAL_SIMULATOR]) + + +@pytest.mark.provider("myqlm") +def test_statevector_demo_myqlm(): + exec_statevector_demo( + [ + ATOSDevice.MYQLM_PYLINALG, + ATOSDevice.MYQLM_CLINALG, + ], + ) + + +@pytest.mark.provider("cirq") +def test_statevector_demo_cirq(): + exec_statevector_demo([GOOGLEDevice.CIRQ_LOCAL_SIMULATOR]) + + +def exec_statevector_demo(devices: list[AvailableDevice]): circuit = QCircuit( [ T(0), @@ -129,37 +178,13 @@ def test_statevector_demo(): ) # when no measure in the circuit, must run in statevector mode - runner = lambda: run( - circuit, - [ - IBMDevice.AER_SIMULATOR_STATEVECTOR, - IBMDevice.AER_SIMULATOR, - IBMDevice.AER_SIMULATOR_MATRIX_PRODUCT_STATE, - ATOSDevice.MYQLM_PYLINALG, - ATOSDevice.MYQLM_CLINALG, - AWSDevice.BRAKET_LOCAL_SIMULATOR, - ], - ) - - warn_guard(AWSDevice.BRAKET_LOCAL_SIMULATOR, runner) + run(circuit, devices) # same when we add a BasisMeasure with 0 shots circuit.add(BasisMeasure([0, 1, 2, 3], shots=0)) # Run the circuit on a selected device - runner = lambda: run( - circuit, - [ - IBMDevice.AER_SIMULATOR_STATEVECTOR, - IBMDevice.AER_SIMULATOR, - IBMDevice.AER_SIMULATOR_MATRIX_PRODUCT_STATE, - ATOSDevice.MYQLM_PYLINALG, - ATOSDevice.MYQLM_CLINALG, - AWSDevice.BRAKET_LOCAL_SIMULATOR, - ], - ) - - warn_guard(AWSDevice.BRAKET_LOCAL_SIMULATOR, runner) + run(circuit, devices) assert True @@ -186,8 +211,47 @@ def test_statevector_demo_stab(): assert True +@pytest.mark.provider("qiskit") +@pytest.mark.parametrize("shots", [0, 1000]) +def test_observable_demo_qiskit(shots: int): + exec_observable_demo( + shots, + [ + IBMDevice.AER_SIMULATOR, + IBMDevice.AER_SIMULATOR_STATEVECTOR, + # IBMDevice.AER_SIMULATOR_EXTENDED_STABILIZER, + # IBMDevice.AER_SIMULATOR_STABILIZER, + IBMDevice.AER_SIMULATOR_DENSITY_MATRIX, + IBMDevice.AER_SIMULATOR_MATRIX_PRODUCT_STATE, + ], + ) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize("shots", [0, 1000]) +def test_observable_demo_braket(shots: int): + exec_observable_demo(shots, [AWSDevice.BRAKET_LOCAL_SIMULATOR]) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize("shots", [0, 1000]) +def test_observable_demo_myqlm(shots: int): + exec_observable_demo( + shots, + [ + ATOSDevice.MYQLM_PYLINALG, + ATOSDevice.MYQLM_CLINALG, + ], + ) + + +@pytest.mark.provider("cirq") @pytest.mark.parametrize("shots", [0, 1000]) -def test_observable_demo(shots: int): +def test_observable_demo_cirq(shots: int): + exec_observable_demo(shots, [GOOGLEDevice.CIRQ_LOCAL_SIMULATOR]) + + +def exec_observable_demo(shots: int, devices: list[AvailableDevice]): obs = Observable( np.array( [ @@ -208,27 +272,15 @@ def test_observable_demo(shots: int): circuit.add(ExpectationMeasure(obs, shots=shots)) # Running the computation on myQLM and on Aer simulator, then retrieving the results - runner = lambda: run( - circuit, - [ - ATOSDevice.MYQLM_PYLINALG, - ATOSDevice.MYQLM_CLINALG, - AWSDevice.BRAKET_LOCAL_SIMULATOR, - IBMDevice.AER_SIMULATOR, - IBMDevice.AER_SIMULATOR_STATEVECTOR, - # IBMDevice.AER_SIMULATOR_EXTENDED_STABILIZER, - # IBMDevice.AER_SIMULATOR_STABILIZER, - IBMDevice.AER_SIMULATOR_DENSITY_MATRIX, - IBMDevice.AER_SIMULATOR_MATRIX_PRODUCT_STATE, - ], - ) - - warn_guard(AWSDevice.BRAKET_LOCAL_SIMULATOR, runner) + run(circuit, devices) assert True +@pytest.mark.provider("braket") def test_aws_qasm_executions(): + from braket.devices import LocalSimulator + device = LocalSimulator() qasm_str = """OPENQASM 3.0; @@ -245,6 +297,7 @@ def test_aws_qasm_executions(): device.run(circuit, shots=100).result() +@pytest.mark.provider("braket") def test_aws_mpqp_executions(): # Declaration of the circuit with the right size circuit = QCircuit(4) @@ -313,7 +366,27 @@ def test_aws_mpqp_executions(): warn_guard(AWSDevice.BRAKET_LOCAL_SIMULATOR, runner) -def test_all_native_gates(): +@pytest.mark.provider("qiskit") +def test_all_native_gates_qiskit(): + exec_all_native_gates(IBMDevice.AER_SIMULATOR_STATEVECTOR) + + +@pytest.mark.provider("cirq") +def test_all_native_gates_cirq(): + exec_all_native_gates(GOOGLEDevice.CIRQ_LOCAL_SIMULATOR) + + +@pytest.mark.provider("braket") +def test_all_native_gates_braket(): + exec_all_native_gates(AWSDevice.BRAKET_LOCAL_SIMULATOR) + + +@pytest.mark.provider("myqlm") +def test_all_native_gates_myqlm(): + exec_all_native_gates(ATOSDevice.MYQLM_PYLINALG) + + +def exec_all_native_gates(device: AvailableDevice): # Declaration of the circuit with the right size circuit = QCircuit(3, label="Test native gates") # Constructing the circuit by adding gates and measurements @@ -326,13 +399,7 @@ def test_all_native_gates(): circuit.to_other_language(Language.QASM2) circuit.to_other_language(Language.QASM3) - with pytest.warns(UnsupportedBraketFeaturesWarning): - run( - circuit, - [ - ATOSDevice.MYQLM_PYLINALG, - ATOSDevice.MYQLM_CLINALG, - IBMDevice.AER_SIMULATOR_STATEVECTOR, - AWSDevice.BRAKET_LOCAL_SIMULATOR, - ], - ) + run( + circuit, + device, + ) diff --git a/tests/execution/providers/test_aws.py b/tests/execution/providers/test_aws.py index 4760134a..d03b4e1d 100644 --- a/tests/execution/providers/test_aws.py +++ b/tests/execution/providers/test_aws.py @@ -5,6 +5,7 @@ from mpqp.tools.errors import UnsupportedBraketFeaturesWarning +@pytest.mark.provider("braket") @pytest.mark.parametrize( "circuit", [ diff --git a/tests/execution/providers/test_google.py b/tests/execution/providers/test_google.py index b986738d..3e031fb3 100644 --- a/tests/execution/providers/test_google.py +++ b/tests/execution/providers/test_google.py @@ -1,11 +1,6 @@ +from typing import TYPE_CHECKING import numpy as np import pytest -from cirq.circuits.circuit import Circuit -from cirq.ops.common_gates import CNOT as CirqCNOT -from cirq.ops.identity import I as CirqI -from cirq.ops.measure_util import measure -from cirq.ops.named_qubit import NamedQubit -from cirq.ops.pauli_gates import X as CirqX from mpqp import ( CNOT, @@ -25,7 +20,11 @@ ) from mpqp.qasm import qasm2_to_cirq_Circuit +if TYPE_CHECKING: + from cirq.circuits.circuit import Circuit + +@pytest.mark.provider("cirq") @pytest.mark.parametrize( "circuit", [ @@ -80,9 +79,16 @@ def test_running_local_cirq(circuit: QCircuit): run(circuit, GOOGLEDevice.CIRQ_LOCAL_SIMULATOR) -@pytest.mark.parametrize( - "circuit, qasm_filename", - [ +@pytest.fixture +def list_cirq() -> list[tuple["Circuit", str]]: + from cirq.circuits.circuit import Circuit + from cirq.ops.common_gates import CNOT as CirqCNOT + from cirq.ops.identity import I as CirqI + from cirq.ops.measure_util import measure + from cirq.ops.named_qubit import NamedQubit + from cirq.ops.pauli_gates import X as CirqX + + return [ ( Circuit( CirqI(NamedQubit("q_0")), @@ -94,13 +100,16 @@ def test_running_local_cirq(circuit: QCircuit): ), "all", ) - ], -) -def test_qasm2_to_cirq_Circuit(circuit: QCircuit, qasm_filename: str): + ] + + +@pytest.mark.provider("cirq") +def test_qasm2_to_cirq_Circuit(list_cirq: list[tuple[QCircuit, str]]): # 3M-TODO test everything - with open( - f"tests/core/test_circuit/{qasm_filename}.qasm2", - "r", - encoding="utf-8", - ) as f: - assert qasm2_to_cirq_Circuit(f.read()) == circuit + for circuit, qasm_filename in list_cirq: + with open( + f"tests/core/test_circuit/{qasm_filename}.qasm2", + "r", + encoding="utf-8", + ) as f: + assert qasm2_to_cirq_Circuit(f.read()) == circuit diff --git a/tests/execution/test_multi_observable.py b/tests/execution/test_multi_observable.py index 72f1b431..da129aa5 100644 --- a/tests/execution/test_multi_observable.py +++ b/tests/execution/test_multi_observable.py @@ -98,19 +98,58 @@ def pauliObservables(): ] -def optimized_devices(): +def optimized_devices_qiskit(): return [ IBMDevice.AER_SIMULATOR, + ] + + +def optimized_devices_cirq(): + return [ GOOGLEDevice.CIRQ_LOCAL_SIMULATOR, + ] + + +def optimized_devices_braket(): + return [ AWSDevice.BRAKET_LOCAL_SIMULATOR, ] +@pytest.mark.provider("qiskit") +@pytest.mark.parametrize( + "observable, device", + [(i, j) for i in pauliObservables() for j in optimized_devices_qiskit()], +) +def test_pauli_grouping_optimization_qiskit( + observable: list[Observable], device: AvailableDevice +): + exec_pauli_grouping_optimization(observable, device) + + +@pytest.mark.provider("cirq") @pytest.mark.parametrize( "observable, device", - [(i, j) for i in pauliObservables() for j in optimized_devices()], + [(i, j) for i in pauliObservables() for j in optimized_devices_cirq()], ) -def test_pauli_grouping_optimization( +def test_pauli_grouping_optimization_cirq( + observable: list[Observable], device: AvailableDevice +): + exec_pauli_grouping_optimization(observable, device) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize( + "observable, device", + [(i, j) for i in pauliObservables() for j in optimized_devices_braket()], +) +def test_pauli_grouping_optimization_braket( + observable: list[Observable], device: AvailableDevice +): + exec_pauli_grouping_optimization(observable, device) + + +def exec_pauli_grouping_optimization( observable: list[Observable], device: AvailableDevice ): from mpqp.execution import Result, run @@ -155,33 +194,74 @@ def test_pauli_grouping_optimization( assert True -@pytest.mark.parametrize( - "expectation_value,circuit,observable", - [ - [ +@pytest.fixture +def list_expect_v_circ_obs() -> list[tuple[float, QCircuit, Observable]]: + return [ + ( -0.2279775, QCircuit([Rx(0.23, 0), Rz(24.23, 1), CNOT(0, 1)]), Observable(pX @ pY + pZ @ pX), - ], - [ - 0, + ), + ( + 0.0, QCircuit([Rx(0.23, 0), Rz(24.23, 1), CNOT(0, 1)]), Observable(pI @ pX + pY @ pZ), - ], - ], -) -def test_expectation_value_all_devices( - expectation_value: float, circuit: QCircuit, observable: Observable -): - devices = [ - IBMDevice.AER_SIMULATOR, - AWSDevice.BRAKET_LOCAL_SIMULATOR, - ATOSDevice.MYQLM_PYLINALG, - GOOGLEDevice.CIRQ_LOCAL_SIMULATOR, + ), ] + + +@pytest.mark.provider("qiskit") +def test_expectation_value_all_devices_qiskit( + list_expect_v_circ_obs: list[tuple[float, QCircuit, Observable]], +): + for expectation_value, circuit, observable in list_expect_v_circ_obs: + exec_expectation_value_all_devices( + expectation_value, circuit, observable, IBMDevice.AER_SIMULATOR + ) + + +@pytest.mark.provider("braket") +def test_expectation_value_all_devices_braket( + list_expect_v_circ_obs: list[tuple[float, QCircuit, Observable]], +): + for expectation_value, circuit, observable in list_expect_v_circ_obs: + exec_expectation_value_all_devices( + expectation_value, + circuit, + observable, + AWSDevice.BRAKET_LOCAL_SIMULATOR, + ) + + +@pytest.mark.provider("cirq") +def test_expectation_value_all_devices_cirq( + list_expect_v_circ_obs: list[tuple[float, QCircuit, Observable]], +): + for expectation_value, circuit, observable in list_expect_v_circ_obs: + exec_expectation_value_all_devices( + expectation_value, circuit, observable, GOOGLEDevice.CIRQ_LOCAL_SIMULATOR + ) + + +@pytest.mark.provider("myqlm") +def test_expectation_value_all_devices_myqlm( + list_expect_v_circ_obs: list[tuple[float, QCircuit, Observable]], +): + for expectation_value, circuit, observable in list_expect_v_circ_obs: + exec_expectation_value_all_devices( + expectation_value, circuit, observable, ATOSDevice.MYQLM_PYLINALG + ) + + +def exec_expectation_value_all_devices( + expectation_value: float, + circuit: QCircuit, + observable: Observable, + device: AvailableDevice, +): circuit.add(ExpectationMeasure(observable, shots=0, optimize_measurement=True)) - assert all( + assert ( round( # pyright: ignore[reportCallIssue] run( circuit, device @@ -189,5 +269,4 @@ def test_expectation_value_all_devices( 7, ) == expectation_value - for device in devices ) diff --git a/tests/execution/test_result.py b/tests/execution/test_result.py index f6020b54..fc3b4c73 100644 --- a/tests/execution/test_result.py +++ b/tests/execution/test_result.py @@ -9,7 +9,6 @@ ATOSDevice, AWSDevice, BasisMeasure, - BatchResult, ExpectationMeasure, GOOGLEDevice, H, @@ -24,6 +23,7 @@ StateVector, run, ) +from mpqp.execution.devices import AvailableDevice @pytest.mark.parametrize( @@ -126,33 +126,119 @@ def test_result_str(result: Result, expected_string: str): assert str(result) == expected_string -state_vector_devices = [ - IBMDevice.AER_SIMULATOR_STATEVECTOR, - GOOGLEDevice.CIRQ_LOCAL_SIMULATOR, - ATOSDevice.MYQLM_CLINALG, - ATOSDevice.MYQLM_PYLINALG, - AWSDevice.BRAKET_LOCAL_SIMULATOR, +state_vector_devices_qiskit: list[AvailableDevice] = [ + device + for device_family in [IBMDevice] + for device in device_family + if not device.is_remote() and device.supports_state_vector() ] -sampling_devices = [ - GOOGLEDevice.CIRQ_LOCAL_SIMULATOR, - IBMDevice.AER_SIMULATOR, - ATOSDevice.MYQLM_CLINALG, - ATOSDevice.MYQLM_PYLINALG, - AWSDevice.BRAKET_LOCAL_SIMULATOR, +state_vector_devices_cirq: list[AvailableDevice] = [ + device + for device_family in [GOOGLEDevice] + for device in device_family + if not device.is_remote() and device.supports_state_vector() ] +state_vector_devices_braket: list[AvailableDevice] = [ + device + for device_family in [AWSDevice] + for device in device_family + if not device.is_remote() and device.supports_state_vector() +] + +state_vector_devices_myqlm: list[AvailableDevice] = [ + device + for device_family in [ATOSDevice] + for device in device_family + if not device.is_remote() and device.supports_state_vector() +] + +sampling_devices_qiskit: list[AvailableDevice] = [ + device + for device_family in [IBMDevice] + for device in device_family + if not device.is_remote() and device.supports_samples() +] + +sampling_devices_cirq: list[AvailableDevice] = [ + device + for device_family in [GOOGLEDevice] + for device in device_family + if not device.is_remote() and device.supports_samples() +] + +sampling_devices_braket: list[AvailableDevice] = [ + device + for device_family in [AWSDevice] + for device in device_family + if not device.is_remote() and device.supports_samples() +] + +sampling_devices_myqlm: list[AvailableDevice] = [ + device + for device_family in [ATOSDevice] + for device in device_family + if not device.is_remote() and device.supports_samples() +] + + +@pytest.mark.provider("qiskit") +@pytest.mark.parametrize("device", sampling_devices_qiskit) +def test_sample_nb_shot_handle_qiskit(device: AvailableDevice): + exec_sample_nb_shot_handle(device) + -def test_sample_nb_shot_handle(): +@pytest.mark.provider("braket") +@pytest.mark.parametrize("device", sampling_devices_braket) +def test_sample_nb_shot_handle_braket(device: AvailableDevice): + exec_sample_nb_shot_handle(device) + + +@pytest.mark.provider("cirq") +@pytest.mark.parametrize("device", sampling_devices_cirq) +def test_sample_nb_shot_handle_cirq(device: AvailableDevice): + exec_sample_nb_shot_handle(device) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize("device", sampling_devices_myqlm) +def test_sample_nb_shot_handle_myqlm(device: AvailableDevice): + exec_sample_nb_shot_handle(device) + + +def exec_sample_nb_shot_handle(device: AvailableDevice): circuit = QCircuit([H(0), CNOT(0, 1), BasisMeasure(shots=1024)]) - batch = run(circuit, sampling_devices) - assert isinstance(batch, BatchResult) - for result in batch: - assert result.error != 0.0 - assert result.shots == 1024 + result = run(circuit, device) + assert result.error != 0.0 + assert result.shots == 1024 + + +@pytest.mark.provider("qiskit") +@pytest.mark.parametrize("device", state_vector_devices_qiskit) +def test_state_vector_nb_shot_handle_qiskit(device: AvailableDevice): + exec_state_vector_nb_shot_handle(device) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize("device", state_vector_devices_braket) +def test_state_vector_nb_shot_handle_braket(device: AvailableDevice): + exec_state_vector_nb_shot_handle(device) + + +@pytest.mark.provider("cirq") +@pytest.mark.parametrize("device", state_vector_devices_cirq) +def test_state_vector_nb_shot_handle_cirq(device: AvailableDevice): + exec_state_vector_nb_shot_handle(device) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize("device", state_vector_devices_myqlm) +def test_state_vector_nb_shot_handle_myqlm(device: AvailableDevice): + exec_state_vector_nb_shot_handle(device) -def test_state_vector_nb_shot_handle(): +def exec_state_vector_nb_shot_handle(device: AvailableDevice): circuit = QCircuit( [ H(0), @@ -172,8 +258,6 @@ def test_state_vector_nb_shot_handle(): ), ] ) - batch = run(circuit, sampling_devices) - assert isinstance(batch, BatchResult) - for result in batch: - assert result.error != 0.0 - assert result.shots == 1024 + result = run(circuit, device) + assert result.error != 0.0 + assert result.shots == 1024 diff --git a/tests/execution/test_simulated_devices.py b/tests/execution/test_simulated_devices.py index 2e55f2a8..22e3ea61 100644 --- a/tests/execution/test_simulated_devices.py +++ b/tests/execution/test_simulated_devices.py @@ -43,41 +43,49 @@ def ibm_simulated_devices(): return list(IBMSimulatedDevice) +@pytest.mark.provider("qiskit") def test_generation_enum(): assert len(ibm_simulated_devices()) > 0 -@pytest.mark.parametrize( - "circuit, device", [(i, j) for i in circuits() for j in ibm_simulated_devices()] -) +@pytest.fixture +def list_ibm_simulated_device() -> list[tuple[QCircuit, StaticIBMSimulatedDevice]]: + return [(i, j) for i in circuits() for j in ibm_simulated_devices()] + + +@pytest.mark.provider("qiskit") def running_sample_job_ibm_simulated_devices( - circuit: QCircuit, device: StaticIBMSimulatedDevice + list_ibm_simulated_device: list[tuple[QCircuit, StaticIBMSimulatedDevice]], ): - c = circuit + QCircuit([BasisMeasure()], nb_qubits=circuit.nb_qubits) - if device.value().num_qubits < c.nb_qubits: - with pytest.raises(DeviceJobIncompatibleError): + for circuit, device in list_ibm_simulated_device: + c = circuit + QCircuit([BasisMeasure()], nb_qubits=circuit.nb_qubits) + if device.value().num_qubits < c.nb_qubits: + with pytest.raises(DeviceJobIncompatibleError): + run(c, device) + else: run(c, device) - else: - run(c, device) - assert True + assert True -@pytest.mark.parametrize( - "circuit, device", [(i, j) for i in circuits() for j in ibm_simulated_devices()] -) +@pytest.mark.provider("qiskit") def running_observable_job_ibm_simulated_devices( - circuit: QCircuit, device: StaticIBMSimulatedDevice + list_ibm_simulated_device: list[tuple[QCircuit, StaticIBMSimulatedDevice]], ): - c = circuit + QCircuit( - [ExpectationMeasure(Observable(rand_hermitian_matrix(2**circuit.nb_qubits)))], - nb_qubits=circuit.nb_qubits, - ) - if device.value().num_qubits < c.nb_qubits: - with pytest.raises(DeviceJobIncompatibleError): + for circuit, device in list_ibm_simulated_device: + c = circuit + QCircuit( + [ + ExpectationMeasure( + Observable(rand_hermitian_matrix(2**circuit.nb_qubits)) + ) + ], + nb_qubits=circuit.nb_qubits, + ) + if device.value().num_qubits < c.nb_qubits: + with pytest.raises(DeviceJobIncompatibleError): + run(c, device) + else: run(c, device) - else: - run(c, device) - assert True + assert True if "--long-local" in sys.argv or "--long" in sys.argv: diff --git a/tests/execution/test_validity.py b/tests/execution/test_validity.py index cf3593c3..8cff26d7 100644 --- a/tests/execution/test_validity.py +++ b/tests/execution/test_validity.py @@ -50,27 +50,55 @@ s = np.sqrt e = np.exp -all_devices = [ + +state_vector_devices_qiskit: list[AvailableDevice] = [ + device + for device in IBMDevice + if not device.is_remote() and device.supports_state_vector() +] + +state_vector_devices_cirq: list[AvailableDevice] = [ + device + for device in GOOGLEDevice + if not device.is_remote() and device.supports_state_vector() +] + +state_vector_devices_braket: list[AvailableDevice] = [ device - for device_family in [IBMDevice, GOOGLEDevice, AWSDevice, ATOSDevice, AZUREDevice] - for device in device_family + for device in AWSDevice + if not device.is_remote() and device.supports_state_vector() ] -# TODO: build the lists bellow from the list above + filtering - -state_vector_devices = [ - IBMDevice.AER_SIMULATOR_STATEVECTOR, - GOOGLEDevice.CIRQ_LOCAL_SIMULATOR, - ATOSDevice.MYQLM_CLINALG, - ATOSDevice.MYQLM_PYLINALG, - AWSDevice.BRAKET_LOCAL_SIMULATOR, + +state_vector_devices_myqlm: list[AvailableDevice] = [ + device + for device in ATOSDevice + if not device.is_remote() and device.supports_state_vector() ] -sampling_devices = [ - GOOGLEDevice.CIRQ_LOCAL_SIMULATOR, - IBMDevice.AER_SIMULATOR, - ATOSDevice.MYQLM_CLINALG, - ATOSDevice.MYQLM_PYLINALG, - AWSDevice.BRAKET_LOCAL_SIMULATOR, +sampling_devices_qiskit: list[AvailableDevice] = [ + device + for device in IBMDevice + if not device.is_remote() + and device.supports_samples() + and not device.has_reduced_gate_set() +] + +sampling_devices_cirq: list[AvailableDevice] = [ + device + for device in GOOGLEDevice + if not device.is_remote() and device.supports_samples() +] + +sampling_devices_braket: list[AvailableDevice] = [ + device + for device in AWSDevice + if not device.is_remote() and device.supports_samples() +] + +sampling_devices_myqlm: list[AvailableDevice] = [ + device + for device in ATOSDevice + if not device.is_remote() and device.supports_samples() ] @@ -93,120 +121,202 @@ def hae_3_qubit_circuit( ) -@pytest.mark.parametrize( - "parameters, expected_vector", - [ - ([0, 0, 0, 0, 0, 0], np.array([1, 0, 0, 0, 0, 0, 0, 0])), - ([pi / 2, 0, -pi, pi / 2, 0, 0], np.array([0, -0.5, 0, 0.5, -0.5, 0, -0.5, 0])), - ( - [pi / 2, pi, -pi / 2, pi / 5, 0, -pi], - np.array( - [ - np.sin(pi / 10), - np.sin(pi / 10), - np.cos(pi / 10), - np.cos(pi / 10), - np.sin(pi / 10), - np.sin(pi / 10), - -np.cos(pi / 10), - -np.cos(pi / 10), - ] - ) - * 0.5, - ), - ( - [0.34, 0.321, -0.7843, 1.2232, 4.2323, 6.66], - np.array( - [ - 0.3812531672, - -0.05833733076, - 0.1494487426, - -0.6633351291, - -0.5508843680, - 0.1989958354, - 0.1014433799, - 0.1884958074, - ] - ), +list_param_expect_vector = [ + ([0, 0, 0, 0, 0, 0], np.array([1, 0, 0, 0, 0, 0, 0, 0])), + ([pi / 2, 0, -pi, pi / 2, 0, 0], np.array([0, -0.5, 0, 0.5, -0.5, 0, -0.5, 0])), + ( + [pi / 2, pi, -pi / 2, pi / 5, 0, -pi], + np.array( + [ + np.sin(pi / 10), + np.sin(pi / 10), + np.cos(pi / 10), + np.cos(pi / 10), + np.sin(pi / 10), + np.sin(pi / 10), + -np.cos(pi / 10), + -np.cos(pi / 10), + ] + ) + * 0.5, + ), + ( + [0.34, 0.321, -0.7843, 1.2232, 4.2323, 6.66], + np.array( + [ + 0.3812531672, + -0.05833733076, + 0.1494487426, + -0.6633351291, + -0.5508843680, + 0.1989958354, + 0.1014433799, + 0.1884958074, + ] ), - ], -) -def test_state_vector_result_HEA_ansatz( + ), +] + + +@pytest.mark.provider("qiskit") +@pytest.mark.parametrize("parameters, expected_vector", list_param_expect_vector) +def test_state_vector_result_HEA_ansatz_qiskit( + parameters: list[float], expected_vector: npt.NDArray[np.complex128] +): + exec_state_vector_result_HEA_ansatz( + parameters, expected_vector, state_vector_devices_qiskit + ) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize("parameters, expected_vector", list_param_expect_vector) +def test_state_vector_result_HEA_ansatz_myqlm( parameters: list[float], expected_vector: npt.NDArray[np.complex128] ): - with pytest.warns(UnsupportedBraketFeaturesWarning): - batch = run(hae_3_qubit_circuit(*parameters), state_vector_devices) + exec_state_vector_result_HEA_ansatz( + parameters, expected_vector, state_vector_devices_myqlm + ) + + +@pytest.mark.provider("cirq") +@pytest.mark.parametrize("parameters, expected_vector", list_param_expect_vector) +def test_state_vector_result_HEA_ansatz_cirq( + parameters: list[float], expected_vector: npt.NDArray[np.complex128] +): + exec_state_vector_result_HEA_ansatz( + parameters, expected_vector, state_vector_devices_cirq + ) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize("parameters, expected_vector", list_param_expect_vector) +def test_state_vector_result_HEA_ansatz_braket( + parameters: list[float], expected_vector: npt.NDArray[np.complex128] +): + exec_state_vector_result_HEA_ansatz( + parameters, expected_vector, state_vector_devices_braket + ) + + +def exec_state_vector_result_HEA_ansatz( + parameters: list[float], + expected_vector: npt.NDArray[np.complex128], + state_vector_devices: list[AvailableDevice], +): + batch = run(hae_3_qubit_circuit(*parameters), state_vector_devices) assert isinstance(batch, BatchResult) for result in batch: assert isinstance(result, Result) assert matrix_eq(result.amplitudes, expected_vector) -@pytest.mark.parametrize( - "gates, expected_vector", - [ - ( - [H(0), H(1), H(2), CNOT(0, 1), P(pi / 3, 2), T(0), CZ(1, 2)], - np.array( - [ - s(2) / 4, - e(1j * pi / 3) * s(2) / 4, - s(2) / 4, - -e(1j * pi / 3) * s(2) / 4, - (s(2) / 2 + 1j * s(2) / 2) * s(2) / 4, - (s(2) / 2 + 1j * s(2) / 2) * e(1j * pi / 3) * s(2) / 4, - (s(2) / 2 + 1j * s(2) / 2) * s(2) / 4, - (-s(2) / 2 - 1j * s(2) / 2) * e(1j * pi / 3) * s(2) / 4, - ] - ), +list_gates_expect_vector = [ + ( + [H(0), H(1), H(2), CNOT(0, 1), P(pi / 3, 2), T(0), CZ(1, 2)], + np.array( + [ + s(2) / 4, + e(1j * pi / 3) * s(2) / 4, + s(2) / 4, + -e(1j * pi / 3) * s(2) / 4, + (s(2) / 2 + 1j * s(2) / 2) * s(2) / 4, + (s(2) / 2 + 1j * s(2) / 2) * e(1j * pi / 3) * s(2) / 4, + (s(2) / 2 + 1j * s(2) / 2) * s(2) / 4, + (-s(2) / 2 - 1j * s(2) / 2) * e(1j * pi / 3) * s(2) / 4, + ] ), - ( - [H(0), Rk(4, 1), H(2), Rx(pi / 3, 0), CRk(6, 1, 2), CNOT(0, 1), X(2)], - np.array( - [ - s(3) / 4 - 1j / 4, - s(3) / 4 - 1j / 4, - 0, - 0, - 0, - 0, - s(3) / 4 - 1j / 4, - s(3) / 4 - 1j / 4, - ] - ), + ), + ( + [H(0), Rk(4, 1), H(2), Rx(pi / 3, 0), CRk(6, 1, 2), CNOT(0, 1), X(2)], + np.array( + [ + s(3) / 4 - 1j / 4, + s(3) / 4 - 1j / 4, + 0, + 0, + 0, + 0, + s(3) / 4 - 1j / 4, + s(3) / 4 - 1j / 4, + ] ), - ( + ), + ( + [ + H(0), + H(1), + H(2), + SWAP(0, 1), + Rz(pi / 7, 2), + Z(0), + Y(1), + S(2), + Id(0), + U(pi / 2, -pi, pi / 3, 1), + Ry(pi / 5, 2), + ], + np.array( [ - H(0), - H(1), - H(2), - SWAP(0, 1), - Rz(pi / 7, 2), - Z(0), - Y(1), - S(2), - Id(0), - U(pi / 2, -pi, pi / 3, 1), - Ry(pi / 5, 2), - ], - np.array( - [ - 0.2329753102e-1 - 0.4845363113 * 1j, - 0.3413257772 + 0.1522448750 * 1j, - 0.2797471698 + 0.1345083586e-1 * 1j, - -0.878986196e-1 + 0.1970645294 * 1j, - -0.2329753102e-1 + 0.4845363113 * 1j, - -0.3413257772 - 0.1522448750 * 1j, - -0.2797471698 - 0.1345083586e-1 * 1j, - 0.878986196e-1 - 0.1970645294 * 1j, - ] - ), + 0.2329753102e-1 - 0.4845363113 * 1j, + 0.3413257772 + 0.1522448750 * 1j, + 0.2797471698 + 0.1345083586e-1 * 1j, + -0.878986196e-1 + 0.1970645294 * 1j, + -0.2329753102e-1 + 0.4845363113 * 1j, + -0.3413257772 - 0.1522448750 * 1j, + -0.2797471698 - 0.1345083586e-1 * 1j, + 0.878986196e-1 - 0.1970645294 * 1j, + ] ), - ], -) -def test_state_vector_various_native_gates(gates: list[Gate], expected_vector: Matrix): - with pytest.warns(UnsupportedBraketFeaturesWarning): - batch = run(QCircuit(gates), state_vector_devices) + ), +] + + +@pytest.mark.provider("qiskit") +@pytest.mark.parametrize("gates, expected_vector", list_gates_expect_vector) +def test_state_vector_various_native_gates_qiskit( + gates: list[Gate], expected_vector: Matrix +): + exec_state_vector_various_native_gates( + gates, expected_vector, state_vector_devices_qiskit + ) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize("gates, expected_vector", list_gates_expect_vector) +def test_state_vector_various_native_gates_braket( + gates: list[Gate], expected_vector: Matrix +): + exec_state_vector_various_native_gates( + gates, expected_vector, state_vector_devices_braket + ) + + +@pytest.mark.provider("cirq") +@pytest.mark.parametrize("gates, expected_vector", list_gates_expect_vector) +def test_state_vector_various_native_gates_cirq( + gates: list[Gate], expected_vector: Matrix +): + exec_state_vector_various_native_gates( + gates, expected_vector, state_vector_devices_cirq + ) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize("gates, expected_vector", list_gates_expect_vector) +def test_state_vector_various_native_gates_myqlm( + gates: list[Gate], expected_vector: Matrix +): + exec_state_vector_various_native_gates( + gates, expected_vector, state_vector_devices_myqlm + ) + + +def exec_state_vector_various_native_gates( + gates: list[Gate], + expected_vector: Matrix, + state_vector_devices: list[AvailableDevice], +): + batch = run(QCircuit(gates), state_vector_devices) assert isinstance(batch, BatchResult) for result in batch: assert isinstance(result, Result) @@ -217,46 +327,76 @@ def test_state_vector_various_native_gates(gates: list[Gate], expected_vector: M assert matrix_eq(result.amplitudes, expected_vector) -@pytest.mark.parametrize( - "gates, basis_states", - [ - ( - [ - H(0), - CNOT(0, 1), - CNOT(1, 2), - ], - ["000", "111"], - ), - ( - [H(0), H(2), CNOT(0, 1), Ry(1.87, 1), H(0), CNOT(2, 3), H(4)], - [ - "00000", - "00011", - "00110", - "00111", - "01000", - "01001", - "01110", - "01111", - "10000", - "10001", - "10110", - "10111", - "11000", - "11001", - "11110", - "11111", - ], - ), - ([X(0), SWAP(0, 1), X(2), Y(0), CNOT(1, 2), S(0), T(1), H(2)], ["110", "111"]), - ], -) -def test_sample_basis_state_in_samples(gates: list[Gate], basis_states: list[str]): +list_gates_basis_states = [ + ( + [ + H(0), + CNOT(0, 1), + CNOT(1, 2), + ], + ["000", "111"], + ), + ( + [H(0), H(2), CNOT(0, 1), Ry(1.87, 1), H(0), CNOT(2, 3), H(4)], + [ + "00000", + "00011", + "00110", + "00111", + "01000", + "01001", + "01110", + "01111", + "10000", + "10001", + "10110", + "10111", + "11000", + "11001", + "11110", + "11111", + ], + ), + ([X(0), SWAP(0, 1), X(2), Y(0), CNOT(1, 2), S(0), T(1), H(2)], ["110", "111"]), +] + + +@pytest.mark.provider("qiskit") +@pytest.mark.parametrize("gates, basis_states", list_gates_basis_states) +def test_sample_basis_state_in_samples_qiskit( + gates: list[Gate], basis_states: list[str] +): + exec_sample_basis_state_in_samples(gates, basis_states, sampling_devices_qiskit) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize("gates, basis_states", list_gates_basis_states) +def test_sample_basis_state_in_samples_braket( + gates: list[Gate], basis_states: list[str] +): + exec_sample_basis_state_in_samples(gates, basis_states, sampling_devices_braket) + + +@pytest.mark.provider("cirq") +@pytest.mark.parametrize("gates, basis_states", list_gates_basis_states) +def test_sample_basis_state_in_samples_cirq(gates: list[Gate], basis_states: list[str]): + exec_sample_basis_state_in_samples(gates, basis_states, sampling_devices_cirq) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize("gates, basis_states", list_gates_basis_states) +def test_sample_basis_state_in_samples_myqlm( + gates: list[Gate], basis_states: list[str] +): + exec_sample_basis_state_in_samples(gates, basis_states, sampling_devices_myqlm) + + +def exec_sample_basis_state_in_samples( + gates: list[Gate], basis_states: list[str], sampling_devices: list[AvailableDevice] +): c = QCircuit(gates) c.add(BasisMeasure(list(range(c.nb_qubits)), shots=10000)) - with pytest.warns(UnsupportedBraketFeaturesWarning): - batch = run(c, sampling_devices) + batch = run(c, sampling_devices) assert isinstance(batch, BatchResult) nb_states = len(basis_states) for result in batch: @@ -265,29 +405,75 @@ def test_sample_basis_state_in_samples(gates: list[Gate], basis_states: list[str assert len(result.samples) == nb_states -@pytest.mark.parametrize( - "instructions", - [ +list_instruction_proba = [ + ( [H(0), CNOT(0, 1), CNOT(1, 2)], - [CustomGate(np.array([[0, 1], [1, 0]]), [1])], + np.array([0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5]), + ), + ([CustomGate(np.array([[0, 1], [1, 0]]), [1])], np.array([0.0, 1.0, 0.0, 0.0])), + ( [U(0.215, 0.5588, 8, 1)], - ], -) -def test_sample_counts_in_trust_interval(instructions: list[Gate]): + np.array([0.9884881971034313, 0.011511802896568812, 0.0, 0.0]), + ), +] + + +@pytest.mark.provider("qiskit") +@pytest.mark.parametrize("instructions, probabilities", list_instruction_proba) +def test_sample_counts_in_trust_interval_qiskit( + instructions: list[Gate], probabilities: list[float] +): + exec_sample_counts_in_trust_interval( + instructions, probabilities, sampling_devices_qiskit + ) + + +@pytest.mark.provider("cirq") +@pytest.mark.parametrize("instructions, probabilities", list_instruction_proba) +def test_sample_counts_in_trust_interval_cirq( + instructions: list[Gate], probabilities: list[float] +): + exec_sample_counts_in_trust_interval( + instructions, probabilities, sampling_devices_cirq + ) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize("instructions, probabilities", list_instruction_proba) +def test_sample_counts_in_trust_interval_braket( + instructions: list[Gate], probabilities: list[float] +): + exec_sample_counts_in_trust_interval( + instructions, probabilities, sampling_devices_braket + ) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize("instructions, probabilities", list_instruction_proba) +def test_sample_counts_in_trust_interval_myqlm( + instructions: list[Gate], probabilities: list[float] +): + exec_sample_counts_in_trust_interval( + instructions, probabilities, sampling_devices_myqlm + ) + + +def exec_sample_counts_in_trust_interval( + instructions: list[Gate], + probabilities: list[float], + sampling_devices: list[AvailableDevice], +): c = QCircuit(instructions) shots = 50000 err_rate = 0.2 err_rate_percentage = 1 - np.power(1 - err_rate, (1 / 2)) - res = run(c, state_vector_devices[0]) - assert isinstance(res, Result) + res = run(c, IBMDevice.AER_SIMULATOR_STATEVECTOR) expected_counts = [int(count) for count in np.round(shots * res.probabilities)] c.add(BasisMeasure(list(range(c.nb_qubits)), shots=shots)) - with pytest.warns(UnsupportedBraketFeaturesWarning): - batch = run(c, sampling_devices) + batch = run(c, sampling_devices) assert isinstance(batch, BatchResult) for result in batch: assert isinstance(result, Result) - print(result) print("expected_counts: " + str(expected_counts)) counts = result.counts # check if the true value is inside the trust interval @@ -295,7 +481,6 @@ def test_sample_counts_in_trust_interval(instructions: list[Gate]): trust_interval = np.ceil( err_rate_percentage * expected_counts[i] + shots / 15 ) - print(trust_interval) assert ( np.floor(counts[i] - trust_interval) <= expected_counts[i] @@ -303,29 +488,71 @@ def test_sample_counts_in_trust_interval(instructions: list[Gate]): ) -@pytest.mark.parametrize( - "gates, observable, expected_vector", - [ - ( - [H(0), Rk(4, 1), H(2), Rx(pi / 3, 0), CRk(6, 1, 2), CNOT(0, 1), X(2)], - rand_hermitian_matrix(2**3), - np.array( - [ - s(3) / 4 - 1j / 4, - s(3) / 4 - 1j / 4, - 0, - 0, - 0, - 0, - s(3) / 4 - 1j / 4, - s(3) / 4 - 1j / 4, - ] - ), +list_gate_obs_vector = [ + ( + [H(0), Rk(4, 1), H(2), Rx(pi / 3, 0), CRk(6, 1, 2), CNOT(0, 1), X(2)], + rand_hermitian_matrix(2**3), + np.array( + [ + s(3) / 4 - 1j / 4, + s(3) / 4 - 1j / 4, + 0, + 0, + 0, + 0, + s(3) / 4 - 1j / 4, + s(3) / 4 - 1j / 4, + ] ), - ], -) -def test_observable_ideal_case( + ), +] + + +@pytest.mark.provider("qiskit") +@pytest.mark.parametrize("gates, observable, expected_vector", list_gate_obs_vector) +def test_observable_ideal_case_qiskit( gates: list[Gate], observable: npt.NDArray[np.complex128], expected_vector: Matrix +): + exec_observable_ideal_case( + gates, observable, expected_vector, state_vector_devices_qiskit + ) + + +@pytest.mark.provider("cirq") +@pytest.mark.parametrize("gates, observable, expected_vector", list_gate_obs_vector) +def test_observable_ideal_case_cirq( + gates: list[Gate], observable: npt.NDArray[np.complex128], expected_vector: Matrix +): + exec_observable_ideal_case( + gates, observable, expected_vector, state_vector_devices_cirq + ) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize("gates, observable, expected_vector", list_gate_obs_vector) +def test_observable_ideal_case_braket( + gates: list[Gate], observable: npt.NDArray[np.complex128], expected_vector: Matrix +): + exec_observable_ideal_case( + gates, observable, expected_vector, state_vector_devices_braket + ) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize("gates, observable, expected_vector", list_gate_obs_vector) +def test_observable_ideal_case_myqlm( + gates: list[Gate], observable: npt.NDArray[np.complex128], expected_vector: Matrix +): + exec_observable_ideal_case( + gates, observable, expected_vector, state_vector_devices_myqlm + ) + + +def exec_observable_ideal_case( + gates: list[Gate], + observable: npt.NDArray[np.complex128], + expected_vector: Matrix, + sampling_devices: list[AvailableDevice], ): c = QCircuit(gates) c.add( @@ -334,10 +561,12 @@ def test_observable_ideal_case( ) ) expected_value = float( - expected_vector.transpose().conjugate().dot(observable.dot(expected_vector)) + expected_vector.transpose() + .conjugate() + .dot(observable.dot(expected_vector)) + .real ) - with pytest.warns(UnsupportedBraketFeaturesWarning): - batch = run(c, sampling_devices) + batch = run(c, sampling_devices) assert isinstance(batch, BatchResult) for result in batch: evs = result.expectation_values @@ -367,15 +596,50 @@ def circuits_type(): ] +@pytest.mark.provider("qiskit") +@pytest.mark.parametrize("device", list(IBMDevice)) +def test_validity_run_job_type_qiskit( + device: AvailableDevice, circuits_type: list[QCircuit] +): + exec_validity_run_job_type(device, circuits_type) + + +@pytest.mark.provider("cirq") +@pytest.mark.parametrize("device", list(GOOGLEDevice)) +def test_validity_run_job_type_cirq( + device: AvailableDevice, circuits_type: list[QCircuit] +): + exec_validity_run_job_type(device, circuits_type) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize("device", list(ATOSDevice)) +def test_validity_run_job_type_myqlm( + device: AvailableDevice, circuits_type: list[QCircuit] +): + exec_validity_run_job_type(device, circuits_type) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize("device", list(AWSDevice)) +def test_validity_run_job_type_braket( + device: AvailableDevice, circuits_type: list[QCircuit] +): + exec_validity_run_job_type(device, circuits_type) + + +@pytest.mark.provider("azure") @pytest.mark.parametrize( "device", - list(IBMDevice) - + list(ATOSDevice) - + list(AWSDevice) - + list(GOOGLEDevice) - + list(AZUREDevice), + list(AZUREDevice), ) -def test_validity_run_job_type(device: AvailableDevice, circuits_type: list[QCircuit]): +def test_validity_run_job_type_azure( + device: AvailableDevice, circuits_type: list[QCircuit] +): + exec_validity_run_job_type(device, circuits_type) + + +def exec_validity_run_job_type(device: AvailableDevice, circuits_type: list[QCircuit]): circuit_state_vector = circuits_type[0] circuit_samples = circuits_type[1] circuit_observable = circuits_type[2] @@ -452,8 +716,35 @@ def test_validity_run_job_type(device: AvailableDevice, circuits_type: list[QCir run(circuit_observable, device) -@pytest.mark.parametrize("language", list(Language)) -def test_validity_native_gate_to_other_language(language: Language): +@pytest.mark.provider("qiskit") +def test_validity_native_gate_to_other_language_qiskit(): + exec_validity_native_gate_to_other_language(Language.QISKIT) + + +@pytest.mark.provider("cirq") +def test_validity_native_gate_to_other_language_cirq(): + exec_validity_native_gate_to_other_language(Language.CIRQ) + + +@pytest.mark.provider("braket") +def test_validity_native_gate_to_other_language_braket(): + exec_validity_native_gate_to_other_language(Language.BRAKET) + + +@pytest.mark.provider("myqlm") +def test_validity_native_gate_to_other_language_myqlm(): + exec_validity_native_gate_to_other_language(Language.MY_QLM) + + +def test_validity_native_gate_to_other_language_qasm2(): + exec_validity_native_gate_to_other_language(Language.QASM2) + + +def test_validity_native_gate_to_other_language_qasm3(): + exec_validity_native_gate_to_other_language(Language.QASM3) + + +def exec_validity_native_gate_to_other_language(language: Language): for gate in NATIVE_GATES: gate_build = random_gate([gate]) @@ -479,8 +770,35 @@ def measures(): ] -@pytest.mark.parametrize("language", list(Language)) -def test_validity_measure_to_other_language( +@pytest.mark.provider("qiskit") +def test_validity_measure_to_other_language_qiskit(measures: list[Measure]): + exec_validity_measure_to_other_language(Language.QISKIT, measures) + + +@pytest.mark.provider("cirq") +def test_validity_measure_to_other_language_cirq(measures: list[Measure]): + exec_validity_measure_to_other_language(Language.CIRQ, measures) + + +@pytest.mark.provider("braket") +def test_validity_measure_to_other_language_braket(measures: list[Measure]): + exec_validity_measure_to_other_language(Language.BRAKET, measures) + + +@pytest.mark.provider("myqlm") +def test_validity_measure_to_other_language_myqlm(measures: list[Measure]): + exec_validity_measure_to_other_language(Language.MY_QLM, measures) + + +def test_validity_measure_to_other_language_qasm2(measures: list[Measure]): + exec_validity_measure_to_other_language(Language.QASM2, measures) + + +def test_validity_measure_to_other_language_qasm3(measures: list[Measure]): + exec_validity_measure_to_other_language(Language.QASM3, measures) + + +def exec_validity_measure_to_other_language( language: Language, measures: list[Measure] ): for measure in measures: @@ -503,8 +821,45 @@ def pauli_strings(): return [pI @ pX @ pY @ pZ, pX + pZ, pY] -@pytest.mark.parametrize("language", list(Language)) -def test_validity_pauli_string_to_other_language( +@pytest.mark.provider("qiskit") +def test_validity_pauli_string_to_other_language_qiskit( + pauli_strings: list[PauliString], +): + exec_validity_pauli_string_to_other_language(Language.QISKIT, pauli_strings) + + +@pytest.mark.provider("cirq") +def test_validity_pauli_string_to_other_language_cirq(pauli_strings: list[PauliString]): + exec_validity_pauli_string_to_other_language(Language.CIRQ, pauli_strings) + + +@pytest.mark.provider("braket") +def test_validity_pauli_string_to_other_language_braket( + pauli_strings: list[PauliString], +): + exec_validity_pauli_string_to_other_language(Language.BRAKET, pauli_strings) + + +@pytest.mark.provider("myqlm") +def test_validity_pauli_string_to_other_language_myqlm( + pauli_strings: list[PauliString], +): + exec_validity_pauli_string_to_other_language(Language.MY_QLM, pauli_strings) + + +def test_validity_pauli_string_to_other_language_qasm2( + pauli_strings: list[PauliString], +): + exec_validity_pauli_string_to_other_language(Language.QASM2, pauli_strings) + + +def test_validity_pauli_string_to_other_language_qasm3( + pauli_strings: list[PauliString], +): + exec_validity_pauli_string_to_other_language(Language.QASM3, pauli_strings) + + +def exec_validity_pauli_string_to_other_language( language: Language, pauli_strings: list[PauliString] ): @@ -516,8 +871,35 @@ def test_validity_pauli_string_to_other_language( assert pauli_string.to_other_language(language) is not None -@pytest.mark.parametrize("language", list(Language)) -def test_validity_noise_to_other_language(language: Language): +@pytest.mark.provider("qiskit") +def test_validity_noise_to_other_language_qiskit(): + exec_validity_noise_to_other_language(Language.QISKIT) + + +@pytest.mark.provider("cirq") +def test_validity_noise_to_other_language_cirq(): + exec_validity_noise_to_other_language(Language.CIRQ) + + +@pytest.mark.provider("braket") +def test_validity_noise_to_other_language_braket(): + exec_validity_noise_to_other_language(Language.BRAKET) + + +@pytest.mark.provider("myqlm") +def test_validity_noise_to_other_language_myqlm(): + exec_validity_noise_to_other_language(Language.MY_QLM) + + +def test_validity_noise_to_other_language_qasm2(): + exec_validity_noise_to_other_language(Language.QASM2) + + +def test_validity_noise_to_other_language_qasm3(): + exec_validity_noise_to_other_language(Language.QASM3) + + +def exec_validity_noise_to_other_language(language: Language): for noise in NOISE_MODELS: noise_build = random_noise([noise]) @@ -543,8 +925,45 @@ def other_instr(): ] -@pytest.mark.parametrize("language", list(Language)) -def test_validity_other_instr_to_other_language( +@pytest.mark.provider("qiskit") +def test_validity_other_instr_to_other_language_qiskit( + other_instr: list[Instruction], +): + exec_validity_other_instr_to_other_language(Language.QISKIT, other_instr) + + +@pytest.mark.provider("cirq") +def test_validity_other_instr_to_other_language_cirq(other_instr: list[Instruction]): + exec_validity_other_instr_to_other_language(Language.CIRQ, other_instr) + + +@pytest.mark.provider("braket") +def test_validity_other_instr_to_other_language_braket( + other_instr: list[Instruction], +): + exec_validity_other_instr_to_other_language(Language.BRAKET, other_instr) + + +@pytest.mark.provider("myqlm") +def test_validity_other_instr_to_other_language_myqlm( + other_instr: list[Instruction], +): + exec_validity_other_instr_to_other_language(Language.MY_QLM, other_instr) + + +def test_validity_other_instr_to_other_language_qasm2( + other_instr: list[Instruction], +): + exec_validity_other_instr_to_other_language(Language.QASM2, other_instr) + + +def test_validity_other_instr_to_other_language_qasm3( + other_instr: list[Instruction], +): + exec_validity_other_instr_to_other_language(Language.QASM3, other_instr) + + +def exec_validity_other_instr_to_other_language( language: Language, other_instr: list[Instruction] ): for instr in other_instr: @@ -563,16 +982,71 @@ def test_validity_other_instr_to_other_language( assert instr.to_other_language(language) is not None +list_circ_obs = [ + (QCircuit([H(0), H(1)]), Observable([1, 2, 5, 3])), + (QCircuit([S(0), T(1)]), Observable([-1, 4, 0, 1])), + (QCircuit([Rx(0.5, 0), Ry(0.6, 1)]), Observable([0, 0, -9, 7])), +] + + +@pytest.mark.provider("qiskit") @pytest.mark.parametrize( "circuit, observable", - [ - (QCircuit([H(0), H(1)]), Observable([1, 2, 5, 3])), - (QCircuit([S(0), T(1)]), Observable([-1, 4, 0, 1])), - (QCircuit([Rx(0.5, 0), Ry(0.6, 1)]), Observable([0, 0, -9, 7])), - ], + list_circ_obs, +) +def test_validity_optim_ideal_single_diag_obs_and_regular_run_qiskit( + circuit: QCircuit, observable: Observable +): + exec_validity_optim_ideal_single_diag_obs_and_regular_run( + circuit, + observable, + IBMDevice.AER_SIMULATOR, + ) + + +@pytest.mark.provider("cirq") +@pytest.mark.parametrize( + "circuit, observable", + list_circ_obs, +) +def test_validity_optim_ideal_single_diag_obs_and_regular_run_cirq( + circuit: QCircuit, observable: Observable +): + exec_validity_optim_ideal_single_diag_obs_and_regular_run( + circuit, observable, IBMDevice.AER_SIMULATOR + ) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize( + "circuit, observable", + list_circ_obs, ) -def test_validity_optim_ideal_single_diag_obs_and_regular_run( +def test_validity_optim_ideal_single_diag_obs_and_regular_run_braket( circuit: QCircuit, observable: Observable +): + exec_validity_optim_ideal_single_diag_obs_and_regular_run( + circuit, + observable, + AWSDevice.BRAKET_LOCAL_SIMULATOR, + ) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize( + "circuit, observable", + list_circ_obs, +) +def test_validity_optim_ideal_single_diag_obs_and_regular_run_myqlm( + circuit: QCircuit, observable: Observable +): + exec_validity_optim_ideal_single_diag_obs_and_regular_run( + circuit, observable, ATOSDevice.MYQLM_PYLINALG + ) + + +def exec_validity_optim_ideal_single_diag_obs_and_regular_run( + circuit: QCircuit, observable: Observable, device: AvailableDevice ): e1 = ExpectationMeasure( observable, shots=0, optim_diagonal=False, optimize_measurement=False @@ -582,30 +1056,11 @@ def test_validity_optim_ideal_single_diag_obs_and_regular_run( ) c1 = circuit + QCircuit([e1], nb_qubits=2) c2 = circuit + QCircuit([e2], nb_qubits=2) - br1 = run( - c1, - [ - IBMDevice.AER_SIMULATOR, - ATOSDevice.MYQLM_PYLINALG, - AWSDevice.BRAKET_LOCAL_SIMULATOR, - GOOGLEDevice.CIRQ_LOCAL_SIMULATOR, - ], - ) - br2 = run( - c2, - [ - IBMDevice.AER_SIMULATOR, - ATOSDevice.MYQLM_PYLINALG, - AWSDevice.BRAKET_LOCAL_SIMULATOR, - GOOGLEDevice.CIRQ_LOCAL_SIMULATOR, - ], - ) - assert isinstance(br1, BatchResult) - assert isinstance(br2, BatchResult) - for r1, r2 in zip(br1.results, br2.results): - assert isinstance(r1.expectation_values, float) - assert isinstance(r2.expectation_values, float) - assert np.isclose(r1.expectation_values, r2.expectation_values) + r1 = run(c1, device) + r2 = run(c2, device) + assert isinstance(r1.expectation_values, float) + assert isinstance(r2.expectation_values, float) + assert np.isclose(r1.expectation_values, r2.expectation_values) @pytest.mark.parametrize( diff --git a/tests/execution/test_vqa.py b/tests/execution/test_vqa.py index 77fb7323..b656a67c 100644 --- a/tests/execution/test_vqa.py +++ b/tests/execution/test_vqa.py @@ -25,32 +25,94 @@ theta: Expr = symbols("θ") -def with_local_devices(args: tuple[Any, ...]): +def with_local_devices_qiskit(args: tuple[Any, ...]): return ( (*args, d) for d in list(IBMDevice) - + list(ATOSDevice) - + list(AWSDevice) - + list(GOOGLEDevice) if not d.is_remote() and d.is_gate_based() and not d.has_reduced_gate_set() ) +def with_local_devices_braket(args: tuple[Any, ...]): + return ( + (*args, d) + for d in list(AWSDevice) + if not d.is_remote() and d.is_gate_based() and not d.has_reduced_gate_set() + ) + + +def with_local_devices_myqlm(args: tuple[Any, ...]): + return ( + (*args, d) + for d in list(ATOSDevice) + if not d.is_remote() and d.is_gate_based() and not d.has_reduced_gate_set() + ) + + +def with_local_devices_cirq(args: tuple[Any, ...]): + return ( + (*args, d) + for d in list(GOOGLEDevice) + if not d.is_remote() and d.is_gate_based() and not d.has_reduced_gate_set() + ) + + +circuit_min = ( + QCircuit( + [ + P(theta, 0), + ExpectationMeasure(Observable(np.array([[0, 1], [1, 0]])), [0]), + ] + ), + 0, +) + + +@pytest.mark.provider("qiskit") @pytest.mark.parametrize( "circ, minimum, device", - with_local_devices( - ( - QCircuit( - [ - P(theta, 0), - ExpectationMeasure(Observable(np.array([[0, 1], [1, 0]])), [0]), - ] - ), - 0, - ) - ), + with_local_devices_qiskit(circuit_min), +) +def test_optimizer_circuit_qiskit( + circ: QCircuit, minimum: float, device: AvailableDevice +): + exec_optimizer_circuit(circ, minimum, device) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize( + "circ, minimum, device", + with_local_devices_braket(circuit_min), ) -def test_optimizer_circuit(circ: QCircuit, minimum: float, device: AvailableDevice): +def test_optimizer_circuit_braket( + circ: QCircuit, minimum: float, device: AvailableDevice +): + exec_optimizer_circuit(circ, minimum, device) + + +@pytest.mark.provider("cirq") +@pytest.mark.parametrize( + "circ, minimum, device", + with_local_devices_cirq(circuit_min), +) +def test_optimizer_circuit_cirq( + circ: QCircuit, minimum: float, device: AvailableDevice +): + exec_optimizer_circuit(circ, minimum, device) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize( + "circ, minimum, device", + with_local_devices_myqlm(circuit_min), +) +def test_optimizer_circuit_myqlm( + circ: QCircuit, minimum: float, device: AvailableDevice +): + exec_optimizer_circuit(circ, minimum, device) + + +def exec_optimizer_circuit(circ: QCircuit, minimum: float, device: AvailableDevice): def run(): assert minimize(circ, Optimizer.BFGS, device)[0] - minimum < 0.05 @@ -65,9 +127,41 @@ def run(): raise +@pytest.mark.provider("qiskit") @pytest.mark.parametrize( "eval_f, minimum, device", - with_local_devices( + with_local_devices_qiskit( + ( + lambda params: ( + 1 + - run( + QCircuit( + [ + P(theta, 0), + ExpectationMeasure( + Observable(np.array([[0, 1], [1, 0]])), [0] + ), + ] + ), + IBMDevice.AER_SIMULATOR, + {theta: params[0]}, + ).expectation_values # pyright: ignore[reportOperatorIssue] + ** 2 + ), + 1, + ) + ), +) +def test_optimizer_func_qiskit( + eval_f: OptimizableFunc, minimum: float, device: AvailableDevice +): + exec_optimizer_func(eval_f, minimum, device) + + +@pytest.mark.provider("myqlm") +@pytest.mark.parametrize( + "eval_f, minimum, device", + with_local_devices_myqlm( ( lambda params: ( 1 @@ -89,7 +183,75 @@ def run(): ) ), ) -def test_optimizer_func( +def test_optimizer_func_myqlm( + eval_f: OptimizableFunc, minimum: float, device: AvailableDevice +): + exec_optimizer_func(eval_f, minimum, device) + + +@pytest.mark.provider("cirq") +@pytest.mark.parametrize( + "eval_f, minimum, device", + with_local_devices_cirq( + ( + lambda params: ( + 1 + - run( + QCircuit( + [ + P(theta, 0), + ExpectationMeasure( + Observable(np.array([[0, 1], [1, 0]])), [0] + ), + ] + ), + GOOGLEDevice.CIRQ_LOCAL_SIMULATOR, + {theta: params[0]}, + ).expectation_values # pyright: ignore[reportOperatorIssue] + ** 2 + ), + 1, + ) + ), +) +def test_optimizer_func_cirq( + eval_f: OptimizableFunc, minimum: float, device: AvailableDevice +): + exec_optimizer_func(eval_f, minimum, device) + + +@pytest.mark.provider("braket") +@pytest.mark.parametrize( + "eval_f, minimum, device", + with_local_devices_braket( + ( + lambda params: ( + 1 + - run( + QCircuit( + [ + P(theta, 0), + ExpectationMeasure( + Observable(np.array([[0, 1], [1, 0]])), [0] + ), + ] + ), + AWSDevice.BRAKET_LOCAL_SIMULATOR, + {theta: params[0]}, + ).expectation_values # pyright: ignore[reportOperatorIssue] + ** 2 + ), + 1, + ) + ), +) +def test_optimizer_func_braket( + eval_f: OptimizableFunc, minimum: float, device: AvailableDevice +): + exec_optimizer_func(eval_f, minimum, device) + + +def exec_optimizer_func( eval_f: OptimizableFunc, minimum: float, device: AvailableDevice ): try: diff --git a/tests/local_storage/test_local_storage.py b/tests/local_storage/test_local_storage.py index 8eb57ee4..8c50d4b1 100644 --- a/tests/local_storage/test_local_storage.py +++ b/tests/local_storage/test_local_storage.py @@ -3,6 +3,7 @@ import inspect import os from copy import deepcopy +import sys from types import TracebackType from typing import Optional, Type @@ -143,7 +144,7 @@ def __exit__( save_env_variable("DB_PATH", self.save_local_storage) -def test_get_all_jobs(mock_local_storage_jobs: list[dict[str, DictDB | Job]]): +def run_get_all_jobs(mock_local_storage_jobs: list[dict[str, DictDB | Job]]): with DBRunner(): jobs = get_all_jobs() @@ -151,7 +152,7 @@ def test_get_all_jobs(mock_local_storage_jobs: list[dict[str, DictDB | Job]]): assert mock_local_storage_job["job"] == job -def test_fetch_all_jobs(mock_local_storage_jobs: list[dict[str, DictDB | Job]]): +def run_fetch_all_jobs(mock_local_storage_jobs: list[dict[str, DictDB | Job]]): with DBRunner(): jobs = fetch_all_jobs() @@ -159,7 +160,7 @@ def test_fetch_all_jobs(mock_local_storage_jobs: list[dict[str, DictDB | Job]]): assert mock_local_storage_job["job_local_storage"] == job -def test_get_all_results(mock_local_storage_results: list[dict[str, DictDB | Result]]): +def run_get_all_results(mock_local_storage_results: list[dict[str, DictDB | Result]]): with DBRunner(): results = get_all_results() @@ -169,7 +170,7 @@ def test_get_all_results(mock_local_storage_results: list[dict[str, DictDB | Res assert result == mock_local_storage_result["result"] -def test_fetch_all_results( +def run_fetch_all_results( mock_local_storage_results: list[dict[str, DictDB | Result]], ): with DBRunner(): @@ -181,7 +182,7 @@ def test_fetch_all_results( assert result == mock_local_storage_result["result_local_storage"] -def test_fetch_jobs_with_job(mock_local_storage_jobs: list[dict[str, DictDB | Job]]): +def run_fetch_jobs_with_job(mock_local_storage_jobs: list[dict[str, DictDB | Job]]): with DBRunner(): job = mock_local_storage_jobs[0]['job'] expected_job = mock_local_storage_jobs[0]['job_local_storage'] @@ -195,7 +196,7 @@ def test_fetch_jobs_with_job(mock_local_storage_jobs: list[dict[str, DictDB | Jo assert fetched_job == expected_job -def test_get_job_with_id(mock_local_storage_jobs: list[dict[str, DictDB | Job]]): +def run_get_job_with_id(mock_local_storage_jobs: list[dict[str, DictDB | Job]]): with DBRunner(): job_id = 1 fetched_jobs = get_jobs_with_id(job_id) @@ -211,7 +212,7 @@ def test_get_job_with_id(mock_local_storage_jobs: list[dict[str, DictDB | Job]]) assert fetched_job == expected_job -def test_fetch_jobs_with_id(mock_local_storage_jobs: list[dict[str, DictDB | Job]]): +def run_fetch_jobs_with_id(mock_local_storage_jobs: list[dict[str, DictDB | Job]]): with DBRunner(): job_id = 1 fetched_jobs = fetch_jobs_with_id(job_id) @@ -227,7 +228,7 @@ def test_fetch_jobs_with_id(mock_local_storage_jobs: list[dict[str, DictDB | Job assert fetched_job == expected_job -def test_get_results_with_result( +def run_get_results_with_result( mock_local_storage_results: list[dict[str, DictDB | Result]], ): with DBRunner(): @@ -239,7 +240,7 @@ def test_get_results_with_result( assert fetched_result == result -def test_fetch_results_with_result( +def run_fetch_results_with_result( mock_local_storage_results: list[dict[str, DictDB | Result]], ): with DBRunner(): @@ -262,7 +263,7 @@ def test_fetch_results_with_result( assert filtered_fetched == filtered_expected -def test_get_results_with_job_id( +def run_get_results_with_job_id( mock_local_storage_results: list[dict[str, DictDB | Result]], ): with DBRunner(): @@ -280,7 +281,7 @@ def test_get_results_with_job_id( assert fetched_result == expected_result -def test_fetch_results_with_job_id( +def run_fetch_results_with_job_id( mock_local_storage_results: list[dict[str, DictDB | Result]], ): with DBRunner(): @@ -298,7 +299,7 @@ def test_fetch_results_with_job_id( assert fetched_result == expected_result -def test_get_result_with_id( +def run_get_result_with_id( mock_local_storage_results: list[dict[str, DictDB | Result]], ): with DBRunner(): @@ -316,7 +317,7 @@ def test_get_result_with_id( assert fetched_result == expected_result -def test_fetch_results_with_id( +def run_fetch_results_with_id( mock_local_storage_results: list[dict[str, DictDB | Result]], ): with DBRunner(): @@ -337,7 +338,7 @@ def test_fetch_results_with_id( assert fetched_result == expected_result -def test_fetch_results_with_job( +def run_fetch_results_with_job( mock_local_storage_jobs: list[dict[str, DictDB | Job]], mock_local_storage_results: list[dict[str, DictDB | Result]], ): @@ -357,7 +358,7 @@ def test_fetch_results_with_job( assert fetched_result == expected_result -def test_get_results_with_result_and_job( +def run_get_results_with_result_and_job( mock_local_storage_results: list[dict[str, DictDB | Result]], ): with DBRunner(): @@ -369,7 +370,7 @@ def test_get_results_with_result_and_job( assert fetched_result == result -def test_fetch_results_with_result_and_job( +def run_fetch_results_with_result_and_job( mock_local_storage_results: list[dict[str, DictDB | Result]], ): with DBRunner(): @@ -382,7 +383,7 @@ def test_fetch_results_with_result_and_job( assert fetched_result == expected_result -def test_get_jobs_with_result( +def run_get_jobs_with_result( mock_local_storage_results: list[dict[str, DictDB | Result]], mock_local_storage_jobs: list[dict[str, DictDB | Job]], ): @@ -405,7 +406,7 @@ def test_get_jobs_with_result( assert fetched_job == expected_job -def test_fetch_jobs_with_result( +def run_fetch_jobs_with_result( mock_local_storage_results: list[dict[str, DictDB | Result]], mock_local_storage_jobs: list[dict[str, DictDB | Job]], ): @@ -430,7 +431,7 @@ def test_fetch_jobs_with_result( assert fetched_job == expected_job -def test_get_jobs_with_job(mock_local_storage_jobs: list[dict[str, DictDB | Job]]): +def run_get_jobs_with_job(mock_local_storage_jobs: list[dict[str, DictDB | Job]]): with DBRunner(): job = mock_local_storage_jobs[0]['job'] assert isinstance(job, Job) @@ -440,7 +441,7 @@ def test_get_jobs_with_job(mock_local_storage_jobs: list[dict[str, DictDB | Job] assert fetched_job == job -def test_local_storage_to_mpqp( +def run_local_storage_to_mpqp( mock_local_storage_results: list[dict[str, DictDB | Result]], mock_local_storage_jobs: list[dict[str, DictDB | Job]], ): @@ -477,7 +478,7 @@ def circuits_type(): ] -def test_local_storage_insert(circuits_type: list[QCircuit]): +def run_local_storage_insert(circuits_type: list[QCircuit]): with DBRunner(): for circuit in circuits_type: results = run(circuit, IBMDevice.AER_SIMULATOR) @@ -488,7 +489,7 @@ def test_local_storage_insert(circuits_type: list[QCircuit]): assert results == get_results_with_id(id)[0] -def test_insert_job(circuits_type: list[QCircuit]): +def run_insert_job(circuits_type: list[QCircuit]): with DBRunner(): for circuit in circuits_type: results = run(circuit, IBMDevice.AER_SIMULATOR) @@ -499,7 +500,7 @@ def test_insert_job(circuits_type: list[QCircuit]): assert results.job == get_jobs_with_id(id)[0] -def test_remove_all_with_job_id(): +def run_remove_all_with_job_id(): with DBRunner(): remove_all_with_job_id(1) jobs = fetch_jobs_with_id(1) @@ -508,14 +509,14 @@ def test_remove_all_with_job_id(): assert len(results) == 0 -def test_remove_jobs_with_id(): +def run_remove_jobs_with_id(): with DBRunner(): remove_jobs_with_id(1) jobs = fetch_jobs_with_id(1) assert len(jobs) == 0 -def test_remove_jobs_with_jobs_local_storage( +def run_remove_jobs_with_jobs_local_storage( mock_local_storage_jobs: list[dict[str, DictDB | Job]], ): with DBRunner(): @@ -527,14 +528,14 @@ def test_remove_jobs_with_jobs_local_storage( assert job != job_local_storage -def test_remove_results_with_id(): +def run_remove_results_with_id(): with DBRunner(): remove_results_with_id(1) results = fetch_results_with_id(1) assert len(results) == 0 -def test_remove_results_with_result( +def run_remove_results_with_result( mock_local_storage_results: list[dict[str, DictDB | Result]], ): with DBRunner(): @@ -546,7 +547,7 @@ def test_remove_results_with_result( assert r != result -def test_remove_results_with_job( +def run_remove_results_with_job( mock_local_storage_jobs: list[dict[str, DictDB | Job]], ): with DBRunner(): @@ -560,7 +561,7 @@ def test_remove_results_with_job( assert r['job_id'] != job_local_storage['id'] -def test_remove_results_with_job_id( +def run_remove_results_with_job_id( mock_local_storage_jobs: list[dict[str, DictDB | Job]], ): with DBRunner(): @@ -572,7 +573,7 @@ def test_remove_results_with_job_id( assert r['job_id'] != job_local_storage['id'] -def test_remove_results_with_results_local_storage( +def run_remove_results_with_results_local_storage( mock_local_storage_results: list[dict[str, DictDB | Result]], ): with DBRunner(): @@ -582,3 +583,38 @@ def test_remove_results_with_results_local_storage( results = fetch_all_results() for r in results: assert r != result_local_storage + + +if "--long-local" in sys.argv or "--long" in sys.argv: + test_get_all_jobs = run_get_all_jobs + test_fetch_all_jobs = run_fetch_all_jobs + test_get_all_results = run_get_all_results + test_fetch_all_results = run_fetch_all_results + test_fetch_jobs_with_job = run_fetch_jobs_with_job + test_get_job_with_id = run_get_job_with_id + test_fetch_jobs_with_id = run_fetch_jobs_with_id + test_get_results_with_result = run_get_results_with_result + test_fetch_results_with_result = run_fetch_results_with_result + test_get_results_with_job_id = run_get_results_with_job_id + test_fetch_results_with_job_id = run_fetch_results_with_job_id + test_get_result_with_id = run_get_result_with_id + test_fetch_results_with_id = run_fetch_results_with_id + test_fetch_results_with_job = run_fetch_results_with_job + test_get_results_with_result_and_job = run_get_results_with_result_and_job + test_fetch_results_with_result_and_job = run_fetch_results_with_result_and_job + test_get_jobs_with_result = run_get_jobs_with_result + test_fetch_jobs_with_result = run_fetch_jobs_with_result + test_get_jobs_with_job = run_get_jobs_with_job + test_local_storage_to_mpqp = run_local_storage_to_mpqp + test_local_storage_insert = run_local_storage_insert + test_insert_job = run_insert_job + test_remove_all_with_job_id = run_remove_all_with_job_id + test_remove_jobs_with_id = run_remove_jobs_with_id + test_remove_jobs_with_jobs_local_storage = run_remove_jobs_with_jobs_local_storage + test_remove_results_with_id = run_remove_results_with_id + test_remove_results_with_result = run_remove_results_with_result + test_remove_results_with_job = run_remove_results_with_job + test_remove_results_with_job_id = run_remove_results_with_job_id + test_remove_results_with_results_local_storage = ( + run_remove_results_with_results_local_storage + ) diff --git a/tests/noise/test_noise_model.py b/tests/noise/test_noise_model.py index d12b7489..150f0a41 100644 --- a/tests/noise/test_noise_model.py +++ b/tests/noise/test_noise_model.py @@ -43,6 +43,7 @@ def noise(): return Depolarizing(0.3, [0]) +@pytest.mark.provider("braket") def test_depolarizing_braket_export(noise: NoiseModel): from braket.circuits.noises import Depolarizing as BraketDepolarizing @@ -52,6 +53,7 @@ def test_depolarizing_braket_export(noise: NoiseModel): assert braket_noise.qubit_count == 1 +@pytest.mark.provider("myqlm") def test_depolarizing_qlm_export(noise: NoiseModel): from qat.quops.quantum_channels import QuantumChannelKraus diff --git a/tests/noise/test_noisy_execution.py b/tests/noise/test_noisy_execution.py index e12f7520..4d56da81 100644 --- a/tests/noise/test_noisy_execution.py +++ b/tests/noise/test_noisy_execution.py @@ -16,7 +16,6 @@ BitFlip, Depolarizing, ExpectationMeasure, - GOOGLEDevice, IBMDevice, Observable, PhaseDamping, @@ -28,14 +27,15 @@ from mpqp.tools.errors import UnsupportedBraketFeaturesWarning from mpqp.tools.theoretical_simulation import validate_noisy_circuit -noisy_devices: list[Any] = [ - dev - for dev in list(ATOSDevice) + list(AWSDevice) + list(IBMDevice) + list(GOOGLEDevice) - if dev.is_noisy_simulator() -] +# noisy_devices: list[Any] = [ +# dev +# for dev in list(ATOSDevice) + list(AWSDevice) + list(IBMDevice) + list(GOOGLEDevice) +# if dev.is_noisy_simulator() +# ] # TODO: in the end this should be automatic as drafted above, but for now only # one device is stable -noisy_devices = [AWSDevice.BRAKET_LOCAL_SIMULATOR, IBMDevice.AER_SIMULATOR] +noisy_devices_Braket = [AWSDevice.BRAKET_LOCAL_SIMULATOR] +noisy_devices_qiskit = [IBMDevice.AER_SIMULATOR] def filter_braket_warning( @@ -78,20 +78,52 @@ def circuit(): @pytest.fixture -def devices(): - devices: list[AvailableDevice] = [ +def devices_myqlm() -> list[AvailableDevice]: + devices = [] + if "--long" in sys.argv: + devices.append(ATOSDevice.QLM_NOISYQPROC) + return devices + + +@pytest.fixture +def devices_braket() -> list[AvailableDevice]: + return [ AWSDevice.BRAKET_LOCAL_SIMULATOR, + ] + + +@pytest.fixture +def devices_IBMDevice() -> list[AvailableDevice]: + return [ IBMDevice.AER_SIMULATOR, IBMDevice.AER_SIMULATOR_STATEVECTOR, IBMDevice.AER_SIMULATOR_MATRIX_PRODUCT_STATE, IBMDevice.AER_SIMULATOR_DENSITY_MATRIX, ] - if "--long" in sys.argv: - devices.append(ATOSDevice.QLM_NOISYQPROC) - return devices -def test_noisy_expectation_value_execution_without_error( +@pytest.mark.provider("myqlm") +def test_exec_noisy_expectation_value_execution_without_error_myqlm( + circuit: QCircuit, devices_myqlm: list[AvailableDevice] +): + exec_noisy_expectation_value_execution_without_error(circuit, devices_myqlm) + + +@pytest.mark.provider("braket") +def test_exec_noisy_expectation_value_execution_without_error_braket( + circuit: QCircuit, devices_braket: list[AvailableDevice] +): + exec_noisy_expectation_value_execution_without_error(circuit, devices_braket) + + +@pytest.mark.provider("qiskit") +def test_exec_noisy_expectation_value_execution_without_error_IBMDevice( + circuit: QCircuit, devices_IBMDevice: list[AvailableDevice] +): + exec_noisy_expectation_value_execution_without_error(circuit, devices_IBMDevice) + + +def exec_noisy_expectation_value_execution_without_error( circuit: QCircuit, devices: list[AvailableDevice] ): circuit.add( @@ -106,12 +138,34 @@ def test_noisy_expectation_value_execution_without_error( PhaseDamping(0.6), ] ) - with pytest.warns(UnsupportedBraketFeaturesWarning): - run(circuit, devices) + run(circuit, devices) assert True -def test_all_native_gates_global_noise_execution_without_error( +@pytest.mark.provider("myqlm") +def test_all_native_gates_global_noise_execution_without_error_myqlm( + circuit: QCircuit, devices_myqlm: list[AvailableDevice] +): + exec_all_native_gates_global_noise_execution_without_error(circuit, devices_myqlm) + + +@pytest.mark.provider("braket") +def test_all_native_gates_global_noise_execution_without_error_braket( + circuit: QCircuit, devices_braket: list[AvailableDevice] +): + exec_all_native_gates_global_noise_execution_without_error(circuit, devices_braket) + + +@pytest.mark.provider("qiskit") +def test_all_native_gates_global_noise_execution_without_error_IBMDevice( + circuit: QCircuit, devices_IBMDevice: list[AvailableDevice] +): + exec_all_native_gates_global_noise_execution_without_error( + circuit, devices_IBMDevice + ) + + +def exec_all_native_gates_global_noise_execution_without_error( circuit: QCircuit, devices: list[AvailableDevice] ): circuit.add( @@ -127,12 +181,32 @@ def test_all_native_gates_global_noise_execution_without_error( PhaseDamping(0.4, gates=[CNOT, H]), ] ) - with pytest.warns(UnsupportedBraketFeaturesWarning): - run(circuit, devices) + run(circuit, devices) assert True -def test_all_native_gates_local_noise( +@pytest.mark.provider("myqlm") +def test_all_native_gates_local_noise_myqlm( + circuit: QCircuit, devices_myqlm: list[AvailableDevice] +): + exec_all_native_gates_local_noise(circuit, devices_myqlm) + + +@pytest.mark.provider("braket") +def test_all_native_gates_local_noise_braket( + circuit: QCircuit, devices_braket: list[AvailableDevice] +): + exec_all_native_gates_local_noise(circuit, devices_braket) + + +@pytest.mark.provider("qiskit") +def test_all_native_gates_local_noise_IBMDevice( + circuit: QCircuit, devices_IBMDevice: list[AvailableDevice] +): + exec_all_native_gates_local_noise(circuit, devices_IBMDevice) + + +def exec_all_native_gates_local_noise( circuit: QCircuit, devices: list[AvailableDevice] ): circuit.add( @@ -149,20 +223,42 @@ def test_all_native_gates_local_noise( PhaseDamping(0.4, [0, 1, 2], gates=[CNOT, H]), ] ) - with pytest.warns(UnsupportedBraketFeaturesWarning): - run(circuit, devices) + run(circuit, devices) assert True +@pytest.mark.provider("braket") +@pytest.mark.parametrize( + "depol_noise, shots, device", + list( + product( + [0.001, 0.01, 0.1, 0.1, 0.2, 0.3], + [500, 1_000, 5_000, 10_000, 50_000, 100_000], + noisy_devices_Braket, + ) + ), +) +def test_validate_depolarizing_noise_braket( + circuit: QCircuit, depol_noise: float, shots: int, device: AvailableDevice +): + circuit.add(Depolarizing(depol_noise)) + assert filter_braket_warning( + lambda d: validate_noisy_circuit(circuit, shots, d), device + ) + + +@pytest.mark.provider("qiskit") @pytest.mark.parametrize( "depol_noise, shots, device", - product( - [0.001, 0.01, 0.1, 0.1, 0.2, 0.3], - [500, 1_000, 5_000, 10_000, 50_000, 100_000], - noisy_devices, + list( + product( + [0.001, 0.01, 0.1, 0.1, 0.2, 0.3], + [500, 1_000, 5_000, 10_000, 50_000, 100_000], + noisy_devices_qiskit, + ) ), ) -def test_validate_depolarizing_noise( +def test_validate_depolarizing_noise_qiskit( circuit: QCircuit, depol_noise: float, shots: int, device: AvailableDevice ): circuit.add(Depolarizing(depol_noise)) diff --git a/tests/qasm/test_mpqp_to_qasm.py b/tests/qasm/test_mpqp_to_qasm.py index 928ddd7e..0aa4a6e5 100644 --- a/tests/qasm/test_mpqp_to_qasm.py +++ b/tests/qasm/test_mpqp_to_qasm.py @@ -4,7 +4,6 @@ from mpqp import Barrier, BasisMeasure, Instruction, Language, QCircuit from mpqp.gates import * from mpqp.qasm.mpqp_to_qasm import mpqp_to_qasm2 -from mpqp.qasm.open_qasm_2_and_3 import remove_user_gates from mpqp.tools.circuit import random_circuit from mpqp.tools.display import format_element_str @@ -266,17 +265,10 @@ def test_mpqp_to_qasm_gate(instructions: list[Instruction], qasm_expectation: st ) def test_mpqp_to_qasm_custom_gate(instructions: list[Instruction]): circuit = QCircuit(instructions) - from qiskit import QuantumCircuit, qasm2 - qiskit_circuit = circuit.to_other_language(Language.QISKIT) - assert isinstance(qiskit_circuit, QuantumCircuit) - str_qiskit_circuit = remove_user_gates(qasm2.dumps(qiskit_circuit), True) str_circuit = circuit.to_other_language(Language.QASM2) - assert isinstance(str_circuit, str) - for i in str_circuit: - assert i in str_qiskit_circuit - for i in str_qiskit_circuit: - assert i in str_circuit + mpqp_qasm = QCircuit.from_other_language(str_circuit) + assert np.isclose(circuit.to_matrix(), mpqp_qasm.to_matrix()).all() @pytest.mark.parametrize( @@ -525,7 +517,7 @@ def test_random_mpqp_to_qasm(): qcircuit = random_circuit(nb_qubits=6, nb_gates=20) from qiskit import QuantumCircuit, qasm2 - qiskit_circuit = qcircuit.to_other_language(Language.QISKIT).reverse_bits() + qiskit_circuit = qcircuit.to_other_language(Language.QISKIT) assert isinstance(qiskit_circuit, QuantumCircuit) qiskit_qasm = normalize_string(qasm2.dumps(qiskit_circuit)) mpqp_qasm = qcircuit.to_other_language(Language.QASM2) diff --git a/tests/qasm/test_open_qasm_2_and_3.py b/tests/qasm/test_open_qasm_2_and_3.py index c17eaaa9..651fa0a3 100644 --- a/tests/qasm/test_open_qasm_2_and_3.py +++ b/tests/qasm/test_open_qasm_2_and_3.py @@ -85,8 +85,7 @@ def test_circular_dependency_detection_false_positive_3_to_2(): @pytest.mark.parametrize( "qasm_code", [ - ( - """OPENQASM 2.0; + ("""OPENQASM 2.0; include "qelib1.inc"; gate rzz(theta) a,b { @@ -97,10 +96,8 @@ def test_circular_dependency_detection_false_positive_3_to_2(): qreg q[3]; creg c[2]; rzz(0.2) q[1], q[2]; - measure q[2] -> c[0];""" - ), - ( - """OPENQASM 2.0; + measure q[2] -> c[0];"""), + ("""OPENQASM 2.0; include "qelib1.inc"; gate my_gate a,b { h a; @@ -109,25 +106,19 @@ def test_circular_dependency_detection_false_positive_3_to_2(): qreg q[2]; creg c[2]; my_gate q[0], q[1]; - measure q -> c;""" - ), - ( - """OPENQASM 2.0; + measure q -> c;"""), + ("""OPENQASM 2.0; include "qelib1.inc"; qreg q[3]; cx q[0],q[1]; - cx q[1],q[2];""" - ), - ( - """OPENQASM 2.0; + cx q[1],q[2];"""), + ("""OPENQASM 2.0; include "qelib1.inc"; qreg q[3]; creg c[2]; u1(0.2) q[1], q[2]; - measure q[2] -> c[0];""" - ), - ( - """OPENQASM 2.0; + measure q[2] -> c[0];"""), + ("""OPENQASM 2.0; include "qelib1.inc"; gate rzz(theta) a,b { cx a,b; @@ -137,10 +128,8 @@ def test_circular_dependency_detection_false_positive_3_to_2(): qreg q[3]; creg c[2]; rzz(0.2) q[1] , q[2]; - measure q[2] -> c[0];""" - ), - ( - """OPENQASM 2.0; + measure q[2] -> c[0];"""), + ("""OPENQASM 2.0; include "qelib1.inc"; gate MyGate a, b { @@ -158,12 +147,11 @@ def test_circular_dependency_detection_false_positive_3_to_2(): creg c[3]; MyGate q[0], q[1]; - MyGate2 q[0], q[1], q[2];""" - ), + MyGate2 q[0], q[1], q[2];"""), ], ) def test_conversion_2_and_3(qasm_code: str): - convert, _ = open_qasm_3_to_2(open_qasm_2_to_3(qasm_code)) + convert = open_qasm_3_to_2(open_qasm_2_to_3(qasm_code)) assert normalize_whitespace(convert) == normalize_whitespace(qasm_code) @@ -369,7 +357,7 @@ def test_conversion_2_to_3(qasm_code: str, expected_output: str): ], ) def test_conversion_3_to_2(expected_output: str, qasm_code: str): - convert, _ = open_qasm_3_to_2(qasm_code) + convert = open_qasm_3_to_2(qasm_code) assert normalize_whitespace(convert) == normalize_whitespace(expected_output) @@ -901,8 +889,7 @@ def test_remove_user_gates(qasm_code: str, expected_output: str): def test_sample_counts_in_trust_interval( qasm3: str, expected: tuple[list[Instruction], float] ): - qasm_2, gphase = open_qasm_3_to_2(qasm3) - print(gphase) + qasm_2 = open_qasm_3_to_2(qasm3) print(qasm_2) circuit = qasm2_parse(qasm_2) @@ -912,10 +899,6 @@ def test_sample_counts_in_trust_interval( err_rate_percentage = 1 - np.power(1 - err_rate, (1 / 2)) expected_amplitudes = amplitude(expected_circuit) * exp(expected_gphase * 1j) - - print(circuit.input_g_phase) - circuit.input_g_phase = gphase - print(circuit.input_g_phase) result = run(circuit, IBMDevice.AER_SIMULATOR) assert isinstance(result, Result) print("result_amplitudes: " + str(result.amplitudes)) diff --git a/tests/qasm/test_qasm_to_braket.py b/tests/qasm/test_qasm_to_braket.py index 220efe76..1b1eb6c8 100644 --- a/tests/qasm/test_qasm_to_braket.py +++ b/tests/qasm/test_qasm_to_braket.py @@ -1,11 +1,14 @@ import pytest -from braket.circuits import Circuit, Operator -from braket.circuits.gates import CNot, H +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from braket.circuits import Operator from mpqp.qasm.qasm_to_braket import qasm3_to_braket_Circuit from mpqp.tools.errors import UnsupportedBraketFeaturesWarning +@pytest.mark.provider("braket") @pytest.mark.parametrize( "qasm_code, braket_operators", [ @@ -15,7 +18,9 @@ ), ], ) -def test_qasm3_to_braket_Circuit(qasm_code: str, braket_operators: list[Operator]): +def test_qasm3_to_braket_Circuit(qasm_code: str, braket_operators: list["Operator"]): + from braket.circuits import Circuit + circ = qasm3_to_braket_Circuit(qasm_code) assert isinstance(circ, Circuit) @@ -23,9 +28,11 @@ def test_qasm3_to_braket_Circuit(qasm_code: str, braket_operators: list[Operator assert circ_instr.operator == expected_operator -@pytest.mark.parametrize( - "qasm_code, braket_operators", - [ +@pytest.fixture +def list_to_braket_operator() -> list[tuple[str, list["Operator"]]]: + from braket.circuits.gates import CNot, H + + return [ ( """OPENQASM 3.0; include 'stdgates.inc'; @@ -39,18 +46,23 @@ def test_qasm3_to_braket_Circuit(qasm_code: str, braket_operators: list[Operator c[1] = measure q[1];""", [H(), CNot()], ), - ], -) + ] + + +@pytest.mark.provider("braket") def test_qasm3_to_braket_Circuit_warning( - qasm_code: str, braket_operators: list[Operator] + list_to_braket_operator: list[tuple[str, list["Operator"]]], ): - warning = ( - "This program uses OpenQASM language features that may not be supported" - " on QPUs or on-demand simulators." - ) - with pytest.warns(UnsupportedBraketFeaturesWarning, match=warning): - circ = qasm3_to_braket_Circuit(qasm_code) + from braket.circuits import Circuit - assert isinstance(circ, Circuit) - for circ_instr, expected_operator in zip(circ.instructions, braket_operators): - assert circ_instr.operator == expected_operator + for qasm_code, braket_operators in list_to_braket_operator: + warning = ( + "This program uses OpenQASM language features that may not be supported" + " on QPUs or on-demand simulators." + ) + with pytest.warns(UnsupportedBraketFeaturesWarning, match=warning): + circ = qasm3_to_braket_Circuit(qasm_code) + + assert isinstance(circ, Circuit) + for circ_instr, expected_operator in zip(circ.instructions, braket_operators): + assert circ_instr.operator == expected_operator diff --git a/tests/qasm/test_qasm_to_cirq.py b/tests/qasm/test_qasm_to_cirq.py index b178fa17..786e64eb 100644 --- a/tests/qasm/test_qasm_to_cirq.py +++ b/tests/qasm/test_qasm_to_cirq.py @@ -1,9 +1,9 @@ import pytest from mpqp.qasm.qasm_to_cirq import qasm2_to_cirq_Circuit -from cirq.circuits.circuit import Circuit +@pytest.mark.provider("cirq") @pytest.mark.parametrize( "qasm_code, gate_names", [ @@ -85,6 +85,8 @@ ], ) def test_qasm2_to_Cirq_Circuit(qasm_code: str, gate_names: list[str]): + from cirq.circuits.circuit import Circuit + circ = qasm2_to_cirq_Circuit(qasm_code) assert isinstance(circ, Circuit) for operations, expected_gate in zip(circ.all_operations(), gate_names): diff --git a/tests/qasm/test_qasm_to_mpqp.py b/tests/qasm/test_qasm_to_mpqp.py index c27b59f7..dbc0da4d 100644 --- a/tests/qasm/test_qasm_to_mpqp.py +++ b/tests/qasm/test_qasm_to_mpqp.py @@ -176,14 +176,12 @@ def test_qasm2_to_mpqp(qasm_code: str, gate_names: list[str]): @pytest.mark.parametrize( "qasm_code", [ - ( - """OPENQASM 2.0; + ("""OPENQASM 2.0; include "qelib1.inc"; qreg q[1]; h q[0] - cx q[0], """ - ), + cx q[0], """), ], ) def test_invalid_qasm_code(qasm_code: str): diff --git a/tests/qasm/test_qasm_to_myqlm.py b/tests/qasm/test_qasm_to_myqlm.py index 8a9b233c..97bcf682 100644 --- a/tests/qasm/test_qasm_to_myqlm.py +++ b/tests/qasm/test_qasm_to_myqlm.py @@ -1,9 +1,9 @@ import pytest from mpqp.qasm.qasm_to_myqlm import qasm2_to_myqlm_Circuit -from qat.core.wrappers.circuit import Circuit +@pytest.mark.provider("myqlm") @pytest.mark.parametrize( "qasm_code, gate_names", [ @@ -26,6 +26,8 @@ ], ) def test_qasm2_to_myqlm_Circuit(qasm_code: str, gate_names: list[str]): + from qat.core.wrappers.circuit import Circuit + circ = qasm2_to_myqlm_Circuit(qasm_code) assert isinstance(circ, Circuit) for op, expected_gate in zip(circ.ops, gate_names): diff --git a/tests/qasm/test_qasm_to_qiskit.py b/tests/qasm/test_qasm_to_qiskit.py index 73771913..4c60a4c0 100644 --- a/tests/qasm/test_qasm_to_qiskit.py +++ b/tests/qasm/test_qasm_to_qiskit.py @@ -3,6 +3,7 @@ from mpqp.qasm.qasm_to_qiskit import qasm2_to_Qiskit_Circuit +@pytest.mark.provider("qiskit") @pytest.mark.parametrize( "qasm_code, gate_names", [ diff --git a/tests/test_doc.py b/tests/test_doc.py index 660c3e80..ebd8812b 100644 --- a/tests/test_doc.py +++ b/tests/test_doc.py @@ -3,7 +3,7 @@ import os import sys import warnings -from doctest import SKIP, DocTest, DocTestFinder, DocTestRunner +from doctest import SKIP, DocTest, DocTestFinder, DocTestRunner, register_optionflag from functools import partial from pathlib import Path from types import TracebackType @@ -223,7 +223,7 @@ def __exit__( test_globals = globals().copy() test_globals.update(locals()) -to_pass = ["connection", "noise_methods", "remote_handle"] +file_to_pass = ["connection", "noise_methods", "remote_handle"] files_needing_db = ["local_storage", "result", "job"] unsafe_files = ["env"] + files_needing_db @@ -231,13 +231,45 @@ def __exit__( finder = DocTestFinder() runner = DocTestRunner() +PROVIDER_MYQLM = register_optionflag("MYQLM") +PROVIDER_QISKIT = register_optionflag("QISKIT") +PROVIDER_BRAKET = register_optionflag("BRAKET") +PROVIDER_CIRQ = register_optionflag("CIRQ") + +PROVIDER_FLAGS = { + "myqlm": PROVIDER_MYQLM, + "qiskit": PROVIDER_QISKIT, + "braket": PROVIDER_BRAKET, + "cirq": PROVIDER_CIRQ, +} + def stable_random(*args: Any, **kwargs: Any): user_seed = args[0] if len(args) != 0 else None return default_rng(user_seed or 351) -def run_doctest(root: str, filename: str, monkeypatch: pytest.MonkeyPatch): +def run_doctest( + root: str, + filename: str, + monkeypatch: pytest.MonkeyPatch, + request: pytest.FixtureRequest, +): + + active_providers = request.config.getoption("--providers") + skip_provider_flags = [] + keyword_to_skip = [] + if active_providers is not None: + for name, flag in PROVIDER_FLAGS.items(): + if ( + len(active_providers) == 0 # pyright: ignore[reportArgumentType] + or name not in active_providers # pyright: ignore[reportOperatorIssue] + ): + keyword_to_skip.append(name) + if name == "qiskit": + keyword_to_skip.append("ibm") + skip_provider_flags.append(flag) + monkeypatch.setattr('numpy.random.default_rng', stable_random) warnings.filterwarnings("ignore", category=UnsupportedBraketFeaturesWarning) warnings.filterwarnings("ignore", category=OpenQASMTranslationWarning) @@ -259,12 +291,20 @@ def run_doctest(root: str, filename: str, monkeypatch: pytest.MonkeyPatch): test.docstring and "3M-TODO" not in test.docstring and "6M-TODO" not in test.docstring + and all(keyword not in test.name for keyword in keyword_to_skip) ): + for example in test.examples: + flags = example.options + for flag in PROVIDER_FLAGS.values(): + if flag in flags and flag in skip_provider_flags: + example.options[SKIP] = True + if safe_needed: with EnvRunner(): if any(name in root + filename for name in files_needing_db): - with DBRunner(test.name): - assert runner.run(test).failed == 0 + if "--long-local" in sys.argv or "--long" in sys.argv: + with DBRunner(test.name): + assert runner.run(test).failed == 0 else: assert runner.run(test).failed == 0 else: @@ -274,7 +314,9 @@ def run_doctest(root: str, filename: str, monkeypatch: pytest.MonkeyPatch): folder_path = "mpqp" for root, _, files in os.walk(folder_path): for filename in files: - if all(str not in filename for str in to_pass) and filename.endswith(".py"): + if all(str not in filename for str in file_to_pass) and filename.endswith( + ".py" + ): t_function_name = "test_doc_" + "mpqp".join( (root + "_" + filename).split("mpqp") ).replace("\\", "_").replace("/", "_").replace(".py", "") diff --git a/tests/tools/test_pauli_grouping.py b/tests/tools/test_pauli_grouping.py index 2588259f..49273ca1 100644 --- a/tests/tools/test_pauli_grouping.py +++ b/tests/tools/test_pauli_grouping.py @@ -17,15 +17,22 @@ from mpqp.tools.circuit import random_circuit -@pytest.mark.parametrize( - "device", - [ - (IBMDevice.AER_SIMULATOR), - (GOOGLEDevice.CIRQ_LOCAL_SIMULATOR), - (AWSDevice.BRAKET_LOCAL_SIMULATOR), - ], -) -def test_expectation_values_devices(device: AvailableDevice): +@pytest.mark.provider("qiskit") +def test_expectation_qiskit(): + exec_expectation_value_check(IBMDevice.AER_SIMULATOR) + + +@pytest.mark.provider("cirq") +def test_expectation_cirq(): + exec_expectation_value_check(GOOGLEDevice.CIRQ_LOCAL_SIMULATOR) + + +@pytest.mark.provider("braket") +def test_expectation_braket(): + exec_expectation_value_check(AWSDevice.BRAKET_LOCAL_SIMULATOR) + + +def exec_expectation_value_check(device: AvailableDevice): circuit = random_circuit(nb_qubits=3) string = pX @ pI @ pZ + pX @ pZ @ pZ + pI @ pZ @ pZ str2 = pI @ pZ @ pZ - 2 * pY @ pZ @ pZ + 3 * pX @ pY @ pZ