From 570ae754be9fbf1896b7f810d35f8860b619b284 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Dec 2023 08:31:43 +0100 Subject: [PATCH 1/3] Bump mypy from 1.7.0 to 1.7.1 (#113) Bumps [mypy](https://github.com/python/mypy) from 1.7.0 to 1.7.1. - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](https://github.com/python/mypy/compare/v1.7.0...v1.7.1) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From 4ae5679c940c72335e0302201bff91420662fa10 Mon Sep 17 00:00:00 2001 From: Mythir Date: Tue, 14 Jan 2025 17:44:02 +0100 Subject: [PATCH 2/3] Clean up --- examples/hqca_circuit.py | 35 +- examples/hydrogen_qiskit.py | 61 -- examples/qaoa.py | 30 +- examples/simple.py | 24 - examples/unfair_dice.py | 44 +- quantuminspire/sdk/qiskit/__init__.py | 0 quantuminspire/sdk/qiskit/backend.py | 273 ----- quantuminspire/sdk/qiskit/circuit_parser.py | 694 ------------- quantuminspire/sdk/qiskit/exceptions.py | 9 - quantuminspire/sdk/qiskit/measurements.py | 249 ----- tests/sdk/qiskit/test_backend.py | 106 -- tests/sdk/qiskit/test_circuit_parser.py | 1032 ------------------- tests/sdk/qiskit/test_measurements.py | 231 ----- 13 files changed, 46 insertions(+), 2742 deletions(-) delete mode 100644 examples/hydrogen_qiskit.py delete mode 100755 examples/simple.py delete mode 100644 quantuminspire/sdk/qiskit/__init__.py delete mode 100644 quantuminspire/sdk/qiskit/backend.py delete mode 100644 quantuminspire/sdk/qiskit/circuit_parser.py delete mode 100644 quantuminspire/sdk/qiskit/exceptions.py delete mode 100644 quantuminspire/sdk/qiskit/measurements.py delete mode 100644 tests/sdk/qiskit/test_backend.py delete mode 100644 tests/sdk/qiskit/test_circuit_parser.py delete mode 100644 tests/sdk/qiskit/test_measurements.py diff --git a/examples/hqca_circuit.py b/examples/hqca_circuit.py index 52eba0c5..13679773 100644 --- a/examples/hqca_circuit.py +++ b/examples/hqca_circuit.py @@ -1,22 +1,19 @@ -from pathlib import Path from typing import Any, Dict, List +from opensquirrel.circuit_builder import CircuitBuilder from opensquirrel.ir import Bit, Qubit - -from quantuminspire.sdk.models.circuit import Circuit -from quantuminspire.sdk.models.hybrid_algorithm import HybridAlgorithm -from quantuminspire.util.api.local_backend import LocalBackend -from quantuminspire.util.api.quantum_interface import QuantumInterface +from opensquirrel.writer import writer +from qi2_shared.hybrid.quantum_interface import QuantumInterface def generate_circuit() -> str: - with Circuit(platform_name="spin-2", program_name="prgm1", number_of_qubits=2) as circuit: - circuit.ir.H(Qubit(0)) - circuit.ir.CNOT(Qubit(0), Qubit(1)) - circuit.ir.measure(Qubit(0), Bit(0)) - circuit.ir.measure(Qubit(1), Bit(1)) + builder = CircuitBuilder(qubit_register_size=2, bit_register_size=2) + builder.H(Qubit(0)) + builder.CNOT(Qubit(0), Qubit(1)) + builder.measure(Qubit(0), Bit(0)) + builder.measure(Qubit(1), Bit(1)) - return circuit.content + return writer.circuit_to_string(builder.to_circuit()) def execute(qi: QuantumInterface) -> None: @@ -58,17 +55,3 @@ def finalize(list_of_measurements: Dict[int, List[Any]]) -> Dict[str, Any]: """ print(list_of_measurements) return {"results": list_of_measurements} - - -if __name__ == "__main__": - # Run the individual steps for debugging - print("=== Circuit ===\n", generate_circuit()) - - algorithm = HybridAlgorithm("test", "test") - algorithm.read_file(Path(__file__)) - - local_backend = LocalBackend() - job_id = local_backend.run(algorithm, 0) - results = local_backend.get_results(job_id) - - print("=== Execute ===\n", results) diff --git a/examples/hydrogen_qiskit.py b/examples/hydrogen_qiskit.py deleted file mode 100644 index 44fc57e7..00000000 --- a/examples/hydrogen_qiskit.py +++ /dev/null @@ -1,61 +0,0 @@ -# (H2) Hydrogen molecuole ground state energy determined using VQE with a UCCSD-ansatz function. -# Compared with Hartee-Fock energies and with energies calculated by NumPyMinimumEigensolver -# This script is based on the Qiskit Chemistry tutorials -import warnings -from dataclasses import dataclass -from typing import Any, Dict, List - -from qiskit.primitives import BackendEstimator -from qiskit_algorithms import NumPyMinimumEigensolverResult, VQEResult -from qiskit_algorithms.minimum_eigensolvers import VQE -from qiskit_algorithms.optimizers import COBYLA -from qiskit_nature.second_q.circuit.library import UCCSD, HartreeFock -from qiskit_nature.second_q.drivers import PySCFDriver -from qiskit_nature.second_q.mappers import ParityMapper - -from quantuminspire.sdk.qiskit.backend import QuantumInspireBackend -from quantuminspire.util.api.quantum_interface import QuantumInterface - - -@dataclass -class _GroundStateEnergyResults: - result: VQEResult | NumPyMinimumEigensolverResult - nuclear_repulsion_energy: float - - -def calculate_H0(backend: QuantumInspireBackend, distance: float = 0.735) -> _GroundStateEnergyResults: - - mapper = ParityMapper(num_particles=(1, 1)) - molecule = f"H 0.0 0.0 0.0; H 0.0 0.0 {distance}" - driver = PySCFDriver(molecule) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=UserWarning) - es_problem = driver.run() - - fermionic_op = es_problem.hamiltonian.second_q_op() - qubit_op = mapper.map(fermionic_op) - n_particles = es_problem.num_particles - n_spatial_orbitals = es_problem.num_spatial_orbitals - - nuclear_repulsion_energy = es_problem.nuclear_repulsion_energy - - initial_state = HartreeFock(n_spatial_orbitals, n_particles, mapper) - ansatz = UCCSD(n_spatial_orbitals, n_particles, mapper, initial_state=initial_state) - - optimizer = COBYLA(maxiter=1) # 10 iterations take two minutes - estimator = BackendEstimator(backend=backend) - - algo = VQE(estimator, ansatz, optimizer) - result = algo.compute_minimum_eigenvalue(qubit_op) - - print(f"{distance=}: nuclear_repulsion_energy={nuclear_repulsion_energy}, eigenvalue={result.eigenvalue}") - return _GroundStateEnergyResults(result, nuclear_repulsion_energy) - - -def execute(qi: QuantumInterface) -> None: - c = calculate_H0(backend=QuantumInspireBackend(qi)) - print(c) - - -def finalize(list_of_measurements: Dict[int, List[Any]]) -> Dict[str, Any]: - return {"measurements": list_of_measurements} diff --git a/examples/qaoa.py b/examples/qaoa.py index b9b33703..513135f1 100644 --- a/examples/qaoa.py +++ b/examples/qaoa.py @@ -9,11 +9,11 @@ import networkx as nx import numpy as np from networkx import Graph +from opensquirrel.circuit_builder import CircuitBuilder from opensquirrel.ir import Bit, Float, Qubit +from opensquirrel.writer import writer from scipy.optimize import Bounds, minimize -from quantuminspire.sdk.models.circuit import Circuit - MATRIX = np.matrix([[0, 1], [1, 0]]) GRAPH = nx.from_numpy_array(MATRIX) positions = nx.circular_layout(GRAPH) @@ -74,23 +74,23 @@ def qaoa_circuit(graph: Graph, beta: np.ndarray, gamma: np.ndarray) -> str: Returns: cQASM string representing the quantum circuit used to compute the energies in the QAOA algorithm. """ - with Circuit(platform_name="spin-2", program_name="qaoa", number_of_qubits=graph.number_of_nodes()) as circuit: - for i in graph.nodes: - circuit.ir.H(Qubit(i)) + builder = CircuitBuilder(qubit_register_size=2, bit_register_size=2) + for i in graph.nodes: + builder.H(Qubit(i)) - for i in range(P): - for edge in graph.edges(): - circuit.ir.CNOT(Qubit(edge[0]), Qubit(edge[1])) - circuit.ir.Rz(Qubit(edge[1]), Float(2 * gamma[i])) - circuit.ir.CNOT(Qubit(edge[0]), Qubit(edge[1])) + for i in range(P): + for edge in graph.edges(): + builder.CNOT(Qubit(edge[0]), Qubit(edge[1])) + builder.Rz(Qubit(edge[1]), Float(2 * gamma[i])) + builder.CNOT(Qubit(edge[0]), Qubit(edge[1])) - for j in graph.nodes(): - circuit.ir.Rx(Qubit(j), Float(2 * beta[i])) + for j in graph.nodes(): + builder.Rx(Qubit(j), Float(2 * beta[i])) - for i in graph.nodes: - circuit.ir.measure(Qubit(i), Bit(i)) + for i in graph.nodes: + builder.measure(Qubit(i), Bit(i)) - return circuit.content + return writer.circuit_to_string(builder.to_circuit()) def generate_objective_function(qi, graph) -> Callable: diff --git a/examples/simple.py b/examples/simple.py deleted file mode 100755 index 4df11cf5..00000000 --- a/examples/simple.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -import time - -from opensquirrel.ir import Bit, Qubit - -from quantuminspire.sdk.models.circuit import Circuit -from quantuminspire.util.api.remote_backend import RemoteBackend - -if __name__ == "__main__": - - with Circuit(platform_name="spin-2", program_name="prgm1", number_of_qubits=2) as c: - c.ir.X(Qubit(0)) - c.ir.H(Qubit(1)) - c.ir.measure(Qubit(0), Bit(0)) - c.ir.measure(Qubit(1), Bit(1)) - - backend = RemoteBackend() - - startTime = time.time() - job_id = backend.run(c, backend_type_id=3, number_of_shots=1024) - executionTime = time.time() - startTime - print(f"Execution time in seconds: {executionTime}") - print(f"job_id: {job_id}") diff --git a/examples/unfair_dice.py b/examples/unfair_dice.py index fc690151..d584ccb6 100644 --- a/examples/unfair_dice.py +++ b/examples/unfair_dice.py @@ -23,12 +23,12 @@ from typing import Any, Dict, List import numpy as np +from opensquirrel.circuit_builder import CircuitBuilder from opensquirrel.ir import Bit, Float, Qubit +from opensquirrel.writer import writer +from qi2_shared.hybrid.quantum_interface import ExecuteCircuitResult, QuantumInterface from qiskit_algorithms.optimizers import SPSA -from quantuminspire.sdk.models.circuit import Circuit -from quantuminspire.util.api.quantum_interface import ExecuteCircuitResult, QuantumInterface - def counts_to_distr(counts: Dict[str, int]) -> dict[int, float]: """Convert Qiskit result counts to a dictionary. @@ -105,7 +105,7 @@ def __call__(self, nfev, parameters, value, update, accepted) -> bool: dt = AverageDecreaseTermination(N=35) -def U(circuit_ir, q: Qubit, theta: float, phi: float, lamb: float): +def U(builder: CircuitBuilder, q: Qubit, theta: float, phi: float, lamb: float): """McKay decomposition of the U gate. :param self: circuit object @@ -115,31 +115,31 @@ def U(circuit_ir, q: Qubit, theta: float, phi: float, lamb: float): :param lamb: angle :return: circuit object """ - circuit_ir.Rz(q, Float(phi)) - circuit_ir.Rx(q, Float(-np.pi / 2)) - circuit_ir.Rz(q, Float(theta)) - circuit_ir.Rx(q, Float(np.pi / 2)) - circuit_ir.Rz(q, Float(lamb)) - return circuit_ir + builder.Rz(q, Float(phi)) + builder.Rx(q, Float(-np.pi / 2)) + builder.Rz(q, Float(theta)) + builder.Rx(q, Float(np.pi / 2)) + builder.Rz(q, Float(lamb)) + return builder -def generate_ansatz(params: List[Any]): - with Circuit(platform_name="spin-2", program_name="prgm1", number_of_qubits=2) as circuit: - U(circuit.ir, Qubit(0), *params[0:3]) - U(circuit.ir, Qubit(1), *params[3:6]) - circuit.ir.CZ(Qubit(0), Qubit(1)) - U(circuit.ir, Qubit(0), *params[6:9]) - U(circuit.ir, Qubit(1), *params[9:12]) - for ii in range(number_of_qubits): - circuit.ir.measure(Qubit(ii), Bit(ii)) +def generate_ansatz(params: List[Any]) -> str: + builder = CircuitBuilder(qubit_register_size=number_of_qubits, bit_register_size=number_of_qubits) + U(builder, Qubit(0), *params[0:3]) + U(builder, Qubit(1), *params[3:6]) + builder.CZ(Qubit(0), Qubit(1)) + U(builder, Qubit(0), *params[6:9]) + U(builder, Qubit(1), *params[9:12]) + for ii in range(number_of_qubits): + builder.measure(Qubit(ii), Bit(ii)) - return circuit + return writer.circuit_to_string(builder.to_circuit()) def objective_function(params: List[Any], qi: QuantumInterface, target_distribution: Dict[int, float], nshots=None): """Compares the output distribution of our circuit with parameters `params` to the target distribution.""" - qc = generate_ansatz(params) - execute_result = qi.execute_circuit(qc.content, nshots) + cqasm = generate_ansatz(params) + execute_result = qi.execute_circuit(cqasm, nshots) # Convert the result to a dictionary with probabilities output_distr = counts_to_distr(execute_result.results) # Calculate the cost as the distance between the output diff --git a/quantuminspire/sdk/qiskit/__init__.py b/quantuminspire/sdk/qiskit/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/quantuminspire/sdk/qiskit/backend.py b/quantuminspire/sdk/qiskit/backend.py deleted file mode 100644 index 82d1e3af..00000000 --- a/quantuminspire/sdk/qiskit/backend.py +++ /dev/null @@ -1,273 +0,0 @@ -""" -This code is based in part on: - https://github.com/QuTech-Delft/quantuminspire/tree/dev/src/quantuminspire/qiskit - - Quantum Inspire SDK - - Copyright 2022 QuTech Delft - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -""" - -import copy -import io -import uuid -from typing import Any, Dict, List, Optional, Union - -from qiskit.circuit import QuantumCircuit -from qiskit.compiler import assemble -from qiskit.providers import BackendV1 as Backend, JobV1 as Job, Options -from qiskit.providers.jobstatus import JobStatus -from qiskit.providers.models import QasmBackendConfiguration -from qiskit.providers.models.backendconfiguration import GateConfig -from qiskit.providers.models.backendstatus import BackendStatus -from qiskit.qobj import QasmQobjExperiment -from qiskit.result.models import ExperimentResult, ExperimentResultData -from qiskit.result.result import Result - -from quantuminspire.sdk.qiskit.circuit_parser import CircuitToString -from quantuminspire.sdk.qiskit.exceptions import QiskitBackendError -from quantuminspire.sdk.qiskit.measurements import Measurements -from quantuminspire.util.api.quantum_interface import ExecuteCircuitResult, QuantumInterface - - -class QiskitQuantumInspireJob(Job): # type: ignore - """Qiskit Job for Quantum Inspire.""" - - def __init__(self, qi: QuantumInterface, backend: Optional[Backend], job_id: str, **kwargs: Any) -> None: - super().__init__(backend, job_id, **kwargs) - self.experiments = None - self.__results: List[ExecuteCircuitResult] = [] - del qi - - def submit(self) -> None: - pass - - def result(self) -> Result: - - job_results = [] - for circuit_result in self.__results: - - exp_data = ExperimentResultData(counts=circuit_result.results, snapshots=circuit_result.results) - - exp = ExperimentResult(circuit_result.shots_done, circuit_result.shots_done > 0, exp_data) - - job_results.append(exp) - - return Result("QuantumInspireBackend", "2.0", 0, 0, len(job_results) > 0, job_results, None, status="COMPLETED") - - def status(self) -> JobStatus: - return JobStatus.DONE - - def add_result(self, result: ExecuteCircuitResult) -> None: - """Add a run result to the job.""" - self.__results.append(result) - - -class QuantumInspireBackend(Backend): # type: ignore - """Qiskit backend definition for Quantum Inspire.""" - - DEFAULT_CONFIGURATION = QasmBackendConfiguration( - backend_name="quantum_inspire_2", - backend_version="2.0", - n_qubits=6, - basis_gates=[ - "x", - "y", - "z", - "h", - "rx", - "ry", - "rz", - "s", - "sdg", - "t", - "tdg", - "cx", - "ccx", - "p", - "u", - "id", - "swap", - "cz", - "snapshot", - "delay", - "barrier", - "reset", - ], - gates=[GateConfig(name="NotUsed", parameters=["NaN"], qasm_def="NaN")], - local=False, - simulator=True, - conditional=True, - open_pulse=False, - memory=True, - max_shots=1024, - max_experiments=1, - coupling_map=[[0, 1], [0, 2], [1, 3], [2, 3], [3, 4], [4, 5]], - multiple_measurements=False, - parallel_computing=False, - ) - qobj_warning_issued = False - - def __init__(self, qi: QuantumInterface, configuration: Optional[QasmBackendConfiguration] = None) -> None: - - super().__init__(configuration=(configuration or QuantumInspireBackend.DEFAULT_CONFIGURATION), provider=None) - self.__qi: QuantumInterface = qi - - @classmethod - def _default_options(cls) -> Options: - """Returns default runtime options. - - Only the options that are relevant to Quantum Inspire backends are added. - """ - return Options(shots=1024, memory=True) - - def _get_run_config(self, **kwargs: Any) -> Dict[str, Any]: - """Return the consolidated runtime configuration. Run arguments overwrite the values of the default runtime - options. Run arguments (not None) that are not defined as options are added to the runtime configuration. - - :param kwargs: The runtime arguments (arguments given with the run method). - :return: A dictionary of runtime arguments for the run. - """ - run_config_dict: Dict[str, Any] = copy.copy(self.options.__dict__) - for key, val in kwargs.items(): - if val is not None: - run_config_dict[key] = val - return run_config_dict - - @property - def backend_name(self) -> str: - """Return backend name.""" - return self.name() # type: ignore - - def run( # pylint: disable=too-many-locals - self, - run_input: Union[QuantumCircuit, List[QuantumCircuit]], - shots: Optional[int] = None, - memory: Optional[bool] = None, - allow_fsp: bool = True, - **run_config: Dict[str, Any], - ) -> QiskitQuantumInspireJob: - run_config_dict = self._get_run_config(shots=shots, memory=memory, **run_config) - - qobj = assemble(run_input, self, **run_config_dict) - number_of_shots = qobj.config.shots - self.__validate_number_of_shots(number_of_shots) - - identifier = uuid.uuid1() - job_id = f"qi-sdk-project-{identifier}" - - experiments = qobj.experiments - job = QiskitQuantumInspireJob(self.__qi, self, job_id) - for experiment in experiments: - measurements = Measurements.from_experiment(experiment) - if Backend.configuration(self).conditional: - self.__validate_nr_of_clbits_conditional_gates(experiment) - - measurements.validate_unsupported_measurements() - result = self._submit_experiment(experiment, number_of_shots, measurements, allow_fsp) - job.add_result(result) - - job.experiments = experiments - return job - - def status(self) -> BackendStatus: - """Return the backend status. Pending jobs is always 0. This information is currently not known. - - Returns: - BackendStatus: the status of the backend. Pending jobs is always 0. - """ - return BackendStatus( - backend_name=self.name(), - backend_version="2.0", - operational=True, - pending_jobs=0, - status_msg="online", - ) - - def _generate_cqasm( - self, experiment: QasmQobjExperiment, measurements: Measurements, full_state_projection: bool = True - ) -> str: - """Generates the cQASM from the Qiskit experiment. - - :param experiment: The experiment that contains instructions to be converted to cQASM. - :param measurements: The measurement instance containing measurement information and measurement functionality. - :raises QiskitBackendError: If a Qiskit instruction is not in the basis gates set of Quantum Inspire backend. - :return: The cQASM code that can be sent to the Quantum Inspire API. - """ - parser = CircuitToString(Backend.configuration(self).basis_gates, measurements, full_state_projection) - number_of_qubits = experiment.header.n_qubits - instructions = experiment.instructions - with io.StringIO() as stream: - stream.write("version 3.0\n") - stream.write("// cQASM generated by QI backend for Qiskit\n") - stream.write(f"qubit[{number_of_qubits}] q\n") - - for instruction in instructions: - parser.parse(stream, instruction) - return stream.getvalue() - - def _submit_experiment( - self, - experiment: QasmQobjExperiment, - number_of_shots: int, - measurements: Measurements, - allow_fsp: bool = True, - ) -> ExecuteCircuitResult: - compiled_qasm = self._generate_cqasm(experiment, measurements, allow_fsp) - - result = self.__qi.execute_circuit(compiled_qasm, number_of_shots) - return result - - def __validate_number_of_shots(self, number_of_shots: int) -> None: - """Checks whether the number of shots has a valid value. - - :param number_of_shots: The number of shots to check. - :raises QiskitBackendError: When the value is not correct. - """ - if number_of_shots < 1 or number_of_shots > self.configuration().max_shots: - raise QiskitBackendError(f"Invalid shots (number_of_shots={number_of_shots})") - - def __validate_nr_of_clbits_conditional_gates(self, experiment: QasmQobjExperiment) -> None: - """Validate the number of classical bits in the algorithm when conditional gates are used. - - 1. When binary controlled gates are used and the number of classical registers - is greater than the number of qubits an error is raised. - - When using binary controlled gates in Qiskit, we can have something like: - - .. code:: - - q = QuantumRegister(2) - c = ClassicalRegister(4) - circuit = QuantumCircuit(q, c) - circuit.h(q[0]).c_if(c, 15) - - Because cQASM has the same number of classical registers as qubits (2 in this case), - this circuit cannot be translated to valid cQASM. - - :param experiment: The experiment with gate operations and header. - - :raises QiskitBackendError: When the value is not correct. - """ - header = experiment.header - number_of_qubits = header.n_qubits - number_of_clbits = header.memory_slots - - if number_of_clbits > number_of_qubits: - if any(hasattr(instruction, "conditional") for instruction in experiment.instructions): - # no problem when there are no conditional gate operations - raise QiskitBackendError( - "Number of classical bits must be less than or equal to the" - " number of qubits when using conditional gate operations" - ) diff --git a/quantuminspire/sdk/qiskit/circuit_parser.py b/quantuminspire/sdk/qiskit/circuit_parser.py deleted file mode 100644 index c1b51635..00000000 --- a/quantuminspire/sdk/qiskit/circuit_parser.py +++ /dev/null @@ -1,694 +0,0 @@ -"""Quantum Inspire SDK. - -Copyright 2022 QuTech Delft - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the -License. You may obtain a copy of the License at -https://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import copy -from io import StringIO -from typing import List, Optional, Tuple - -from qiskit.qobj import QasmQobjInstruction - -from quantuminspire.sdk.qiskit.exceptions import CircuitError -from quantuminspire.sdk.qiskit.measurements import Measurements - - -class CircuitToString: - """Contains the translational elements to convert the Qiskit circuits to cQASM code.""" - - def __init__(self, basis_gates: List[str], measurements: Measurements, full_state_projection: bool = False) -> None: - """ - :param basis_gates: List of basis gates from the configuration. - :param measurements: The measured qubits/classical bits and the number of qubits and classical bits. - """ - self.basis_gates = basis_gates.copy() - if len(self.basis_gates) > 0: - self.basis_gates.append("measure") - self.bfunc_instructions: List[QasmQobjInstruction] = [] - self.measurements = measurements - self.full_state_projection = full_state_projection - - @staticmethod - def _gate_not_supported( - _stream: StringIO, instruction: QasmQobjInstruction, _binary_control: Optional[str] = None - ) -> None: - """Called when a gate is not supported with the backend. Throws an exception (ApiError) - - :param instruction: The Qiskit instruction to translate to cQASM. - :raises ApiError: the gate is not supported by the circuit parser. - """ - if hasattr(instruction, "conditional"): - raise CircuitError(f"Conditional gate 'c-{instruction.name.lower()}' not supported") - - raise CircuitError(f"Gate '{instruction.name.lower()}' not supported") - - @staticmethod - def _cz(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the controlled Z element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"CZ q[{instruction.qubits[0]}], " f"q[{instruction.qubits[1]}]\n") - - @staticmethod - def _c_cz(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled controlled Z element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - :param binary_control: The multi-bits control string. The gate is executed when all specified classical bits - are 1. - """ - stream.write(f"C-CZ {binary_control}q[{instruction.qubits[0]}], " f"q[{instruction.qubits[1]}]\n") - - @staticmethod - def _cx(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the controlled X element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"CNOT q[{instruction.qubits[0]}], " f"q[{instruction.qubits[1]}]\n") - - @staticmethod - def _c_cx(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled controlled X element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - :param binary_control: The multi-bits control string. The gate is executed when all specified classical bits - are 1. - """ - stream.write(f"C-CNOT {binary_control}q[{instruction.qubits[0]}], " f"q[{instruction.qubits[1]}]\n") - - @staticmethod - def _ccx(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the Toffoli element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write( - f"Toffoli q[{instruction.qubits[0]}], " f"q[{instruction.qubits[1]}], q[{instruction.qubits[2]}]\n" - ) - - @staticmethod - def _c_ccx(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary controlled Toffoli element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - :param binary_control: The multi-bits control string. The gate is executed when all specified classical bits - are 1. - """ - stream.write( - f"C-Toffoli {binary_control}q[{instruction.qubits[0]}], q[{instruction.qubits[1]}], " - f"q[{instruction.qubits[2]}]\n" - ) - - @staticmethod - def _h(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the H element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"H q[{instruction.qubits[0]}]\n") - - @staticmethod - def _c_h(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled H element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - :param binary_control: The multi-bits control string. The gate is executed when all specified classical bits - are 1. - """ - stream.write(f"C-H {binary_control}q[{instruction.qubits[0]}]\n") - - @staticmethod - def _id(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the ID element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"I q[{instruction.qubits[0]}]\n") - - @staticmethod - def _c_id(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled ID element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - :param binary_control: The multi-bits control string. The gate is executed when all specified classical bits - are 1. - """ - stream.write(f"C-I {binary_control}q[{instruction.qubits[0]}]\n") - - @staticmethod - def _s(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the S element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"S q[{instruction.qubits[0]}]\n") - - @staticmethod - def _c_s(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled S element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"C-S {binary_control}q[{instruction.qubits[0]}]\n") - - @staticmethod - def _sdg(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the Sdag element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"Sdag q[{instruction.qubits[0]}]\n") - - @staticmethod - def _c_sdg(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled Sdag element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"C-Sdag {binary_control}q[{instruction.qubits[0]}]\n") - - @staticmethod - def _swap(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the SWAP element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"SWAP q[{instruction.qubits[0]}], q[{instruction.qubits[1]}]\n") - - @staticmethod - def _c_swap(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled SWAP element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"C-SWAP {binary_control}q[{instruction.qubits[0]}], " f"q[{instruction.qubits[1]}]\n") - - @staticmethod - def _t(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the T element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"T q[{instruction.qubits[0]}]\n") - - @staticmethod - def _c_t(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled T element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"C-T {binary_control}q[{instruction.qubits[0]}]\n") - - @staticmethod - def _tdg(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the Tdag element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"Tdag q[{instruction.qubits[0]}]\n") - - @staticmethod - def _c_tdg(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled Tdag element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"C-Tdag {binary_control}q[{instruction.qubits[0]}]\n") - - @staticmethod - def _x(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the X element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"X q[{instruction.qubits[0]}]\n") - - @staticmethod - def _c_x(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled X element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"C-X {binary_control}q[{instruction.qubits[0]}]\n") - - @staticmethod - def _y(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the Y element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"Y q[{instruction.qubits[0]}]\n") - - @staticmethod - def _c_y(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled Y element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - :param binary_control: The multi-bits control string. The gate is executed when all specified classical bits - are 1. - """ - stream.write(f"C-Y {binary_control}q[{instruction.qubits[0]}]\n") - - @staticmethod - def _z(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the Z element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"Z q[{instruction.qubits[0]}]\n") - - @staticmethod - def _c_z(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled Z element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - :param binary_control: The multi-bits control string. The gate is executed when all specified classical bits - are 1. - """ - stream.write(f"C-Z {binary_control}q[{instruction.qubits[0]}]\n") - - @staticmethod - def _r(stream: StringIO, instruction: QasmQobjInstruction, axis: str) -> None: - """Translates the Rotation element for an axis (x,y,z). - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - :param axis: The axis for which the Rotation operator is parsed ('x', 'y' or 'z'). - """ - angle_q0 = float(instruction.params[0]) - stream.write(f"R{axis} q[{instruction.qubits[0]}], {angle_q0:.6f}\n") - - @staticmethod - def _c_r(stream: StringIO, instruction: QasmQobjInstruction, axis: str, binary_control: str) -> None: - """Translates the binary-controlled Rotation element for an axis (x,y,z). - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - :param axis: The axis for which the Rotation operator is parsed ('x', 'y' or 'z'). - :param binary_control: The multi-bits control string. The gate is executed when all specified classical bits - are 1. - """ - angle_q0 = float(instruction.params[0]) - stream.write(f"C-R{axis} {binary_control}q[{instruction.qubits[0]}], {angle_q0:.6f}\n") - - @staticmethod - def _rx(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the Rx element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - CircuitToString._r(stream, instruction, "x") - - @staticmethod - def _c_rx(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled Rx element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - :param binary_control: The multi-bits control string. The gate is executed when all specified classical bits - are 1. - """ - CircuitToString._c_r(stream, instruction, "x", binary_control) - - @staticmethod - def _ry(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the Ry element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - CircuitToString._r(stream, instruction, "y") - - @staticmethod - def _c_ry(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled Ry element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - :param binary_control: The multi-bits control string. The gate is executed when all specified classical bits - are 1. - """ - CircuitToString._c_r(stream, instruction, "y", binary_control) - - @staticmethod - def _rz(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the Rz element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - CircuitToString._r(stream, instruction, "z") - - @staticmethod - def _c_rz(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled Rz element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - :param binary_control: The multi-bits control string. The gate is executed when all specified classical bits - are 1. - """ - CircuitToString._c_r(stream, instruction, "z", binary_control) - - @staticmethod - def _u(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the U element to U3. - - The u element is used by Qiskit for the u_base gate and when a u0-gate is used in the circuit but not supported - as a basis gate for the backend. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - CircuitToString._u3(stream, instruction) - - @staticmethod - def _c_u(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled U element to binary-controlled U3. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - :param binary_control: The multi-bits control string. The gate is executed when all specified classical bits - are 1. - """ - CircuitToString._c_u3(stream, instruction, binary_control) - - @staticmethod - def _u1(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the U1(lambda) element to U3(0, 0, lambda). - - A copy of the circuit is made to prevent side effects for the caller. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - temp_instruction = copy.deepcopy(instruction) - temp_instruction.params[0:0] = (0, 0) - CircuitToString._u3(stream, temp_instruction) - - @staticmethod - def _c_u1(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled U1(lambda) element to U3(0, 0, lambda). - - A copy of the circuit is made to prevent side effects for the caller. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - :param binary_control: The multi-bits control string. The gate is executed when all specified classical bits - are 1. - """ - temp_instruction = copy.deepcopy(instruction) - temp_instruction.params[0:0] = (0, 0) - CircuitToString._c_u3(stream, temp_instruction, binary_control) - - @staticmethod - def _p(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the p element to u1. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - CircuitToString._u1(stream, instruction) - - @staticmethod - def _c_p(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the c-p element to c-u1. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - :param binary_control: The multi-bits control string. The gate is executed when all specified classical bits - are 1. - """ - CircuitToString._c_u1(stream, instruction, binary_control) - - @staticmethod - def _u3(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the U3(theta, phi, lambda) element to 3 rotation gates. - - Any single qubit operation (a 2x2 unitary matrix) can be written as the product of rotations. - As an example, a unitary single-qubit gate can be expressed as a combination of - Rz and Ry rotations (Nielsen and Chuang, 10th edition, section 4.2). - U(theta, phi, lambda) = Rz(phi)Ry(theta)Rz(lambda). - Note: The expression above is the matrix multiplication, when implementing this in a gate circuit, - the gates need to be executed in reversed order. - Any rotation of 0 radials is left out of the resulting circuit. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - gates = ["Rz", "Ry", "Rz"] - angles = list(float(instruction.params[i]) for i in [2, 0, 1]) - index_q0 = [instruction.qubits[0]] * 3 - for triplet in zip(gates, index_q0, angles): - if triplet[2] != 0: - stream.write(f"{triplet[0]} q[{triplet[1]}], {triplet[2]:.6f}\n") - - @staticmethod - def _c_u3(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled U3(theta, phi, lambda) element to 3 rotation gates. - - See gate :meth:`~._u3` for more information. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - :param binary_control: The multi-bits control string. The gate is executed when all specified classical bits - are 1. - """ - gates = ["C-Rz", "C-Ry", "C-Rz"] - binary_controls = [binary_control] * 3 - angles = list(float(instruction.params[i]) for i in [2, 0, 1]) - index_q0 = [instruction.qubits[0]] * 3 - for quadruplets in zip(gates, binary_controls, index_q0, angles): - if quadruplets[3] != 0: - stream.write(f"{quadruplets[0]} {quadruplets[1]}q[{quadruplets[2]}], {quadruplets[3]:.6f}\n") - - @staticmethod - def _barrier(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the | element for a variable number of qubits. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"barrier q[{','.join(map(str, instruction.qubits))}]\n") - - @staticmethod - def _c_barrier(stream: StringIO, instruction: QasmQobjInstruction, binary_control: str) -> None: - """Translates the binary-controlled | element. No cQASM is added for this gate. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - :param binary_control: The multi-bits control string. The gate is executed when all specified classical bits - are 1. - """ - - def _reset(self, stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the reset element. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - stream.write(f"prep_z q[{instruction.qubits[0]}]\n") - - @staticmethod - def _delay(stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the delay element for a qubit. In cQASM wait parameter is int and the unit is hardware cycles. - Only the Qiskit default unit "dt" will work correctly with cQASM, i.e. integer time unit depending on the - target backend. - - In qiskit/circuit/delay.py multiple units are defined for delay instruction. In qiskit/circuit/instruction.py - assemble() method the unit of the delay instruction is not passed. Only the parameter (which is the value of - the delay instruction) is taken. Here we cannot convert delays originally in another unit than dt to dt, which - is the unit for wait in cQASM. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - wait_period = int(instruction.params[0]) - stream.write(f"wait q[{instruction.qubits[0]}], {wait_period}\n") - - def _measure(self, stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Translates the measure element. No cQASM is added for this gate when FSP is used. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - if not self.full_state_projection: - stream.write(f"measure q[{instruction.qubits[0]}]\n") - - @staticmethod - def get_mask_data(mask: int) -> Tuple[int, int]: - """Get mask data. - - A mask is a continuous set of 1-bits with a certain length. This method returns the lowest bit of - the mask and the length of the mask. - - Examples: - - ============ ==================================== - ``76543210`` bit_nr - ============ ==================================== - ``00111000`` lowest mask bit = 3, mask_length = 3 - ``00000001`` lowest mask bit = 0, mask_length = 1 - ``11111111`` lowest mask bit = 0, mask_length = 8 - ``10000000`` lowest mask bit = 7, mask_length = 1 - ============ ==================================== - - :param mask: The mask to get the mask data from. - - :return: The mask data, i.e. a tuple (lowest_bit_number, mask_length) - """ - # Precondition: mask != 0 - if mask == 0: - return -1, 0 - mask_length = 0 - bit_value = 1 - bit_nr = 0 - while not mask & bit_value: - bit_value <<= 1 - bit_nr += 1 - lowest_mask_bit = bit_nr - while mask & bit_value: - mask_length += 1 - bit_value <<= 1 - return lowest_mask_bit, mask_length - - def _parse_bin_ctrl_gate( # pylint: disable=too-many-locals - self, stream: StringIO, instruction: QasmQobjInstruction - ) -> None: - """Parses a binary controlled gate. - - A binary controlled gate name is preceded by 'c-'. - The gate is executed when a specific measurement is true. Multiple measurement outcomes are used - to control the quantum operation. This measurement is a combination of classical bits being 1 and others - being 0. Because cQASM only supports measurement outcomes of 1, any other bits in the - masked bit pattern first have to be inverted with the not-operator. The same inversion also has to - take place after the binary controlled quantum operation. - The mask can be one or more bits and start at any bit depending on the instruction and the declaration - of classical bits. - The resulting stream will be expanded with something like: - not b[the 0-bits in the value relative to the mask changed to 1] - c-gate [classical bits in the mask], other arguments - not b[the 0-bits reset to 0 again] - When the c-gate results in an empty string (e.g. binary controlled u(0, 0, 0) or barrier gate), - nothing is added to the stream. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - conditional_reg_idx = instruction.conditional - conditional = next((x for x in self.bfunc_instructions if x.register == conditional_reg_idx), None) - if conditional is None: - raise CircuitError(f"Conditional not found: reg_idx = {conditional_reg_idx}") - self.bfunc_instructions.remove(conditional) - - conditional_type = conditional.relation - if conditional_type != "==": - raise CircuitError(f"Conditional statement with relation {conditional_type} not supported") - mask = int(conditional.mask, 16) - if mask == 0: - raise CircuitError(f"Conditional statement {instruction.name.lower()} without a mask") - lowest_mask_bit, mask_length = self.get_mask_data(mask) - val = int(conditional.val, 16) - masked_val = mask & val - - # form the negation to the 0-values of the measurement registers, when value == mask no bits are negated - negate_zeroes_line = "" - if masked_val != mask: - negate_zeroes_line = ( - "not b[" - + ",".join( - str(self.measurements.get_qreg_for_conditional_creg(i)) - for i in range(lowest_mask_bit, lowest_mask_bit + mask_length) - if not (masked_val & (1 << i)) - ) - + "]\n" - ) - - if mask_length == 1: - binary_control = f"b[{self.measurements.get_qreg_for_conditional_creg(lowest_mask_bit)}], " - else: - # form multi bits control - qasm-single-gate-multiple-qubits - binary_control = ( - "b[" - + ",".join( - str(self.measurements.get_qreg_for_conditional_creg(i)) - for i in range(lowest_mask_bit, lowest_mask_bit + mask_length) - ) - + "], " - ) - - with StringIO() as gate_stream: - # add the gate - gate_name = f"_c_{instruction.name.lower()}" - gate_function = getattr(self, gate_name, getattr(self, "_gate_not_supported")) - gate_function(gate_stream, instruction, binary_control) - line = gate_stream.getvalue() - if len(line) != 0: - # negate the measurement registers that has to be 0 - stream.write(negate_zeroes_line) - stream.write(line) - # reverse the measurement registers that had to be 0 - stream.write(negate_zeroes_line) - - def parse(self, stream: StringIO, instruction: QasmQobjInstruction) -> None: - """Parses a gate. - - For each type of gate a separate (private) parsing method is defined and called. The resulting cQASM code is - written to the stream. When the gate is a binary controlled gate, Qiskit uses two instructions to handle it. - The first instruction is a so-called bfunc with the conditional information (mask, value to check etc.) which - is stored for later use. The next instruction is the actual gate which must be executed conditionally. The - parsing is forwarded to method _parse_bin_ctrl_gate which reads the earlier stored bfunc. When a gate is not - supported _gate_not_supported is called which raises an exception. - - :param stream: The string-io stream to where the resulting cQASM is written. - :param instruction: The Qiskit instruction to translate to cQASM. - """ - if instruction.name == "bfunc": - self.bfunc_instructions.append(instruction) - elif len(self.basis_gates) > 0 and instruction.name.lower() not in self.basis_gates: - self._gate_not_supported(stream, instruction) - elif hasattr(instruction, "conditional"): - self._parse_bin_ctrl_gate(stream, instruction) - else: - gate_name = f"_{instruction.name.lower()}" - gate_function = getattr(self, gate_name, getattr(self, "_gate_not_supported")) - gate_function(stream, instruction) diff --git a/quantuminspire/sdk/qiskit/exceptions.py b/quantuminspire/sdk/qiskit/exceptions.py deleted file mode 100644 index 6e3d59e0..00000000 --- a/quantuminspire/sdk/qiskit/exceptions.py +++ /dev/null @@ -1,9 +0,0 @@ -"""Definition of exceptions for qiskit backend.""" - - -class QiskitBackendError(Exception): - """Qiskit backend errors.""" - - -class CircuitError(Exception): - """Circuit errors.""" diff --git a/quantuminspire/sdk/qiskit/measurements.py b/quantuminspire/sdk/qiskit/measurements.py deleted file mode 100644 index 453a3237..00000000 --- a/quantuminspire/sdk/qiskit/measurements.py +++ /dev/null @@ -1,249 +0,0 @@ -"""Quantum Inspire SDK. - -Copyright 2022 QuTech Delft - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the -License. You may obtain a copy of the License at -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from __future__ import annotations - -from typing import Any, Dict, List - -from qiskit.qobj import QasmQobjExperiment - -from quantuminspire.sdk.qiskit.exceptions import QiskitBackendError - - -class Measurements: # pylint: disable=too-many-instance-attributes - """The Measurements class registers measurements and the translations of measurements of qubits to classical - registers.""" - - def __init__(self) -> None: - """_measurements_reg and _measurements_state are list of lists, for each measurement the lists contains a list - of [qubit_index, classical_bit_index], which represents the measurement of a qubit to a classical bit. - - _measurements_reg contains the bit indices of the qubit and classical bit as used in the algorithm - _measurements_state contains the positional indexes in the resulting qubit_state and classical_state arrays - """ - self._number_of_qubits = 0 - self._number_of_clbits = 0 - self._measurements_reg: List[List[int]] = [] - self._measurements_state: List[List[int]] = [] - - @property - def nr_of_qubits(self) -> int: - """ - :return: Return number of qubits in the algorithm - """ - return self._number_of_qubits - - @nr_of_qubits.setter - def nr_of_qubits(self, nr_of_qubits: int) -> None: - """Setter for _number_of_qubits.""" - self._number_of_qubits = nr_of_qubits - - @property - def nr_of_clbits(self) -> int: - """ - :return: Return number of classical bits in the algorithm - """ - return self._number_of_clbits - - @nr_of_clbits.setter - def nr_of_clbits(self, nr_of_clbits: int) -> None: - """Setter for _number_of_clbits.""" - self._number_of_clbits = nr_of_clbits - - @property - def measurements_reg(self) -> List[List[int]]: - """ - :return: Return the bit indices of the qubit and classical bit as used in the algorithm - """ - return self._measurements_reg - - @measurements_reg.setter - def measurements_reg(self, measurements_reg: List[List[int]]) -> None: - """Setter for _measurements_reg.""" - self._measurements_reg = measurements_reg - - @property - def measurements_state(self) -> List[List[int]]: - """ - :return: Return the positional indexes in the resulting qubit_state and classical_state arrays - """ - return self._measurements_state - - @measurements_state.setter - def measurements_state(self, measurements_state: List[List[int]]) -> None: - """Setter for _measurements_state.""" - self._measurements_state = measurements_state - - @classmethod - def from_experiment(cls, experiment: QasmQobjExperiment) -> Measurements: - """Determines the measured qubits and classical bits for an experiment. - - The full-state measured qubits is returned when no measurements are present in the compiled circuit. - - .. code:: - - q = QuantumRegister(3) - c = ClassicalRegister(3) - circuit = QuantumCircuit(q, c) - - circuit.measure(2, 0) - - for this measurement [2, 0] is added to the list _measurements_reg - for this measurement [0, 2] is added to the list _measurements_state, qubit 2 is located at position 0 - in the resulting state array and can be indexed with [0]. classical 0 is located at position 2 - in the resulting state array and can be indexed with [2]. - - :param experiment: The experiment with gate operations and header. - - :return: Instance of Measurements with collected measurements from experiment - """ - instance = cls() - header = experiment.header - instance.nr_of_qubits = header.n_qubits - instance.nr_of_clbits = header.memory_slots - - for instruction in experiment.instructions: - if instruction.name == "measure": - instance.measurements_reg.append([instruction.qubits[0], instruction.memory[0]]) - instance.measurements_state.append( - [ - instance.nr_of_qubits - 1 - instruction.qubits[0], - instance.nr_of_clbits - 1 - instruction.memory[0], - ] - ) - - if not instance.measurements_reg: - instance.measurements_reg = [[index, index] for index in range(instance.nr_of_qubits)] - instance.measurements_state = instance.measurements_reg - - # do some validations - instance.validate_number_of_clbits() - - return instance - - @property - def max_measurement_index(self) -> int: - """ - :return: Return the highest classical bit that is used as a storage for a qubit measurement - """ - return max(measurement[1] for measurement in self._measurements_reg) - - def get_qreg_for_conditional_creg(self, creg: int) -> int: - """This method returns the qubit register that was measured to be stored in the classical register given. - - 1. Try to find a measurement where the classical bit creg is used as storage -> return the qubit index of - this measurement. - 2. When no measurement is found for this classical bit, we assume the equivalent qubit (same index) is used - First check if a measurement is found for this qubit to another classical register, we raise an error. - This situation is not supported. - - :param creg: The classical register. - - :return: Returns the qubit index for the qubit that is measured to the classical register creg - """ - for q1, c1 in self._measurements_reg: - if c1 == creg: - return q1 - - for q1, c1 in self._measurements_reg: - if q1 == creg: - raise QiskitBackendError( - f"Classical bit {creg} used in a conditional gate is not measured " - f"and the equivalent qubit {q1} is measured to another classical bit {c1}" - ) - - return creg - - def to_dict(self) -> Dict[str, Any]: - """Translate the class instance to a (minimal) dictionary used for interpreting the results from the backend. - - :return: Dict containing the measurement list with positional indexes in the resulting qubit_state and - classical_state and the number of classical bits. - """ - return { - "measurements_state": self._measurements_state, - "measurements_reg": self._measurements_reg, - "number_of_qubits": self._number_of_qubits, - "number_of_clbits": self._number_of_clbits, - } - - @classmethod - def from_dict(cls, measurement_input: Dict[str, Any]) -> Measurements: - """Translate the input dictionary to an instance of class Measurements. - - :param measurement_input: A dictionary with the measurement information. See method to_dict - :return: Instance of Measurements with input from dictionary - """ - instance = cls() - instance.measurements_state = measurement_input["measurements_state"] - instance.measurements_reg = measurement_input["measurements_reg"] - instance.nr_of_qubits = measurement_input["number_of_qubits"] - instance.nr_of_clbits = measurement_input["number_of_clbits"] - - return instance - - def validate_number_of_clbits(self) -> None: - """Validate the (number of) classical bits used in the measurements are valid for the algorithm. - - Checks whether the number of classical bits has a value cQASM can support. - - 1. When number of classical bits is less than 1 an error is raised. - 2. When a classical bit is used in a measurement with an index higher than the number of classical bits an - error is raised. - """ - if self._number_of_clbits < 1: - raise QiskitBackendError(f"Invalid number of classical bits ({self._number_of_clbits})!") - - if self.max_measurement_index >= self._number_of_clbits: - raise QiskitBackendError( - f"Number of classical bits ({self._number_of_clbits}) is not sufficient for " - f"storing the outcomes of the experiment" - ) - - def validate_unsupported_measurements(self) -> None: - """Validate unsupported measurements (for not full state projection algorithms) - - Certain measurements cannot be handled correctly because cQASM isn't as flexible as Qiskit in measuring to - specific classical bits. Therefore some Qiskit constructions are not supported in QI: - - 1. When a quantum register is measured to different classical registers - 2. When a classical register is used for the measurement of more than one quantum register - - :raises QiskitBackendError: When the circuit contains an invalid measurement - """ - for q1, c1 in self._measurements_reg: - for q2, c2 in self._measurements_reg: - if q1 == q2 and c1 != c2: - raise QiskitBackendError( - f"Measurement of qubit {q1} to different classical registers is not " f"supported" - ) - if q1 != q2 and c1 == c2: - raise QiskitBackendError( - f"Measurement of different qubits to the same classical register {c1} " f"is not supported" - ) - - def qubit_to_classical_hex(self, qubit_register: str) -> str: - """This function converts the qubit register data to the hexadecimal representation of the classical state. - - :param qubit_register: The measured value of the qubits represented as int. - :return: The hexadecimal value of the classical state. - """ - qubit_state = f"{int(qubit_register):0{self.nr_of_qubits}b}" - classical_state = ["0"] * self.nr_of_clbits - for q, c in self._measurements_state: - classical_state[c] = qubit_state[q] - classical_state_str = "".join(classical_state) - classical_state_hex = hex(int(classical_state_str, 2)) - return classical_state_hex diff --git a/tests/sdk/qiskit/test_backend.py b/tests/sdk/qiskit/test_backend.py deleted file mode 100644 index d5dd4f8d..00000000 --- a/tests/sdk/qiskit/test_backend.py +++ /dev/null @@ -1,106 +0,0 @@ -"""Quantum Inspire SDK. - -Copyright 2022 QuTech Delft - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the -License. You may obtain a copy of the License at -https://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import unittest -from unittest.mock import Mock - -import pytest -from qiskit.circuit import ClassicalRegister, QuantumCircuit -from qiskit.providers.jobstatus import JobStatus - -from quantuminspire.sdk.qiskit.backend import QiskitQuantumInspireJob, QuantumInspireBackend -from quantuminspire.sdk.qiskit.exceptions import QiskitBackendError - - -class TestQiskitQuantumInspireJob(unittest.TestCase): - - def setUp(self): ... - - def test_create(self): - q = QiskitQuantumInspireJob(Mock(), None, 1) - assert q.status() == JobStatus.DONE - - def test_results(self): - mock_result = Mock() - mock_result.results = [] - mock_result.shots_done = 1024 - q = QiskitQuantumInspireJob(Mock(), None, 1) - q.submit() - q.add_result(mock_result) - assert q.result() - - -class TestBackend: - - @pytest.fixture(scope="function") - def backend(self) -> QuantumInspireBackend: - return QuantumInspireBackend(Mock(), None) - - def test_default_configuration(self, backend): - assert backend.backend_name == "quantum_inspire_2" - assert backend.configuration().backend_version == "2.0" - assert backend.configuration().n_qubits == 6 - backend.set_options(shots=1024) - config = backend._get_run_config(extra="value") - assert config["shots"] == 1024 - assert config["extra"] == "value" - status = backend.status() - assert status.backend_version == "2.0" - assert status.backend_name == "quantum_inspire_2" - assert status.pending_jobs == 0 - assert status.operational - assert status.status_msg == "online" - - def test_run_normal(self, backend): - qc = QuantumCircuit(2, 2) - qc.h(0) - qc.cx(0, 1) - qc.measure([0, 1], [0, 1]) - - job = backend.run(qc) - assert job - - def test_run_normal_not_conditional(self, backend): - backend.configuration().conditional = False - qc = QuantumCircuit(2, 4) - qc.h(0) - qc.cx(0, 1) - qc.measure([0, 1], [0, 1]) - - job = backend.run(qc) - assert job - - def test_invalid_shots(self, backend): - backend.set_options(shots=0) - with pytest.raises(QiskitBackendError): - backend.run(QuantumCircuit(2, 2)) - - def test_invalid_memory_slots(self, backend): - backend.configuration().conditional = True - with pytest.raises(QiskitBackendError): - qc = QuantumCircuit(2, 4) - c = ClassicalRegister(4, "c") - qc.h(1) - qc.cx(0, 1).c_if(c, 1) - qc.measure([0, 1], [0, 1]) - backend.run(qc) - - def test_valid_memory_slots(self, backend): - backend.configuration().conditional = True - qc = QuantumCircuit(2, 4) - qc.h(1) - qc.cx(0, 1) - qc.measure([0, 1], [0, 1]) - backend.run(qc) diff --git a/tests/sdk/qiskit/test_circuit_parser.py b/tests/sdk/qiskit/test_circuit_parser.py deleted file mode 100644 index 608667c8..00000000 --- a/tests/sdk/qiskit/test_circuit_parser.py +++ /dev/null @@ -1,1032 +0,0 @@ -"""Quantum Inspire SDK. - -Copyright 2022 QuTech Delft - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the -License. You may obtain a copy of the License at -https://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import copy -import unittest -from unittest.mock import Mock - -import numpy as np -import qiskit -from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister -from qiskit.circuit import Instruction -from qiskit.compiler import assemble, transpile - -from quantuminspire.sdk.qiskit.backend import QuantumInspireBackend -from quantuminspire.sdk.qiskit.circuit_parser import CircuitToString -from quantuminspire.sdk.qiskit.exceptions import CircuitError -from quantuminspire.sdk.qiskit.measurements import Measurements - - -class TestQiskitToCQasm(unittest.TestCase): - - @staticmethod - def _backend(): - configuration = copy.copy(QuantumInspireBackend.DEFAULT_CONFIGURATION) - configuration.simulator = False - configuration.coupling_map = [[0, 1], [0, 2], [1, 3], [2, 3]] - backend = QuantumInspireBackend(Mock(), configuration) - return backend - - def _bell_circuit(self): - q = QuantumRegister(2, "q") - c = ClassicalRegister(2, "c") - circuit = QuantumCircuit(q, c) - circuit.h(q[0]) # Hadamard gate - circuit.cx(q[0], q[1]) # CNOT gate - circuit.measure(q, c) # Qubit Measurment - return circuit - - def test_generate_cqasm_v1_with_bell_circuit(self): - backend = self._backend() - circuit = self._bell_circuit() - qobj = assemble(circuit, backend, shots=25, memory=True) - experiment = qobj.experiments[0] - measurements = Measurements.from_experiment(experiment) - cqasm_string = backend._generate_cqasm(experiment, measurements, False) - - expected = ( - "version 3.0\n" - "// cQASM generated by QI backend for Qiskit\n" - "qubit[2] q\n" - "H q[0]\n" - "CNOT q[0], q[1]\n" - "measure q[0]\n" - "measure q[1]\n" - ) - self.assertEqual(cqasm_string, expected) - - def test_generate_cqasm_with_bell_circuit(self): - backend = self._backend() - circuit = self._bell_circuit() - qobj = assemble(circuit, backend, shots=25, memory=True) - experiment = qobj.experiments[0] - measurements = Measurements.from_experiment(experiment) - cqasm_string = backend._generate_cqasm(experiment, measurements, False) - - expected = ( - "version 3.0\n" - "// cQASM generated by QI backend for Qiskit\n" - "qubit[2] q\n" - "H q[0]\n" - "CNOT q[0], q[1]\n" - "measure q[0]\n" - "measure q[1]\n" - ) - self.assertEqual(cqasm_string, expected) - - -class TestQiCircuitToString(unittest.TestCase): - - @staticmethod - def _generate_cqasm_from_circuit(circuit, full_state_projection=True, transpile_first=False): - run_config_dict = {"shots": 25, "memory": True} - configuration = copy.copy(QuantumInspireBackend.DEFAULT_CONFIGURATION) - if transpile_first: - # qiskit transpiler expects coupling map - configuration.simulator = False - configuration.coupling_map = [[0, 1], [0, 2], [1, 3], [2, 3]] - backend = QuantumInspireBackend(Mock(), configuration) - if transpile_first: - circuit = transpile(circuit, backend=backend) - qobj = assemble(circuit, backend, **run_config_dict) - experiment = qobj.experiments[0] - measurements = Measurements.from_experiment(experiment) - result = backend._generate_cqasm(experiment, measurements, full_state_projection) - return result - - @staticmethod - def _generate_cqasm_from_instructions(instructions, number_of_qubits=2, full_state_projection=True): - """Needed to create invalid qiskit circuits for triggering Exceptions.""" - experiment_dict = { - "instructions": instructions, - "header": {"n_qubits": number_of_qubits, "memory_slots": number_of_qubits, "compiled_circuit_qasm": ""}, - "config": { - "coupling_map": "all-to-all", - "basis_gates": "x,y,z,h,rx,ry,rz,s,cx,ccx,p,id,snapshot", - "n_qubits": number_of_qubits, - }, - } - experiment = qiskit.qobj.QasmQobjExperiment.from_dict(experiment_dict) - for instruction in experiment.instructions: - if hasattr(instruction, "params"): - # convert params to params used in qiskit instructions - qiskit_instruction = Instruction("dummy", 0, 0, instruction.params) - instruction.params = qiskit_instruction.params - - measurements = Measurements.from_experiment(experiment) - simulator = QuantumInspireBackend(Mock(), None) - result = simulator._generate_cqasm(experiment, measurements, full_state_projection) - return result - - def test_generate_cqasm_with_entangle_algorithm(self): - q = QuantumRegister(2) - b = ClassicalRegister(2) - circuit = QuantumCircuit(q, b) - - circuit.h(q[0]) - circuit.cx(q[0], q[1]) - circuit.measure(q[0], b[0]) - circuit.measure(q[1], b[1]) - - result = self._generate_cqasm_from_circuit(circuit) - - expected = ( - "version 3.0\n" - "// cQASM generated by QI backend for Qiskit\n" - "qubit[2] q\n" - "H q[0]\n" - "CNOT q[0], q[1]\n" - ) - self.assertEqual(result, expected) - - def test_generate_cqasm_correct_output_controlled_z(self): - qc = QuantumCircuit(2, 2) - qc.cz(0, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("CZ q[0], q[1]\n" in result) - - def test_generate_cqasm_correct_output_conditional_controlled_z(self): - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.cz(0, 1).c_if(c, 14) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[0]\nC-CZ b[0,1,2,3], q[0], q[1]\nnot b[0]\n" in result) - - def test_generate_cqasm_correct_output_controlled_not(self): - qc = QuantumCircuit(2, 2) - qc.cx(0, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("CNOT q[0], q[1]\n" in result) - - def test_generate_cqasm_correct_output_conditional_controlled_not(self): - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.cx(0, 1).c_if(c, 14) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[0]\nC-CNOT b[0,1,2,3], q[0], q[1]\nnot b[0]\n" in result) - - def test_generate_cqasm_correct_output_toffoli(self): - qc = QuantumCircuit(3, 3) - qc.ccx(0, 1, 2) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Toffoli q[0], q[1], q[2]\n" in result) - - def test_generate_cqasm_correct_output_conditional_toffoli(self): - q = QuantumRegister(8, "q") - c = ClassicalRegister(8, "c") - qc = QuantumCircuit(q, c, name="test") - qc.ccx(0, 1, 2).c_if(c, 14) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue( - "not b[0,4,5,6,7]\nC-Toffoli b[0,1,2,3,4,5,6,7], q[0], q[1], q[2]\nnot b[0,4,5,6,7]\n" in result - ) - - def test_generate_cqasm_correct_output_measure(self): - qc = QuantumCircuit(2, 2) - qc.measure(0, 0) - result = self._generate_cqasm_from_circuit(qc) - measure_line = "measure q[0]\n" - self.assertTrue(measure_line not in result) - - def test_generate_cqasm_correct_output_measure_q0_non_fsp(self): - qc = QuantumCircuit(2, 2) - qc.measure(0, 0) - result = self._generate_cqasm_from_circuit(qc, False) - measure_line = "measure q[0]\n" - self.assertTrue(measure_line in result) - - def test_generate_cqasm_correct_output_measure_q1_non_fsp(self): - qc = QuantumCircuit(2, 2) - qc.measure(1, 0) - result = self._generate_cqasm_from_circuit(qc, False) - measure_line = "measure q[1]\n" - self.assertTrue(measure_line in result) - - def test_generate_cqasm_correct_output_hadamard(self): - qc = QuantumCircuit(2, 2) - qc.h(0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("H q[0]\n" in result) - - def test_generate_cqasm_correct_output_conditional_hadamard(self): - q = QuantumRegister(8, "q") - c = ClassicalRegister(8, "c") - qc = QuantumCircuit(q, c, name="test") - qc.h(0).c_if(c, 14) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[0,4,5,6,7]\nC-H b[0,1,2,3,4,5,6,7], q[0]\nnot b[0,4,5,6,7]\n" in result) - - def test_generate_cqasm_correct_output_barrier(self): - qc = QuantumCircuit(2, 2) - qc.barrier(0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("barrier q[0]\n" in result) - - def test_generate_cqasm_correct_output_barrier_multiple_qubits(self): - q1 = QuantumRegister(2, "q1") - q2 = QuantumRegister(4, "q2") - c1 = ClassicalRegister(2, "c1") - c2 = ClassicalRegister(4, "c2") - qc = QuantumCircuit(q1, q2, c1, c2, name="test") - - qc.barrier(q1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("barrier q[0,1]\n" in result) - - qc.barrier(q2) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("barrier q[2,3,4,5]\n" in result) - - def test_generate_cqasm_correct_output_barrier_all_qubits(self): - q1 = QuantumRegister(2, "q1") - q2 = QuantumRegister(4, "q2") - c1 = ClassicalRegister(2, "c1") - c2 = ClassicalRegister(4, "c2") - qc = QuantumCircuit(q1, q2, c1, c2, name="test") - - qc.barrier() - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("barrier q[0,1,2,3,4,5]\n" in result) - - def test_generate_cqasm_correct_output_delay_all_qubits(self): - q1 = QuantumRegister(1, "q1") - q2 = QuantumRegister(2, "q2") - c1 = ClassicalRegister(1, "c1") - c2 = ClassicalRegister(2, "c2") - qc = QuantumCircuit(q1, q2, c1, c2, name="test") - - qc.delay(1.0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("wait q[0], 1\n" in result) - self.assertTrue("wait q[1], 1\n" in result) - self.assertTrue("wait q[2], 1\n" in result) - - def test_generate_cqasm_correct_output_delay_qarg(self): - q1 = QuantumRegister(1, "q1") - q2 = QuantumRegister(2, "q2") - c1 = ClassicalRegister(1, "c1") - c2 = ClassicalRegister(2, "c2") - qc = QuantumCircuit(q1, q2, c1, c2, name="test") - - qc.delay(1, q2) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("wait q[1], 1\n" in result) - self.assertTrue("wait q[2], 1\n" in result) - - # float is translated to int - qc.delay(2.0, q1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("wait q[0], 2\n" in result) - - def test_generate_cqasm_correct_output_delay_units_in_dt(self): - q1 = QuantumRegister(2, "q1") - q2 = QuantumRegister(2, "q2") - c1 = ClassicalRegister(2, "c1") - c2 = ClassicalRegister(2, "c2") - qc = QuantumCircuit(q1, q2, c1, c2, name="test") - - qc.delay(1.0, q1, unit="dt") - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("wait q[0], 1\n" in result) - self.assertTrue("wait q[1], 1\n" in result) - - qc.delay(2, q1, unit="dt") - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("wait q[0], 2\n" in result) - self.assertTrue("wait q[1], 2\n" in result) - - def test_generate_cqasm_correct_output_delay_units_in_s(self): - # we need to transpile first to let the circuit convert the delay to seconds - q1 = QuantumRegister(2, "q1") - q2 = QuantumRegister(2, "q2") - c1 = ClassicalRegister(2, "c1") - c2 = ClassicalRegister(2, "c2") - qc = QuantumCircuit(q1, q2, c1, c2, name="test") - - qc.delay(1.1, q1, "ms") - result = self._generate_cqasm_from_circuit(qc, transpile_first=True) - self.assertTrue("wait q[0], 0\n" in result) - self.assertTrue("wait q[1], 0\n" in result) - - qc.delay(0.9, q2, "ns") - result = self._generate_cqasm_from_circuit(qc, transpile_first=True) - self.assertTrue("wait q[2], 0\n" in result) - self.assertTrue("wait q[3], 0\n" in result) - - qc.delay(1.0, unit="s") - result = self._generate_cqasm_from_circuit(qc, transpile_first=True) - self.assertTrue("wait q[0], 1\n" in result) - self.assertTrue("wait q[1], 1\n" in result) - self.assertTrue("wait q[2], 1\n" in result) - self.assertTrue("wait q[3], 1\n" in result) - - def test_generate_cqasm_correct_output_reset_qubit(self): - q1 = QuantumRegister(1, "q1") - q2 = QuantumRegister(2, "q2") - c1 = ClassicalRegister(1, "c1") - c2 = ClassicalRegister(2, "c2") - qc = QuantumCircuit(q1, q2, c1, c2, name="test") - - qc.reset(q1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("prep_z q[0]\n" in result) - - def test_generate_cqasm_correct_output_reset_qubits(self): - q1 = QuantumRegister(1, "q1") - q2 = QuantumRegister(2, "q2") - c1 = ClassicalRegister(1, "c1") - c2 = ClassicalRegister(2, "c2") - qc = QuantumCircuit(q1, q2, c1, c2, name="test") - - qc.reset(q2) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("prep_z q[1]\nprep_z q[2]\n" in result) - - def test_generate_cqasm_correct_output_identity(self): - qc = QuantumCircuit(2, 2) - qc.id(0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("I q[0]\n" in result) - - qc = QuantumCircuit(2, 2) - qc.id(0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("I q[0]\n" in result) - - def test_generate_cqasm_correct_output_conditional_identity(self): - q = QuantumRegister(8, "q") - c = ClassicalRegister(8, "c") - qc = QuantumCircuit(q, c, name="test") - qc.id(0).c_if(c, 14) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[0,4,5,6,7]\nC-I b[0,1,2,3,4,5,6,7], q[0]\nnot b[0,4,5,6,7]\n" in result) - - q = QuantumRegister(8, "q") - c = ClassicalRegister(8, "c") - qc = QuantumCircuit(q, c, name="test") - qc.id(0).c_if(c, 14) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[0,4,5,6,7]\nC-I b[0,1,2,3,4,5,6,7], q[0]\nnot b[0,4,5,6,7]\n" in result) - - def test_generate_cqasm_correct_output_gate_s(self): - qc = QuantumCircuit(2, 2) - qc.s(1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("S q[1]\n" in result) - - def test_generate_cqasm_correct_output_conditional_gate_s(self): - q = QuantumRegister(9, "q") - c = ClassicalRegister(9, "c") - qc = QuantumCircuit(q, c, name="test") - qc.s(2).c_if(c, 11) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[2,4,5,6,7,8]\nC-S b[0,1,2,3,4,5,6,7,8], q[2]\nnot b[2,4,5,6,7,8]\n" in result) - - def test_generate_cqasm_correct_output_gate_sdag(self): - qc = QuantumCircuit(3, 3) - qc.sdg(2) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Sdag q[2]\n" in result) - - def test_generate_cqasm_correct_output_conditional_gate_sdag(self): - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.sdg(0).c_if(c, 14) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[0]\nC-Sdag b[0,1,2,3], q[0]\nnot b[0]\n" in result) - - def test_generate_cqasm_correct_output_gate_swap(self): - qc = QuantumCircuit(4, 4) - qc.swap(2, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("SWAP q[2], q[3]\n" in result) - - def test_generate_cqasm_correct_output_conditional_gate_swap(self): - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.swap(0, 1).c_if(c, 14) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[0]\nC-SWAP b[0,1,2,3], q[0], q[1]\nnot b[0]\n" in result) - - def test_generate_cqasm_correct_output_gate_t(self): - qc = QuantumCircuit(3, 3) - qc.t(2) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("T q[2]\n" in result) - - def test_generate_cqasm_correct_output_conditional_gate_t(self): - q = QuantumRegister(9, "q") - c = ClassicalRegister(9, "c") - qc = QuantumCircuit(q, c, name="test") - qc.t(1).c_if(c, 11) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[2,4,5,6,7,8]\nC-T b[0,1,2,3,4,5,6,7,8], q[1]\nnot b[2,4,5,6,7,8]\n" in result) - - def test_generate_cqasm_correct_output_gate_tdag(self): - qc = QuantumCircuit(3, 3) - qc.tdg(2) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Tdag q[2]\n" in result) - - def test_generate_cqasm_correct_output_conditional_gate_tdag(self): - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.tdg(0).c_if(c, 14) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[0]\nC-Tdag b[0,1,2,3], q[0]\nnot b[0]\n" in result) - - def test_generate_cqasm_correct_output_gate_x(self): - qc = QuantumCircuit(2, 2) - qc.x(0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("X q[0]\n" in result) - - def test_generate_cqasm_correct_output_conditional_gate_x(self): - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.x(0).c_if(c, 14) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[0]\nC-X b[0,1,2,3], q[0]\nnot b[0]\n" in result) - - def test_generate_cqasm_correct_output_gate_y(self): - qc = QuantumCircuit(2, 2) - qc.y(0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Y q[0]\n" in result) - - def test_generate_cqasm_correct_output_conditional_gate_y(self): - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.y(0).c_if(c, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[1,2,3]\nC-Y b[0,1,2,3], q[0]\nnot b[1,2,3]\n" in result) - - def test_generate_cqasm_correct_output_gate_z(self): - qc = QuantumCircuit(2, 2) - qc.z(0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Z q[0]\n" in result) - - def test_generate_cqasm_correct_output_conditional_gate_z(self): - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.z(0).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[2,3]\nC-Z b[0,1,2,3], q[0]\nnot b[2,3]\n" in result) - - def test_generate_cqasm_correct_output_gate_u(self): - qc = QuantumCircuit(2, 2) - qc.u(0, 0, np.pi / 2, 0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rz q[0], 1.570796\n" in result) - - qc = QuantumCircuit(2, 2) - qc.u(-np.pi / 2, 0, 0, 0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Ry q[0], -1.570796\n" in result) - - qc = QuantumCircuit(2, 2) - qc.u(np.pi / 4, np.pi / 2, -np.pi / 2, 0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rz q[0], -1.570796\nRy q[0], 0.785398\nRz q[0], 1.570796\n" in result) - - qc = QuantumCircuit(2, 2) - qc.u(0.123456, 0.654321, -0.333333, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rz q[1], -0.333333\nRy q[1], 0.123456\nRz q[1], 0.654321\n" in result) - - def test_generate_cqasm_correct_output_conditional_gate_u(self): - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.u(0, 0, np.pi / 2, 0).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[2,3]\nC-Rz b[0,1,2,3], q[0], 1.570796\nnot b[2,3]\n" in result) - - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.u(-np.pi / 2, 0, 0, 0).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[2,3]\nC-Ry b[0,1,2,3], q[0], -1.570796\nnot b[2,3]" in result) - - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.u(np.pi / 4, np.pi / 2, -np.pi / 2, 0).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue( - "not b[2,3]\nC-Rz b[0,1,2,3], q[0], -1.570796\nC-Ry b[0,1,2,3], q[0], 0.785398\nC-Rz b[0,1,2,3]," - " q[0], 1.570796\nnot b[2,3]\n" in result - ) - - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.u(0.123456, 0.654321, -0.333333, 1).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue( - "not b[2,3]\nC-Rz b[0,1,2,3], q[1], -0.333333\nC-Ry b[0,1,2,3], q[1], 0.123456\nC-Rz b[0,1,2,3]," - " q[1], 0.654321\nnot b[2,3]\n" in result - ) - - def test_generate_cqasm_correct_output_gate_p(self): - qc = QuantumCircuit(2, 2) - qc.p(np.pi / 2, 0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rz q[0], 1.570796\n" in result) - - qc = QuantumCircuit(2, 2) - qc.p(np.pi / 4, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rz q[1], 0.785398\n" in result) - - qc = QuantumCircuit(3, 3) - qc.p(-np.pi / 4, 2) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rz q[2], -0.785398\n" in result) - - qc = QuantumCircuit(3, 3) - qc.p(0.123456, 2) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rz q[2], 0.123456\n" in result) - - qc = QuantumCircuit(2, 2) - qc.p(0, 0) - result = self._generate_cqasm_from_circuit(qc) - self.assertFalse("q[0]" in result) - - def test_generate_cqasm_correct_output_conditional_gate_p(self): - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.p(np.pi / 2, 0).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[2,3]\nC-Rz b[0,1,2,3], q[0], 1.570796\nnot b[2,3]\n" in result) - - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.p(np.pi / 4, 1).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[2,3]\nC-Rz b[0,1,2,3], q[1], 0.785398\nnot b[2,3]\n" in result) - - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.p(-np.pi / 4, 2).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[2,3]\nC-Rz b[0,1,2,3], q[2], -0.785398\nnot b[2,3]\n" in result) - - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.p(0.123456, 2).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[2,3]\nC-Rz b[0,1,2,3], q[2], 0.123456\nnot b[2,3]\n" in result) - - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.p(0, 0).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertFalse("q[0]" in result) - - def test_generate_cqasm_correct_output_gate_u2(self): - # Qiskit u2 is deprecated from 0.16.0. u2(a, b) -> u(pi/2, a, b) - qc = QuantumCircuit(2, 2) - qc.u(np.pi / 2, np.pi, np.pi / 2, 0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rz q[0], 1.570796\nRy q[0], 1.570796\nRz q[0], 3.141593\n" in result) - - qc = QuantumCircuit(2, 2) - qc.u(np.pi / 2, 0, np.pi, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rz q[1], 3.141593\nRy q[1], 1.570796\n" in result) - - qc = QuantumCircuit(3, 3) - qc.u(np.pi / 2, 0.123456, -0.654321, 2) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rz q[2], -0.654321\nRy q[2], 1.570796\nRz q[2], 0.123456\n" in result) - - qc = QuantumCircuit(3, 3) - qc.u(np.pi / 2, 0, 0, 0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Ry q[0], 1.570796\n" in result) - - def test_generate_cqasm_correct_output_conditional_gate_u2(self): - # Qiskit u2 is deprecated from 0.16.0. u2(a, b) -> u(pi/2, a, b) - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.u(np.pi / 2, np.pi, np.pi / 2, 0).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue( - "not b[2,3]\nC-Rz b[0,1,2,3], q[0], 1.570796\nC-Ry b[0,1,2,3], q[0], 1.570796\nC-Rz b[0,1,2,3], q[0]," - " 3.141593\nnot b[2,3]\n" in result - ) - - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.u(np.pi / 2, 0, np.pi, 1).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue( - "not b[2,3]\nC-Rz b[0,1,2,3], q[1], 3.141593\nC-Ry b[0,1,2,3], q[1], 1.570796\nnot b[2,3]\n" in result - ) - - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.u(np.pi / 2, 0.123456, -0.654321, 2).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue( - "not b[2,3]\nC-Rz b[0,1,2,3], q[2], -0.654321\nC-Ry b[0,1,2,3], q[2], 1.570796\nC-Rz b[0,1,2,3], q[2]," - " 0.123456\nnot b[2,3]\n" in result - ) - - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.u(np.pi / 2, 0, 0, 0).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[2,3]\nC-Ry b[0,1,2,3], q[0], 1.570796\nnot b[2,3]\n" in result) - - def test_generate_cqasm_correct_output_gate_u3(self): - # Qiskit u3 is deprecated from 0.16.0. u3 -> u - qc = QuantumCircuit(2, 2) - qc.u(1, 2, 3, 0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rz q[0], 3.000000\nRy q[0], 1.000000\nRz q[0], 2.000000\n" in result) - - qc = QuantumCircuit(2, 2) - qc.u(0.123456, 0.654321, -0.333333, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rz q[1], -0.333333\nRy q[1], 0.123456\nRz q[1], 0.654321\n" in result) - - qc = QuantumCircuit(2, 2) - qc.u(0, 0.654321, 0, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rz q[1], 0.654321\n" in result) - - qc = QuantumCircuit(3, 3) - qc.u(0.654321, 0, 0, 2) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Ry q[2], 0.654321\n" in result) - - qc = QuantumCircuit(2, 2) - qc.u(0, 0, 0, 0) - result = self._generate_cqasm_from_circuit(qc) - self.assertFalse("q[0]" in result) - - def test_generate_cqasm_correct_output_conditional_gate_u3(self): - # Qiskit u3 is deprecated from 0.16.0. u3 -> u - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.u(1, 2, 3, 0).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue( - "not b[2,3]\nC-Rz b[0,1,2,3], q[0], 3.000000\nC-Ry b[0,1,2,3], q[0], 1.000000\nC-Rz b[0,1,2,3], q[0]," - " 2.000000\nnot b[2,3]\n" in result - ) - - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.u(0.123456, 0.654321, -0.333333, 1).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue( - "not b[2,3]\nC-Rz b[0,1,2,3], q[1], -0.333333\nC-Ry b[0,1,2,3], q[1], 0.123456\nC-Rz b[0,1,2,3], q[1]," - " 0.654321\nnot b[2,3]\n" in result - ) - - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.u(0, 0.654321, 0, 1).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[2,3]\nC-Rz b[0,1,2,3], q[1], 0.654321\nnot b[2,3]\n" in result) - - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.u(0.654321, 0, 0, 2).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[2,3]\nC-Ry b[0,1,2,3], q[2], 0.654321\nnot b[2,3]\n" in result) - - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.u(0, 0, 0, 0).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertFalse("q[0]" in result) - - def test_generate_cqasm_correct_output_sympy_special_cases(self): - # Zero - qc = QuantumCircuit(2, 2) - qc.rx(0, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rx q[1], 0.000000\n" in result) - - # One - qc = QuantumCircuit(2, 2) - qc.rx(1, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rx q[1], 1.000000\n" in result) - - # Integer - qc = QuantumCircuit(2, 2) - qc.rx(2, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rx q[1], 2.000000\n" in result) - - # NegativeOne - qc = QuantumCircuit(2, 2) - qc.rx(-1, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rx q[1], -1.000000\n" in result) - - # Float - qc = QuantumCircuit(2, 2) - qc.rx(np.pi / 2, 0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rx q[0], 1.570796\n" in result) - - def test_generate_cqasm_correct_output_rotation_x(self): - qc = QuantumCircuit(2, 2) - qc.rx(np.pi / 2, 0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rx q[0], 1.570796\n" in result) - - qc = QuantumCircuit(2, 2) - qc.rx(0.123456, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rx q[1], 0.123456\n" in result) - - def test_generate_cqasm_correct_output_conditional_rotation_x(self): - q = QuantumRegister(8, "q") - c = ClassicalRegister(8, "c") - qc = QuantumCircuit(q, c, name="test") - qc.rx(np.pi / 2, 0).c_if(c, 14) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[0,4,5,6,7]\nC-Rx b[0,1,2,3,4,5,6,7], q[0], 1.570796\nnot b[0,4,5,6,7]\n" in result) - - q = QuantumRegister(8, "q") - c = ClassicalRegister(8, "c") - qc = QuantumCircuit(q, c, name="test") - qc.rx(0.123456, 1).c_if(c, 14) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[0,4,5,6,7]\nC-Rx b[0,1,2,3,4,5,6,7], q[1], 0.123456\nnot b[0,4,5,6,7]\n" in result) - - def test_generate_cqasm_correct_output_rotation_y(self): - qc = QuantumCircuit(2, 2) - qc.ry(np.pi / 2, 0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Ry q[0], 1.570796\n" in result) - - qc = QuantumCircuit(2, 2) - qc.ry(0.654321, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Ry q[1], 0.654321\n" in result) - - def test_generate_cqasm_correct_output_conditional_rotation_y(self): - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.ry(np.pi / 2, 0).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[2,3]\nC-Ry b[0,1,2,3], q[0], 1.570796\nnot b[2,3]\n" in result) - - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.ry(0.654321, 1).c_if(c, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[2,3]\nC-Ry b[0,1,2,3], q[1], 0.654321\nnot b[2,3]\n" in result) - - def test_generate_cqasm_correct_output_rotation_z(self): - qc = QuantumCircuit(2, 2) - qc.rz(np.pi / 2, 0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rz q[0], 1.570796\n" in result) - - qc = QuantumCircuit(2, 2) - qc.rz(-np.pi / 2, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("Rz q[1], -1.570796\n" in result) - - def test_generate_cqasm_correct_output_conditional_rotation_z(self): - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.rz(np.pi / 2, 0).c_if(c, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[1,2,3]\nC-Rz b[0,1,2,3], q[0], 1.570796\nnot b[1,2,3]\n" in result) - - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.rz(-np.pi / 2, 1).c_if(c, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[1,2,3]\nC-Rz b[0,1,2,3], q[1], -1.570796\nnot b[1,2,3]\n" in result) - - def test_generate_cqasm_correct_output_unknown_gate(self): - instructions = [{"name": "bla", "qubits": [1], "params": [-np.pi / 2]}] - self.assertRaisesRegex( - CircuitError, "Gate 'bla' not supported", self._generate_cqasm_from_instructions, instructions, 2 - ) - - def test_generate_cqasm_correct_output_unknown_controlled_gate(self): - instructions = [ - {"mask": "0xF", "name": "bfunc", "register": 17, "relation": "==", "val": "0x1"}, - {"conditional": 17, "name": "bla", "qubits": [1], "params": [-np.pi / 2]}, - ] - self.assertRaisesRegex( - CircuitError, - "Conditional gate 'c-bla' not supported", - self._generate_cqasm_from_instructions, - instructions, - 2, - ) - - def test_generate_cqasm_correct_output_no_bit_negation(self): - q = QuantumRegister(4, "q") - c = ClassicalRegister(4, "c") - qc = QuantumCircuit(q, c, name="test") - qc.rx(-np.pi / 2, 1).c_if(c, 15) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("C-Rx b[0,1,2,3], q[1], -1.570796\n" in result) - self.assertFalse("not\n" in result) - - def test_generate_cqasm_correct_output_one_bit_condition(self): - q1 = QuantumRegister(1, "q1") - q2 = QuantumRegister(1, "q2") - c1 = ClassicalRegister(1, "c1") - c2 = ClassicalRegister(1, "c2") - qc = QuantumCircuit(q1, q2, c1, c2, name="test") - qc.rx(-np.pi / 2, q2).c_if(c1, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("C-Rx b[0], q[1], -1.570796\n" in result) - self.assertFalse("not\n" in result) - - q1 = QuantumRegister(1, "q1") - q2 = QuantumRegister(1, "q2") - c1 = ClassicalRegister(1, "c1") - c2 = ClassicalRegister(1, "c2") - qc = QuantumCircuit(q1, q2, c1, c2, name="test") - qc.rx(-np.pi / 2, q2).c_if(c2, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("C-Rx b[1], q[1], -1.570796\n" in result) - self.assertFalse("not\n" in result) - - q1 = QuantumRegister(6, "q1") - q2 = QuantumRegister(1, "q2") - c1 = ClassicalRegister(6, "c1") - c2 = ClassicalRegister(1, "c2") - qc = QuantumCircuit(q1, q2, c1, c2, name="test") - qc.rx(-np.pi / 2, 1).c_if(c2, 1) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("C-Rx b[6], q[1], -1.570796\n" in result) - self.assertFalse("not\n" in result) - - q1 = QuantumRegister(6, "q1") - q2 = QuantumRegister(1, "q2") - c1 = ClassicalRegister(6, "c1") - c2 = ClassicalRegister(1, "c2") - qc = QuantumCircuit(q1, q2, c1, c2, name="test") - qc.rx(-np.pi / 2, 1).c_if(c2, 0) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[6]\nC-Rx b[6], q[1], -1.570796\nnot b[6]\n" in result) - - def test_generate_cqasm_correct_output_more_bit_condition(self): - q1 = QuantumRegister(3, "q1") - q2 = QuantumRegister(3, "q2") - c1 = ClassicalRegister(3, "c1") - c2 = ClassicalRegister(3, "c2") - qc = QuantumCircuit(q1, q2, c1, c2, name="test") - qc.y(2).c_if(c2, 3) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[5]\nC-Y b[3,4,5], q[2]\nnot b[5]\n" in result) - - q1 = QuantumRegister(1, "q1") - q2 = QuantumRegister(7, "q2") - c1 = ClassicalRegister(1, "c1") - c2 = ClassicalRegister(7, "c2") - qc = QuantumCircuit(q1, q2, c1, c2, name="test") - qc.y(2).c_if(c2, 12) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[1,2,5,6,7]\nC-Y b[1,2,3,4,5,6,7], q[2]\nnot b[1,2,5,6,7]\n" in result) - - q1 = QuantumRegister(1, "q1") - q2 = QuantumRegister(7, "q2") - c1 = ClassicalRegister(1, "c1") - c2 = ClassicalRegister(7, "c2") - qc = QuantumCircuit(q1, q2, c1, c2, name="test") - qc.y(2).c_if(c2, 27) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[3,6,7]\nC-Y b[1,2,3,4,5,6,7], q[2]\nnot b[3,6,7]\n" in result) - - q1 = QuantumRegister(5, "q1") - q2 = QuantumRegister(2, "q2") - c1 = ClassicalRegister(5, "c1") - c2 = ClassicalRegister(2, "c2") - qc = QuantumCircuit(q1, q2, c1, c2, name="test") - qc.y(2).c_if(c2, 2) - result = self._generate_cqasm_from_circuit(qc) - self.assertTrue("not b[5]\nC-Y b[5,6], q[2]\nnot b[5]\n" in result) - - def test_generate_cqasm_correct_output_unknown_type(self): - instructions = [ - {"mask": "0xF", "name": "bfunc", "register": 18, "relation": "!=", "val": "0x1"}, - {"conditional": 18, "name": "rx", "qubits": [1], "params": [-np.pi / 2]}, - ] - self.assertRaisesRegex( - CircuitError, - "Conditional statement with relation != not supported", - self._generate_cqasm_from_instructions, - instructions, - 2, - ) - - def test_generate_cqasm_correct_output_no_mask(self): - instructions = [ - {"mask": "0x0", "name": "bfunc", "register": 18, "relation": "==", "val": "0x1"}, - {"conditional": 18, "name": "rx", "qubits": [1], "params": [-np.pi / 2]}, - ] - self.assertRaisesRegex( - CircuitError, - "Conditional statement rx without a mask", - self._generate_cqasm_from_instructions, - instructions, - 2, - ) - - def test_generate_cqasm_register_no_match(self): - instructions = [ - {"mask": "0xF", "name": "bfunc", "register": 1, "relation": "==", "val": "0x3"}, - {"conditional": 2, "name": "rx", "qubits": [1], "params": [-np.pi / 2]}, - ] - self.assertRaisesRegex( - CircuitError, "Conditional not found: reg_idx = 2", self._generate_cqasm_from_instructions, instructions, 2 - ) - - def test_get_mask_data(self): - mask = 0 - lowest_mask_bit, mask_length = CircuitToString.get_mask_data(mask) - self.assertEqual(lowest_mask_bit, -1) - self.assertEqual(mask_length, 0) - - mask = 56 - lowest_mask_bit, mask_length = CircuitToString.get_mask_data(mask) - self.assertEqual(lowest_mask_bit, 3) - self.assertEqual(mask_length, 3) - - mask = 1 - lowest_mask_bit, mask_length = CircuitToString.get_mask_data(mask) - self.assertEqual(lowest_mask_bit, 0) - self.assertEqual(mask_length, 1) - - mask = 255 - lowest_mask_bit, mask_length = CircuitToString.get_mask_data(mask) - self.assertEqual(lowest_mask_bit, 0) - self.assertEqual(mask_length, 8) - - mask = 510 - lowest_mask_bit, mask_length = CircuitToString.get_mask_data(mask) - self.assertEqual(lowest_mask_bit, 1) - self.assertEqual(mask_length, 8) - - mask = 128 - lowest_mask_bit, mask_length = CircuitToString.get_mask_data(mask) - self.assertEqual(lowest_mask_bit, 7) - self.assertEqual(mask_length, 1) - - mask = 192 - lowest_mask_bit, mask_length = CircuitToString.get_mask_data(mask) - self.assertEqual(lowest_mask_bit, 6) - self.assertEqual(mask_length, 2) - - def test_no_basis_gates(self): - c = CircuitToString([], Mock()) - assert "measure" not in c.basis_gates diff --git a/tests/sdk/qiskit/test_measurements.py b/tests/sdk/qiskit/test_measurements.py deleted file mode 100644 index b5cbb775..00000000 --- a/tests/sdk/qiskit/test_measurements.py +++ /dev/null @@ -1,231 +0,0 @@ -"""Quantum Inspire SDK. - -Copyright 2022 QuTech Delft - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the -License. You may obtain a copy of the License at -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import unittest -from unittest.mock import Mock - -from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister -from qiskit.compiler import assemble - -from quantuminspire.sdk.qiskit.backend import QuantumInspireBackend -from quantuminspire.sdk.qiskit.exceptions import QiskitBackendError -from quantuminspire.sdk.qiskit.measurements import Measurements - - -class TestMeasurements(unittest.TestCase): - - @staticmethod - def _circuit_to_qobj(circuit): - run_config_dict = {"shots": 25, "memory": True} - backend = QuantumInspireBackend(Mock(), None) - qobj = assemble(circuit, backend, **run_config_dict) - return qobj - - @staticmethod - def _circuit_to_experiment(circuit): - qobj = TestMeasurements._circuit_to_qobj(circuit) - return qobj.experiments[0] - - def test_from_experiment(self): - qc = QuantumCircuit(2, 2) - qc.cx(0, 1) - qc.measure(0, 1) - qc.measure(1, 0) - - experiment = self._circuit_to_experiment(qc) - measurements = Measurements.from_experiment(experiment) - expected_result = { - "measurements_state": [[1, 0], [0, 1]], - "measurements_reg": [[0, 1], [1, 0]], - "number_of_qubits": 2, - "number_of_clbits": 2, - } - self.assertDictEqual(measurements.to_dict(), expected_result) - self.assertEqual(measurements.nr_of_qubits, 2) - self.assertEqual(measurements.nr_of_clbits, 2) - - def test_collect_measurements_without_measurements(self): - qc = QuantumCircuit(2, 2) - qc.cx(0, 1) - - experiment = self._circuit_to_experiment(qc) - - measurements = Measurements.from_experiment(experiment) - expected_result = { - "measurements_state": [[0, 0], [1, 1]], - "measurements_reg": [[0, 0], [1, 1]], - "number_of_qubits": 2, - "number_of_clbits": 2, - } - self.assertDictEqual(measurements.to_dict(), expected_result) - self.assertEqual(measurements.nr_of_qubits, 2) - self.assertEqual(measurements.nr_of_clbits, 2) - - def test_validate_nr_classical_qubits_less_than_needed_for_storing_measured_qubits(self): - q = QuantumRegister(2, "q") - c = ClassicalRegister(1, "c") - qc = QuantumCircuit(q, c, name="conditional") - qc.cx(q[0], q[1]) - - experiment = self._circuit_to_experiment(qc) - self.assertRaisesRegex( - QiskitBackendError, - "Number of classical bits \(1\) is not sufficient for storing the " "outcomes of the experiment", - Measurements.from_experiment, - experiment, - ) - - def test_invalid_number_of_classical_bits(self): - qc = QuantumCircuit(2, 2) - qc.measure(1, 0) - - qobj = self._circuit_to_qobj(qc) - qobj.experiments[0].header.memory_slots = 0 - - experiment = qobj.experiments[0] - self.assertRaisesRegex( - QiskitBackendError, "Invalid number of classical bits \(0\)!", Measurements.from_experiment, experiment - ) - - def test_max_measurement_index(self): - qr = QuantumRegister(5) - cr = ClassicalRegister(3) - cr_ghz = ClassicalRegister(2) - circuit = QuantumCircuit(qr, cr, cr_ghz) - - circuit.measure(2, 3) - circuit.measure(3, 4) - circuit.measure([0, 1, 4], cr) - circuit.measure([2, 3], cr_ghz) - - experiment = self._circuit_to_experiment(circuit) - - measurements = Measurements.from_experiment(experiment) - - self.assertEqual(measurements.max_measurement_index, 4) - - def test_max_measurement_index_less_than_nr_of_clbits(self): - qr = QuantumRegister(5) - cr = ClassicalRegister(3) - cr_ghz = ClassicalRegister(2) - circuit = QuantumCircuit(qr, cr, cr_ghz) - - circuit.measure(2, 3) - circuit.measure([0, 1, 4], cr) - circuit.measure([2], cr_ghz[0]) - - experiment = self._circuit_to_experiment(circuit) - measurements = Measurements.from_experiment(experiment) - - self.assertEqual(measurements.max_measurement_index, 3) - - def test_get_qreg_for_conditional_creg(self): - qr = QuantumRegister(5) - cr = ClassicalRegister(3) - cr_ghz = ClassicalRegister(2) - circuit = QuantumCircuit(qr, cr, cr_ghz) - - circuit.measure(2, 3) - circuit.measure([1, 4], [1, 2]) - circuit.measure([2], cr_ghz[0]) - - experiment = self._circuit_to_experiment(circuit) - measurements = Measurements.from_experiment(experiment) - - self.assertEqual(measurements.get_qreg_for_conditional_creg(3), 2) - self.assertEqual(measurements.get_qreg_for_conditional_creg(1), 1) - self.assertEqual(measurements.get_qreg_for_conditional_creg(2), 4) - self.assertEqual(measurements.get_qreg_for_conditional_creg(0), 0) - self.assertRaisesRegex( - QiskitBackendError, - "Classical bit 4 used in a conditional gate is not measured and the " - "equivalent qubit 4 is measured to another classical bit 2", - measurements.get_qreg_for_conditional_creg, - 4, - ) - - def test_from_dict(self): - input = { - "measurements_state": [[0, 0], [1, 1]], - "measurements_reg": [[0, 0], [1, 1]], - "number_of_qubits": 2, - "number_of_clbits": 2, - } - measurements = Measurements.from_dict(input) - - self.assertEqual(measurements.nr_of_qubits, 2) - self.assertEqual(measurements.nr_of_clbits, 2) - self.assertDictEqual(measurements.to_dict(), input) - - def test_measurement_2_qubits_to_1_classical_bit(self): - qc = QuantumCircuit(2, 2) - qc.cx(0, 1) - qc.measure(0, 0) - qc.x(0) - qc.measure(1, 0) - - experiment = self._circuit_to_experiment(qc) - measurements = Measurements.from_experiment(experiment) - - self.assertRaisesRegex( - QiskitBackendError, - "Measurement of different qubits to the same classical " "register 0 is not supported", - measurements.validate_unsupported_measurements, - ) - - def test_measurement_1_qubit_to_2_classical_bits(self): - qc = QuantumCircuit(2, 2) - qc.cx(0, 1) - qc.measure(1, 1) - qc.measure(0, 0) - qc.x(0) - qc.measure(1, 0) - - experiment = self._circuit_to_experiment(qc) - measurements = Measurements.from_experiment(experiment) - - self.assertRaisesRegex( - QiskitBackendError, - "Measurement of qubit 1 to different classical registers " "is not supported", - measurements.validate_unsupported_measurements, - ) - - def test_qubit_to_classical_hex(self): - qc = QuantumCircuit(4, 4) - qc.measure(0, 0) - qc.measure(1, 1) - qc.measure(2, 2) - qc.measure(3, 3) - - experiment = self._circuit_to_experiment(qc) - measurements = Measurements.from_experiment(experiment) - - self.assertEqual(measurements.qubit_to_classical_hex("3"), "0x3") - self.assertEqual(measurements.qubit_to_classical_hex("7"), "0x7") - self.assertEqual(measurements.qubit_to_classical_hex("10"), "0xa") - - def test_qubit_to_classical_hex_reversed(self): - qc = QuantumCircuit(4, 4) - qc.measure(0, 3) - qc.measure(1, 2) - qc.measure(2, 1) - qc.measure(3, 0) - - experiment = self._circuit_to_experiment(qc) - measurements = Measurements.from_experiment(experiment) - - self.assertEqual(measurements.qubit_to_classical_hex("3"), "0xc") - self.assertEqual(measurements.qubit_to_classical_hex("7"), "0xe") - self.assertEqual(measurements.qubit_to_classical_hex("10"), "0x5") From 184f068b768c59deca8ee5b44a806c796c4eb31b Mon Sep 17 00:00:00 2001 From: Mythir Date: Mon, 20 Jan 2025 10:52:51 +0100 Subject: [PATCH 3/3] Update dependency --- poetry.lock | 12 +++++++----- pyproject.toml | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 9d188f0f..f4f5768f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -1491,18 +1491,20 @@ files = [ [[package]] name = "qi-compute-api-client" -version = "0.42.0" +version = "0.44.0" description = "An API client for the Compute Job Manager of Quantum Inspire." optional = false python-versions = "<4.0,>=3.8" files = [ - {file = "qi_compute_api_client-0.42.0-py3-none-any.whl", hash = "sha256:0c3593a28c1f43bc13a4742f52db765006c1b51d02bd08ad9e33dbb7af46450d"}, - {file = "qi_compute_api_client-0.42.0.tar.gz", hash = "sha256:f63c197c26793028a42856703a9bfaead92155ff24455eaa4e3ff94adc6c021d"}, + {file = "qi_compute_api_client-0.44.0-py3-none-any.whl", hash = "sha256:e16ad15345d142981b9ae95104cc35b1a5d00281e32e64a034f88b8987091877"}, + {file = "qi_compute_api_client-0.44.0.tar.gz", hash = "sha256:15f690a79d19c6f1ec044098151be0658a0c0c98d4b23af51aec00dc707d7d34"}, ] [package.dependencies] aiohttp = ">=3.10.5,<4.0.0" +pydantic = ">=2.10.4,<3.0.0" python-dateutil = ">=2.8.2,<3.0.0" +requests = ">=2.31.0,<3.0.0" urllib3 = ">=2.0.0,<3.0.0" [[package]] @@ -2067,4 +2069,4 @@ local = ["qxelarator"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "cb95d40171f4e85034cfbafcfe5d5ee17550b484ecb50698f01615bd03e6c695" +content-hash = "98f344b11944cd5db26bb53e38e8ecad323b5ed477f916fa6e4fb65e056a95d3" diff --git a/pyproject.toml b/pyproject.toml index fc7e9218..c631a13d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ qi = "quantuminspire.cli.command_list:app" python = "^3.9" typer = {extras = ["all"], version = "^0.15.1"} pydantic = "^2.10.5" -qi-compute-api-client = "^0.42.0" +qi-compute-api-client = "^0.44.0" qxelarator = {version = "^0.7.2", optional = true} pydantic-settings = "^2.7.1" qiskit = "1.0.2"