diff --git a/qiskit/providers/fake_provider/__init__.py b/qiskit/providers/fake_provider/__init__.py index 943869114f85..cb8268451957 100644 --- a/qiskit/providers/fake_provider/__init__.py +++ b/qiskit/providers/fake_provider/__init__.py @@ -265,3 +265,4 @@ # Configurable fake backend from .utils.configurable_backend import ConfigurableFakeBackend +from .fake_generic import FakeGeneric diff --git a/qiskit/providers/fake_provider/fake_generic.py b/qiskit/providers/fake_provider/fake_generic.py new file mode 100644 index 000000000000..e8f3a0f07907 --- /dev/null +++ b/qiskit/providers/fake_provider/fake_generic.py @@ -0,0 +1,566 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Generic fake BackendV2 class""" + +from __future__ import annotations +import warnings + +from collections.abc import Iterable +import numpy as np + +from qiskit import pulse +from qiskit.pulse.instruction_schedule_map import InstructionScheduleMap +from qiskit.circuit import Measure, Parameter, Delay, Reset, QuantumCircuit, Instruction +from qiskit.circuit.controlflow import ( + IfElseOp, + WhileLoopOp, + ForLoopOp, + SwitchCaseOp, + BreakLoopOp, + ContinueLoopOp, +) +from qiskit.circuit.library import XGate, RZGate, SXGate, CXGate, ECRGate, IGate +from qiskit.exceptions import QiskitError +from qiskit.transpiler import CouplingMap, Target, InstructionProperties, QubitProperties +from qiskit.providers import Options +from qiskit.providers.basicaer import BasicAer +from qiskit.providers.backend import BackendV2 +from qiskit.providers.models import ( + PulseDefaults, + Command, +) +from qiskit.qobj import PulseQobjInstruction, PulseLibraryItem +from qiskit.utils import optionals as _optionals + + +class GenericTarget(Target): + """ + This class will generate a :class:`~.Target` instance with + default qubit, instruction and calibration properties. + A target object represents the minimum set of information + the transpiler needs from a backend. + """ + + def __init__( + self, + num_qubits: int, + basis_gates: list[str], + coupling_map: CouplingMap, + control_flow: bool = False, + calibrate_gates: list[str] | None = None, + rng: np.random.Generator = None, + ): + """ + Args: + num_qubits (int): Number of qubits in the target. + + basis_gates (list[str]): List of basis gate names to be supported by + the target. These must be within the supported operations of this + target generator, which can be consulted through the + ``supported_operations`` property. + Common sets of basis gates are ``{"cx", "id", "rz", "sx", "x"}`` + and ``{"ecr", "id", "rz", "sx", "x"}``. + + coupling_map (CouplingMap): Target's coupling map as an instance of + :class:`~.CouplingMap`. + + control_flow (bool): Flag to enable control flow directives on the backend + (defaults to False). + + calibrate_gates (list[str] | None): List of gate names which should contain + default calibration entries. These must be a subset of ``basis_gates``. + + rng (np.random.Generator): Optional fixed-seed generator for default random values. + """ + self._rng = rng if rng else np.random.default_rng(seed=42) + + if num_qubits != coupling_map.size(): + raise QiskitError( + f"The number of qubits (got {num_qubits}) must match " + f"the size of the provided coupling map (got {coupling_map.size()})." + ) + + self._num_qubits = num_qubits + self._coupling_map = coupling_map + + # hardcoded default target attributes. To modify, + # access corresponding properties through the public `Target` API + super().__init__( + description="Generic Target", + num_qubits=num_qubits, + dt=0.222e-9, + qubit_properties=[ + QubitProperties( + t1=self._rng.uniform(100e-6, 200e-6), + t2=self._rng.uniform(100e-6, 200e-6), + frequency=self._rng.uniform(5e9, 5.5e9), + ) + for _ in range(num_qubits) + ], + concurrent_measurements=[list(range(num_qubits))], + ) + + # ensure that Reset, Delay and Measure are in basis_gates + self._basis_gates = set(basis_gates) + for name in ["reset", "delay", "measure"]: + self._basis_gates.add(name) + + # iterate over gates, generate noise params. from defaults + # and add instructions to target + for name in self._basis_gates: + if name not in self.supported_operations: + raise QiskitError( + f"Provided base gate {name} is not a supported " + f"operation ({self.supported_operations})." + ) + gate = self.supported_operations[name] + noise_params = self.noise_defaults[name] + self.add_noisy_instruction(gate, noise_params) + + if control_flow: + self.add_instruction(IfElseOp, name="if_else") + self.add_instruction(WhileLoopOp, name="while_loop") + self.add_instruction(ForLoopOp, name="for_loop") + self.add_instruction(SwitchCaseOp, name="switch_case") + self.add_instruction(BreakLoopOp, name="break") + self.add_instruction(ContinueLoopOp, name="continue") + + # generate block of calibration defaults and add to target + # Note: this could be improved if we could generate and add + # calibration defaults per-gate, and not as a block. + defaults = self._generate_calibration_defaults(calibrate_gates) + inst_map = defaults.instruction_schedule_map + self.add_calibrations_from_instruction_schedule_map(inst_map) + + @property + def supported_operations(self) -> dict[str, Instruction]: + """Mapping of names to class instances for operations supported + in ``basis_gates``. + + Returns: + Dictionary mapping operation names to class instances. + """ + return { + "cx": CXGate(), + "ecr": ECRGate(), + "id": IGate(), + "rz": RZGate(Parameter("theta")), + "sx": SXGate(), + "x": XGate(), + "measure": Measure(), + "delay": Delay(Parameter("Time")), + "reset": Reset(), + } + + @property + def noise_defaults(self) -> dict[str, tuple | None]: + """Noise default values/ranges for duration and error of supported + instructions. There are three possible formats: + + #. (min_duration, max_duration, min_error, max_error), + if the defaults are ranges + #. (duration, error), if the defaults are fixed values + #. None + + Returns: + Dictionary mapping instruction names to noise defaults + """ + return { + "cx": (1e-5, 5e-3, 1e-8, 9e-7), + "ecr": (1e-5, 5e-3, 1e-8, 9e-7), + "id": (0.0, 0.0), + "rz": (0.0, 0.0), + "sx": (1e-5, 5e-3, 1e-8, 9e-7), + "x": (1e-5, 5e-3, 1e-8, 9e-7), + "measure": (1e-5, 5e-3, 1e-8, 9e-7), + "delay": None, + "reset": None, + } + + def add_noisy_instruction( + self, instruction: Instruction, noise_params: tuple[float, ...] | None + ) -> None: + """Add instruction properties to target for specified instruction. + + Args: + instruction (Instruction): Instance of instruction to be added to the target + noise_params (tuple[float, ...] | None): error and duration noise values/ranges to + include in instruction properties. + + Returns: + None + """ + + qarg_set = self._coupling_map if instruction.num_qubits > 1 else range(self._num_qubits) + props = {} + + for qarg in qarg_set: + try: + qargs = tuple(qarg) + except TypeError: + qargs = (qarg,) + + duration, error = ( + (None, None) + if noise_params is None + else noise_params + if len(noise_params) == 2 + else (self._rng.uniform(*noise_params[:2]), self._rng.uniform(*noise_params[2:])) + ) + + props.update({qargs: InstructionProperties(duration, error)}) + + self.add_instruction(instruction, props) + + def add_calibrations_from_instruction_schedule_map( + self, inst_map: InstructionScheduleMap + ) -> None: + """Add calibration entries from provided pulse defaults to target. + + Args: + defaults (PulseDefaults): pulse defaults with instruction schedule map + + Returns: + None + """ + for inst in inst_map.instructions: + for qarg in inst_map.qubits_with_instruction(inst): + try: + qargs = tuple(qarg) + except TypeError: + qargs = (qarg,) + # Do NOT call .get method. This parses Qpbj immediately. + # This operation is computationally expensive and should be bypassed. + calibration_entry = inst_map._get_calibration_entry(inst, qargs) + if inst in self._gate_map: + if inst == "measure": + for qubit in qargs: + self._gate_map[inst][(qubit,)].calibration = calibration_entry + elif qargs in self._gate_map[inst] and inst not in ["delay", "reset"]: + self._gate_map[inst][qargs].calibration = calibration_entry + + def _generate_calibration_defaults(self, calibrate_gates: list[str] | None) -> PulseDefaults: + """Generate calibration defaults for specified gates. + + Args: + calibrate_gates (list[str]): list of gates to be calibrated. + + Returns: + Corresponding PulseDefaults + """ + calibrate_gates = calibrate_gates or [] + measure_command_sequence = [ + PulseQobjInstruction( + name="acquire", + duration=1792, + t0=0, + qubits=list(range(self.num_qubits)), + memory_slot=list(range(self.num_qubits)), + ) + ] + + measure_command_sequence += [ + PulseQobjInstruction(name="pulse_1", ch=f"m{i}", duration=1792, t0=0) + for i in range(self.num_qubits) + ] + + measure_command = Command( + name="measure", + qubits=list(range(self.num_qubits)), + sequence=measure_command_sequence, + ) + + cmd_def = [measure_command] + + for gate in self._basis_gates: + for i in range(self.num_qubits): + sequence = [] + if gate in calibrate_gates: + sequence = [ + PulseQobjInstruction(name="fc", ch=f"d{i}", t0=0, phase="-P0"), + PulseQobjInstruction(name="pulse_3", ch=f"d{i}", t0=0), + ] + cmd_def.append( + Command( + name=gate, + qubits=[i], + sequence=sequence, + ) + ) + + for qubit1, qubit2 in self._coupling_map: + sequence = [ + PulseQobjInstruction(name="pulse_1", ch=f"d{qubit1}", t0=0), + PulseQobjInstruction(name="pulse_2", ch=f"u{qubit1}", t0=10), + PulseQobjInstruction(name="pulse_1", ch=f"d{qubit2}", t0=20), + PulseQobjInstruction(name="fc", ch=f"d{qubit2}", t0=20, phase=2.1), + ] + + if "cx" in self._basis_gates: + if "cx" in calibrate_gates: + sequence = [] + cmd_def += [ + Command( + name="cx", + qubits=[qubit1, qubit2], + sequence=sequence, + ) + ] + if "ecr" in self._basis_gates: + if "ecr" in calibrate_gates: + sequence = [] + cmd_def += [ + Command( + name="ecr", + qubits=[qubit1, qubit2], + sequence=sequence, + ) + ] + + qubit_freq_est = np.random.normal(4.8, scale=0.01, size=self.num_qubits).tolist() + meas_freq_est = np.linspace(6.4, 6.6, self.num_qubits).tolist() + pulse_library = [ + PulseLibraryItem(name="pulse_1", samples=[[0.0, 0.0], [0.0, 0.1]]), + PulseLibraryItem(name="pulse_2", samples=[[0.0, 0.0], [0.0, 0.1], [0.0, 1.0]]), + PulseLibraryItem( + name="pulse_3", samples=[[0.0, 0.0], [0.0, 0.1], [0.0, 1.0], [0.5, 0.0]] + ), + ] + + return PulseDefaults( + qubit_freq_est=qubit_freq_est, + meas_freq_est=meas_freq_est, + buffer=0, + pulse_library=pulse_library, + cmd_def=cmd_def, + ) + + +class FakeGeneric(BackendV2): + """ + Configurable fake :class:`~.BackendV2` generator. This class will + generate a fake backend from a combination of generated defaults + (with a fixable ``seed``) driven from a series of optional input arguments. + """ + + def __init__( + self, + num_qubits: int, + basis_gates: list[str], + *, + coupling_map: list[list[int]] | CouplingMap | None = None, + control_flow: bool = False, + calibrate_gates: list[str] | None = None, + seed: int = 42, + ): + """ + Args: + basis_gates (list[str]): List of basis gate names to be supported by + the backend. The currently supported gate names are: + ``"cx"``, ``"ecr"``, ``"id"``, ``"rz"``, ``"sx"``, and ``"x"`` + Common sets of basis gates are ``["cx", "id", "rz", "sx", "x"]`` + and ``["ecr", "id", "rz", "sx", "x"]``. + + num_qubits (int): Number of qubits of the fake backend. Note that this + fake backend runs on a local noisy simulator, + + coupling_map (list[list[int]] | CouplingMap | None): Optional coupling map + for the fake backend. Multiple formats are supported: + + #. :class:`~.CouplingMap` instance + #. List, must be given as an adjacency matrix, where each entry + specifies all directed two-qubit interactions supported by the backend, + e.g: ``[[0, 1], [0, 3], [1, 2], [1, 5], [2, 5], [4, 1], [5, 3]]`` + + If ``coupling_map`` is specified, it must match the number of qubits + specified in ``num_qubits``. If ``coupling_map`` is not specified, + a fully connected coupling map will be generated with ``num_qubits`` + qubits. + + control_flow (bool): Flag to enable control flow directives on the backend + (defaults to False). + + calibrate_gates (list[str] | None): List of gate names which should contain + default calibration entries (overriden if an ``instruction_schedule_map`` is + provided). These must be a subset of ``basis_gates``. + + seed (int): Optional seed for generation of default values. + """ + + super().__init__( + provider=None, + name="fake_generic", + description=f"This is a fake device with {num_qubits} " f"and generic settings.", + backend_version="", + ) + self._rng = np.random.default_rng(seed=seed) + + # the coupling map is necessary to build the default channels + if coupling_map is None: + self._coupling_map = CouplingMap().from_full(num_qubits) + else: + if isinstance(coupling_map, CouplingMap): + self._coupling_map = coupling_map + else: + self._coupling_map = CouplingMap(coupling_map) + + self._target = GenericTarget( + num_qubits, + basis_gates, + self._coupling_map, + control_flow, + calibrate_gates, + self._rng, + ) + + self._build_default_channels() + self.sim = None + + @property + def target(self): + return self._target + + @property + def max_circuits(self): + return None + + @property + def meas_map(self) -> list[list[int]]: + return self._target.concurrent_measurements + + def drive_channel(self, qubit: int): + drive_channels_map = getattr(self, "channels_map", {}).get("drive", {}) + qubits = (qubit,) + if qubits in drive_channels_map: + return drive_channels_map[qubits][0] + return None + + def measure_channel(self, qubit: int): + measure_channels_map = getattr(self, "channels_map", {}).get("measure", {}) + qubits = (qubit,) + if qubits in measure_channels_map: + return measure_channels_map[qubits][0] + return None + + def acquire_channel(self, qubit: int): + acquire_channels_map = getattr(self, "channels_map", {}).get("acquire", {}) + qubits = (qubit,) + if qubits in acquire_channels_map: + return acquire_channels_map[qubits][0] + return None + + def control_channel(self, qubits: Iterable[int]): + control_channels_map = getattr(self, "channels_map", {}).get("control", {}) + qubits = tuple(qubits) + if qubits in control_channels_map: + return control_channels_map[qubits] + return [] + + def run(self, run_input, **options): + """Run on the fake backend using a simulator. + + This method runs circuit jobs (an individual or a list of :class:`~.QuantumCircuit` + ) and pulse jobs (an individual or a list of :class:`~.Schedule` or + :class:`~.ScheduleBlock`) using :class:`~.BasicAer` or Aer simulator and returns a + :class:`~qiskit.providers.Job` object. + + If qiskit-aer is installed, jobs will be run using the ``AerSimulator`` with + noise model of the fake backend. Otherwise, jobs will be run using the + ``BasicAer`` simulator without noise. + + Noisy simulations of pulse jobs are not yet supported in :class:`~.FakeGeneric`. + + Args: + run_input (QuantumCircuit or Schedule or ScheduleBlock or list): An + individual or a list of + :class:`~qiskit.circuit.QuantumCircuit`, + :class:`~qiskit.pulse.ScheduleBlock`, or + :class:`~qiskit.pulse.Schedule` objects to run on the backend. + options: Any kwarg options to pass to the backend for running the + config. If a key is also present in the options + attribute/object, then the expectation is that the value + specified will be used instead of what's set in the options + object. + + Returns: + Job: The job object for the run + + Raises: + QiskitError: If a pulse job is supplied and qiskit_aer is not installed. + """ + + circuits = run_input + pulse_job = None + if isinstance(circuits, (pulse.Schedule, pulse.ScheduleBlock)): + pulse_job = True + elif isinstance(circuits, QuantumCircuit): + pulse_job = False + elif isinstance(circuits, list): + if circuits: + if all(isinstance(x, (pulse.Schedule, pulse.ScheduleBlock)) for x in circuits): + pulse_job = True + elif all(isinstance(x, QuantumCircuit) for x in circuits): + pulse_job = False + if pulse_job is None: # submitted job is invalid + raise QiskitError( + "Invalid input object %s, must be either a " + "QuantumCircuit, Schedule, or a list of either" % circuits + ) + if pulse_job: # pulse job + raise QiskitError("Pulse simulation is currently not supported for V2 fake backends.") + # circuit job + if not _optionals.HAS_AER: + warnings.warn("Aer not found using BasicAer and no noise", RuntimeWarning) + if self.sim is None: + self._setup_sim() + self.sim._options = self._options + job = self.sim.run(circuits, **options) + return job + + def _setup_sim(self) -> None: + + if _optionals.HAS_AER: + from qiskit_aer import AerSimulator + from qiskit_aer.noise import NoiseModel + + self.sim = AerSimulator() + noise_model = NoiseModel.from_backend(self) + self.sim.set_options(noise_model=noise_model) + # Update fake backend default too to avoid overwriting + # it when run() is called + self.set_options(noise_model=noise_model) + + else: + self.sim = BasicAer.get_backend("qasm_simulator") + + @classmethod + def _default_options(cls) -> Options: + + if _optionals.HAS_AER: + from qiskit_aer import AerSimulator + + return AerSimulator._default_options() + else: + return BasicAer.get_backend("qasm_simulator")._default_options() + + def _build_default_channels(self) -> None: + + channels_map = { + "acquire": {(i,): [pulse.AcquireChannel(i)] for i in range(self.num_qubits)}, + "drive": {(i,): [pulse.DriveChannel(i)] for i in range(self.num_qubits)}, + "measure": {(i,): [pulse.MeasureChannel(i)] for i in range(self.num_qubits)}, + "control": { + (edge): [pulse.ControlChannel(i)] for i, edge in enumerate(self._coupling_map) + }, + } + setattr(self, "channels_map", channels_map) diff --git a/test/python/primitives/test_backend_estimator.py b/test/python/primitives/test_backend_estimator.py index 0df15f590728..80e96db433a9 100644 --- a/test/python/primitives/test_backend_estimator.py +++ b/test/python/primitives/test_backend_estimator.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2022. +# (C) Copyright IBM 2022, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Tests for Estimator.""" +"""Tests for BackendEstimator.""" import unittest from unittest.mock import patch @@ -26,14 +26,15 @@ from qiskit.circuit.library import RealAmplitudes from qiskit.primitives import BackendEstimator, EstimatorResult from qiskit.providers import JobV1 -from qiskit.providers.fake_provider import FakeNairobi, FakeNairobiV2 +from qiskit.providers.fake_provider import FakeNairobi +from qiskit.providers.backend_compat import BackendV2Converter from qiskit.providers.fake_provider.fake_backend_v2 import FakeBackendSimple from qiskit.quantum_info import SparsePauliOp from qiskit.test import QiskitTestCase from qiskit.transpiler import PassManager from qiskit.utils import optionals -BACKENDS = [FakeNairobi(), FakeNairobiV2(), FakeBackendSimple()] +BACKENDS = [FakeNairobi(), BackendV2Converter(FakeNairobi()), FakeBackendSimple()] class CallbackPass(DummyAP): @@ -82,11 +83,11 @@ def setUp(self): @combine(backend=BACKENDS) def test_estimator_run(self, backend): """Test Estimator.run()""" - backend.set_options(seed_simulator=123) psi1, psi2 = self.psi hamiltonian1, hamiltonian2, hamiltonian3 = self.hamiltonian theta1, theta2, theta3 = self.theta estimator = BackendEstimator(backend=backend) + estimator.set_options(seed_simulator=123) # Specify the circuit and observable by indices. # calculate [ ] @@ -121,9 +122,9 @@ def test_estimator_run(self, backend): @combine(backend=BACKENDS) def test_estimator_run_no_params(self, backend): """test for estimator without parameters""" - backend.set_options(seed_simulator=123) circuit = self.ansatz.assign_parameters([0, 1, 1, 2, 3, 5]) est = BackendEstimator(backend=backend) + est.set_options(seed_simulator=123) result = est.run([circuit], [self.observable]).result() self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [-1.284366511861733], rtol=0.05) @@ -131,7 +132,6 @@ def test_estimator_run_no_params(self, backend): @combine(backend=BACKENDS, creg=[True, False]) def test_run_1qubit(self, backend, creg): """Test for 1-qubit cases""" - backend.set_options(seed_simulator=123) qc = QuantumCircuit(1, 1) if creg else QuantumCircuit(1) qc2 = QuantumCircuit(1, 1) if creg else QuantumCircuit(1) qc2.x(0) @@ -140,6 +140,7 @@ def test_run_1qubit(self, backend, creg): op2 = SparsePauliOp.from_list([("Z", 1)]) est = BackendEstimator(backend=backend) + est.set_options(seed_simulator=123) result = est.run([qc], [op], [[]]).result() self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1], rtol=0.1) @@ -196,7 +197,6 @@ def test_run_2qubits(self, backend, creg): @combine(backend=BACKENDS) def test_run_errors(self, backend): """Test for errors""" - backend.set_options(seed_simulator=123) qc = QuantumCircuit(1) qc2 = QuantumCircuit(2) @@ -204,6 +204,7 @@ def test_run_errors(self, backend): op2 = SparsePauliOp.from_list([("II", 1)]) est = BackendEstimator(backend=backend) + est.set_options(seed_simulator=123) with self.assertRaises(ValueError): est.run([qc], [op2], [[]]).result() with self.assertRaises(ValueError): @@ -218,7 +219,6 @@ def test_run_errors(self, backend): @combine(backend=BACKENDS) def test_run_numpy_params(self, backend): """Test for numpy array as parameter values""" - backend.set_options(seed_simulator=123) qc = RealAmplitudes(num_qubits=2, reps=2) op = SparsePauliOp.from_list([("IZ", 1), ("XI", 2), ("ZY", -1)]) k = 5 @@ -226,6 +226,8 @@ def test_run_numpy_params(self, backend): params_list = params_array.tolist() params_list_array = list(params_array) estimator = BackendEstimator(backend=backend) + estimator.set_options(seed_simulator=123) + target = estimator.run([qc] * k, [op] * k, params_list).result() with self.subTest("ndarrary"): @@ -274,14 +276,14 @@ def test_options(self, backend): def test_job_size_limit_v2(self): """Test BackendEstimator respects job size limit""" - class FakeNairobiLimitedCircuits(FakeNairobiV2): - """FakeNairobiV2 with job size limit.""" + class FakeBackendLimitedCircuits(FakeBackendSimple): + """FakeBackend V2 with job size limit.""" @property def max_circuits(self): return 1 - backend = FakeNairobiLimitedCircuits() + backend = FakeBackendLimitedCircuits() backend.set_options(seed_simulator=123) qc = RealAmplitudes(num_qubits=2, reps=2) op = SparsePauliOp.from_list([("IZ", 1), ("XI", 2), ("ZY", -1)]) @@ -299,13 +301,13 @@ def test_job_size_limit_v1(self): config = backend.configuration() config.max_experiments = 1 backend._configuration = config - backend.set_options(seed_simulator=123) qc = RealAmplitudes(num_qubits=2, reps=2) op = SparsePauliOp.from_list([("IZ", 1), ("XI", 2), ("ZY", -1)]) k = 5 params_array = np.random.rand(k, qc.num_parameters) params_list = params_array.tolist() estimator = BackendEstimator(backend=backend) + estimator.set_options(seed_simulator=123) with patch.object(backend, "run") as run_mock: estimator.run([qc] * k, [op] * k, params_list).result() self.assertEqual(run_mock.call_count, 10) @@ -316,7 +318,6 @@ def test_no_max_circuits(self): config = backend.configuration() del config.max_experiments backend._configuration = config - backend.set_options(seed_simulator=123) qc = RealAmplitudes(num_qubits=2, reps=2) op = SparsePauliOp.from_list([("IZ", 1), ("XI", 2), ("ZY", -1)]) k = 5 @@ -324,6 +325,7 @@ def test_no_max_circuits(self): params_list = params_array.tolist() params_list_array = list(params_array) estimator = BackendEstimator(backend=backend) + estimator.set_options(seed_simulator=123) target = estimator.run([qc] * k, [op] * k, params_list).result() with self.subTest("ndarrary"): result = estimator.run([qc] * k, [op] * k, params_array).result() @@ -390,8 +392,9 @@ def test_layout(self, backend): estimator = BackendEstimator(backend) estimator.set_transpile_options(seed_transpiler=15) value = estimator.run(qc, op, shots=10000).result().values[0] + if optionals.HAS_AER and not isinstance(backend, FakeBackendSimple): - self.assertEqual(value, -0.916) + self.assertEqual(value, -0.9154) else: self.assertEqual(value, -1) @@ -405,8 +408,9 @@ def test_layout(self, backend): estimator = BackendEstimator(backend) estimator.set_transpile_options(initial_layout=[0, 1, 2], seed_transpiler=15) value = estimator.run(qc, op, shots=10000).result().values[0] + if optionals.HAS_AER and not isinstance(backend, FakeBackendSimple): - self.assertEqual(value, -0.8902) + self.assertEqual(value, -0.8908) else: self.assertEqual(value, -1) diff --git a/test/python/primitives/test_backend_sampler.py b/test/python/primitives/test_backend_sampler.py index b4b8d79e32a3..72d40aa8382c 100644 --- a/test/python/primitives/test_backend_sampler.py +++ b/test/python/primitives/test_backend_sampler.py @@ -26,13 +26,18 @@ from qiskit.circuit.library import RealAmplitudes from qiskit.primitives import BackendSampler, SamplerResult from qiskit.providers import JobStatus, JobV1 -from qiskit.providers.fake_provider import FakeNairobi, FakeNairobiV2 +from qiskit.providers.fake_provider import FakeNairobi +from qiskit.providers.backend_compat import BackendV2Converter +from qiskit.providers.fake_provider.fake_backend_v2 import FakeBackendSimple from qiskit.providers.basicaer import QasmSimulatorPy from qiskit.test import QiskitTestCase from qiskit.transpiler import PassManager from qiskit.utils import optionals -BACKENDS = [FakeNairobi(), FakeNairobiV2()] +BACKENDS = [ + FakeNairobi(), + BackendV2Converter(FakeNairobi()), +] class CallbackPass(DummyAP): @@ -243,7 +248,7 @@ def test_run_empty_parameter(self, backend): self.assertEqual(len(result.quasi_dists), 1) for q_d in result.quasi_dists: quasi_dist = {k: v for k, v in q_d.items() if v != 0.0} - self.assertDictAlmostEqual(quasi_dist, {0: 1.0}, delta=0.1) + self.assertDictAlmostEqual(quasi_dist, {0: 1.0}, delta=0.15) self.assertEqual(len(result.metadata), 1) with self.subTest("two circuits"): @@ -251,7 +256,7 @@ def test_run_empty_parameter(self, backend): self.assertEqual(len(result.quasi_dists), 2) for q_d in result.quasi_dists: quasi_dist = {k: v for k, v in q_d.items() if v != 0.0} - self.assertDictAlmostEqual(quasi_dist, {0: 1.0}, delta=0.1) + self.assertDictAlmostEqual(quasi_dist, {0: 1.0}, delta=0.15) self.assertEqual(len(result.metadata), 2) @combine(backend=BACKENDS) @@ -300,23 +305,25 @@ def test_primitive_job_status_done(self, backend): def test_primitive_job_size_limit_backend_v2(self): """Test primitive respects backend's job size limit.""" - class FakeNairobiLimitedCircuits(FakeNairobiV2): - """FakeNairobiV2 with job size limit.""" + class FakeBackendLimitedCircuits(FakeBackendSimple): + """FakeBackend V2 with job size limit.""" @property def max_circuits(self): return 1 + backend = FakeBackendLimitedCircuits() qc = QuantumCircuit(1) qc.measure_all() qc2 = QuantumCircuit(1) qc2.x(0) qc2.measure_all() - sampler = BackendSampler(backend=FakeNairobiLimitedCircuits()) + sampler = BackendSampler(backend=backend) result = sampler.run([qc, qc2]).result() self.assertIsInstance(result, SamplerResult) self.assertEqual(len(result.quasi_dists), 2) + print(result.quasi_dists) self.assertDictAlmostEqual(result.quasi_dists[0], {0: 1}, 0.1) self.assertDictAlmostEqual(result.quasi_dists[1], {1: 1}, 0.1) @@ -353,8 +360,8 @@ def test_circuit_with_dynamic_circuit(self): qc.break_loop().c_if(0, True) backend = Aer.get_backend("aer_simulator") - backend.set_options(seed_simulator=15) sampler = BackendSampler(backend, skip_transpilation=True) + sampler.set_options(seed_simulator=15) sampler.set_transpile_options(seed_transpiler=15) result = sampler.run(qc).result() self.assertDictAlmostEqual(result.quasi_dists[0], {0: 0.5029296875, 1: 0.4970703125}) diff --git a/test/python/pulse/test_builder_v2.py b/test/python/pulse/test_builder_v2.py index 1f194a9d3c29..0521e371138f 100644 --- a/test/python/pulse/test_builder_v2.py +++ b/test/python/pulse/test_builder_v2.py @@ -19,17 +19,77 @@ from qiskit.pulse.instructions import directives from qiskit.pulse.transforms import target_qobj_transform -from qiskit.providers.fake_provider import FakeMumbaiV2 +from qiskit.providers.fake_provider import FakeGeneric from qiskit.pulse import instructions from qiskit.test import QiskitTestCase +MUMBAI_CMAP = [ + [0, 1], + [1, 0], + [1, 2], + [1, 4], + [2, 1], + [2, 3], + [3, 2], + [3, 5], + [4, 1], + [4, 7], + [5, 3], + [5, 8], + [6, 7], + [7, 4], + [7, 6], + [7, 10], + [8, 5], + [8, 9], + [8, 11], + [9, 8], + [10, 7], + [10, 12], + [11, 8], + [11, 14], + [12, 10], + [12, 13], + [12, 15], + [13, 12], + [13, 14], + [14, 11], + [14, 13], + [14, 16], + [15, 12], + [15, 18], + [16, 14], + [16, 19], + [17, 18], + [18, 15], + [18, 17], + [18, 21], + [19, 16], + [19, 20], + [19, 22], + [20, 19], + [21, 18], + [21, 23], + [22, 19], + [22, 25], + [23, 21], + [23, 24], + [24, 23], + [24, 25], + [25, 22], + [25, 24], + [25, 26], + [26, 25], +] + class TestBuilderV2(QiskitTestCase): """Test the pulse builder context with backendV2.""" def setUp(self): super().setUp() - self.backend = FakeMumbaiV2() + basis_gates = ["cx", "id", "rz", "sx", "x"] + self.backend = FakeGeneric(basis_gates=basis_gates, num_qubits=27, coupling_map=MUMBAI_CMAP) def assertScheduleEqual(self, program, target): """Assert an error when two pulse programs are not equal. diff --git a/test/python/qpy/test_circuit_load_from_qpy.py b/test/python/qpy/test_circuit_load_from_qpy.py index ba6aeb81f290..b545686ca5eb 100644 --- a/test/python/qpy/test_circuit_load_from_qpy.py +++ b/test/python/qpy/test_circuit_load_from_qpy.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2022. +# (C) Copyright IBM 2022, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -18,7 +18,7 @@ from ddt import ddt, data from qiskit.circuit import QuantumCircuit, QuantumRegister, Qubit -from qiskit.providers.fake_provider import FakeHanoi, FakeSherbrooke +from qiskit.providers.fake_provider import FakeHanoi, FakeGeneric from qiskit.exceptions import QiskitError from qiskit.qpy import dump, load, formats from qiskit.qpy.common import QPY_VERSION @@ -99,7 +99,7 @@ def test_transpile_layout(self, opt_level): qc.h(0) qc.cx(0, 1) qc.measure_all() - backend = FakeSherbrooke() + backend = FakeGeneric(basis_gates=["cx", "id", "rz", "sx", "x"], num_qubits=127) tqc = transpile(qc, backend, optimization_level=opt_level) self.assert_roundtrip_equal(tqc) @@ -113,7 +113,7 @@ def test_transpile_with_routing(self, opt_level): qc.cx(0, 3) qc.cx(0, 4) qc.measure_all() - backend = FakeSherbrooke() + backend = FakeGeneric(basis_gates=["cx", "id", "rz", "sx", "x"], num_qubits=127) tqc = transpile(qc, backend, optimization_level=opt_level) self.assert_roundtrip_equal(tqc) @@ -124,7 +124,7 @@ def test_transpile_layout_explicit_None_final_layout(self, opt_level): qc.h(0) qc.cx(0, 1) qc.measure_all() - backend = FakeSherbrooke() + backend = FakeGeneric(basis_gates=["cx", "id", "rz", "sx", "x"], num_qubits=127) tqc = transpile(qc, backend, optimization_level=opt_level) tqc.layout.final_layout = None self.assert_roundtrip_equal(tqc) @@ -149,7 +149,7 @@ def test_custom_register_name(self, opt_level): qc.cx(0, 3) qc.cx(0, 4) qc.measure_all() - backend = FakeSherbrooke() + backend = FakeGeneric(basis_gates=["cx", "id", "rz", "sx", "x"], num_qubits=127) tqc = transpile(qc, backend, optimization_level=opt_level) self.assert_roundtrip_equal(tqc) @@ -161,7 +161,7 @@ def test_no_register(self, opt_level): qc.h(0) qc.cx(0, 1) qc.measure_all() - backend = FakeSherbrooke() + backend = FakeGeneric(basis_gates=["cx", "id", "rz", "sx", "x"], num_qubits=127) tqc = transpile(qc, backend, optimization_level=opt_level) # Manually validate to deal with qubit equality needing exact objects qpy_file = io.BytesIO() diff --git a/test/python/transpiler/test_apply_layout.py b/test/python/transpiler/test_apply_layout.py index d156db19dc18..a0d8117a774c 100644 --- a/test/python/transpiler/test_apply_layout.py +++ b/test/python/transpiler/test_apply_layout.py @@ -21,7 +21,7 @@ from qiskit.transpiler.passes import ApplyLayout, SetLayout from qiskit.transpiler.exceptions import TranspilerError from qiskit.transpiler.preset_passmanagers import common -from qiskit.providers.fake_provider import FakeVigoV2 +from qiskit.providers.fake_provider import FakeGeneric from qiskit.transpiler import PassManager @@ -125,7 +125,7 @@ def test_final_layout_is_updated(self): for i in range(5): qc.cx(i % qubits, int(i + qubits / 2) % qubits) initial_pm = PassManager([SetLayout([1, 3, 4])]) - cmap = FakeVigoV2().coupling_map + cmap = FakeGeneric(basis_gates=["cx", "id", "rz", "sx", "x"], num_qubits=5).coupling_map initial_pm += common.generate_embed_passmanager(cmap) first_layout_circ = initial_pm.run(qc) out_pass = ApplyLayout() diff --git a/test/python/transpiler/test_csp_layout.py b/test/python/transpiler/test_csp_layout.py index 7ab369d7a6e8..3c736db82a30 100644 --- a/test/python/transpiler/test_csp_layout.py +++ b/test/python/transpiler/test_csp_layout.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2019. +# (C) Copyright IBM 2019, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -20,9 +20,29 @@ from qiskit.transpiler.passes import CSPLayout from qiskit.converters import circuit_to_dag from qiskit.test import QiskitTestCase -from qiskit.providers.fake_provider import FakeTenerife, FakeRueschlikon, FakeTokyo, FakeYorktownV2 +from qiskit.providers.fake_provider.fake_generic import GenericTarget from qiskit.utils import optionals +# fmt: off +TENERIFE_CMAP = [[1, 0], [2, 0], [2, 1], [3, 2], [3, 4], [4, 2]] +RUESCHLIKON_CMAP = [[1, 0], [1, 2], [2, 3], [3, 4], [3, 14], [5, 4], + [6, 5], [6, 7], [6, 11], [7, 10], [8, 7], [9, 8], + [9, 10], [11, 10], [12, 5], [12, 11], [12, 13], + [13, 4], [13, 14], [15, 0], [15, 2], [15, 14]] +TOKYO_CMAP = [[0, 1], [0, 5], [1, 0], [1, 2], [1, 6], [1, 7], + [2, 1], [2, 6], [3, 8], [4, 8], [4, 9], [5, 0], + [5, 6], [5, 10], [5, 11], [6, 1], [6, 2], [6, 5], + [6, 7], [6, 10], [6, 11], [7, 1], [7, 6], [7, 8], + [7, 12], [8, 3], [8, 4], [8, 7], [8, 9], [8, 12], + [8, 13], [9, 4], [9, 8], [10, 5], [10, 6], [10, 11], + [10, 15], [11, 5], [11, 6], [11, 10], [11, 12], [11, 16], + [11, 17], [12, 7], [12, 8], [12, 11], [12, 13], [12, 16], + [13, 8], [13, 12], [13, 14], [13, 18], [13, 19], [14, 13], + [14, 18], [14, 19], [15, 10], [15, 16], [16, 11], [16, 12], + [16, 15], [16, 17], [17, 11], [17, 16], [17, 18], + [18, 13], [18, 14], [18, 17], [19, 13], [19, 14]] +# fmt: on + @unittest.skipUnless(optionals.HAS_CONSTRAINT, "needs python-constraint") class TestCSPLayout(QiskitTestCase): @@ -56,8 +76,7 @@ def test_3q_circuit_5q_coupling(self): | / 4 """ - cmap5 = FakeTenerife().configuration().coupling_map - + cmap5 = TENERIFE_CMAP qr = QuantumRegister(3, "qr") circuit = QuantumCircuit(qr) circuit.cx(qr[1], qr[0]) # qr1 -> qr0 @@ -82,13 +101,31 @@ def test_3q_circuit_5q_coupling_with_target(self): | / 4 """ - target = FakeYorktownV2().target + yorktown_cm = [ + [0, 1], + [0, 2], + [1, 0], + [1, 2], + [2, 0], + [2, 1], + [2, 3], + [2, 4], + [3, 2], + [3, 4], + [4, 2], + [4, 3], + ] + target = GenericTarget( + basis_gates=["cx", "id", "rz", "sx", "x"], + num_qubits=5, + coupling_map=CouplingMap(yorktown_cm), + ) qr = QuantumRegister(3, "qr") circuit = QuantumCircuit(qr) circuit.cx(qr[1], qr[0]) # qr1 -> qr0 circuit.cx(qr[0], qr[2]) # qr0 -> qr2 - circuit.cx(qr[1], qr[2]) # qr1 -> qr2 + circuit.cx(qr[1], qr[2]) # qr1 -> qr2s dag = circuit_to_dag(circuit) pass_ = CSPLayout(target, strict_direction=False, seed=self.seed) @@ -106,7 +143,7 @@ def test_9q_circuit_16q_coupling(self): | | | | | | | | q0[2] - q1[4] -- 14 ---- 13 ---- 12 ---- 11 ---- 10 --- 9 """ - cmap16 = FakeRueschlikon().configuration().coupling_map + cmap16 = RUESCHLIKON_CMAP qr0 = QuantumRegister(4, "q0") qr1 = QuantumRegister(5, "q1") @@ -157,7 +194,7 @@ def test_3q_circuit_5q_coupling_sd(self): ↑ ↙ 4 """ - cmap5 = FakeTenerife().configuration().coupling_map + cmap5 = TENERIFE_CMAP qr = QuantumRegister(3, "qr") circuit = QuantumCircuit(qr) @@ -181,7 +218,7 @@ def test_9q_circuit_16q_coupling_sd(self): ↓ ↑ ↓ ↓ ↑ ↓ ↓ ↑ q0[2] ← q1[4] → 14 ← 13 ← 12 → 11 → 10 ← 9 """ - cmap16 = FakeRueschlikon().configuration().coupling_map + cmap16 = RUESCHLIKON_CMAP qr0 = QuantumRegister(4, "q0") qr1 = QuantumRegister(5, "q1") @@ -213,7 +250,7 @@ def test_5q_circuit_16q_coupling_no_solution(self): q0[0] q0[3] ↙ ↘ q0[4] """ - cmap16 = FakeRueschlikon().configuration().coupling_map + cmap16 = RUESCHLIKON_CMAP qr = QuantumRegister(5, "q") circuit = QuantumCircuit(qr) @@ -269,7 +306,7 @@ def create_hard_dag(): def test_time_limit(self): """Hard to solve situations hit the time limit""" dag = TestCSPLayout.create_hard_dag() - coupling_map = CouplingMap(FakeTokyo().configuration().coupling_map) + coupling_map = CouplingMap(TOKYO_CMAP) pass_ = CSPLayout(coupling_map, call_limit=None, time_limit=1) start = process_time() @@ -282,7 +319,7 @@ def test_time_limit(self): def test_call_limit(self): """Hard to solve situations hit the call limit""" dag = TestCSPLayout.create_hard_dag() - coupling_map = CouplingMap(FakeTokyo().configuration().coupling_map) + coupling_map = CouplingMap(TOKYO_CMAP) pass_ = CSPLayout(coupling_map, call_limit=1, time_limit=None) start = process_time() @@ -297,7 +334,7 @@ def test_seed(self): seed_1 = 42 seed_2 = 43 - cmap5 = FakeTenerife().configuration().coupling_map + cmap5 = TENERIFE_CMAP qr = QuantumRegister(3, "qr") circuit = QuantumCircuit(qr) diff --git a/test/python/transpiler/test_parameterizedgate_translator.py b/test/python/transpiler/test_parameterizedgate_translator.py index 28fa9f140548..343117cb3a26 100644 --- a/test/python/transpiler/test_parameterizedgate_translator.py +++ b/test/python/transpiler/test_parameterizedgate_translator.py @@ -19,7 +19,7 @@ from qiskit.circuit.library import TwoLocal from qiskit.exceptions import QiskitError from qiskit.transpiler.passes import TranslateParameterizedGates -from qiskit.providers.fake_provider import FakeAthensV2 +from qiskit.providers.fake_provider import FakeGeneric class TestTranslateParameterized(QiskitTestCase): @@ -64,7 +64,7 @@ def test_only_parameterized_is_unrolled(self): def test_target(self): """Test unrolling with a target.""" - target = FakeAthensV2().target + target = FakeGeneric(basis_gates=["cx", "id", "rz", "sx", "x"], num_qubits=5).target circuit = TwoLocal(2, "rz", "cx", reps=2, entanglement="linear") translator = TranslateParameterizedGates(target=target) diff --git a/test/python/transpiler/test_passmanager_config.py b/test/python/transpiler/test_passmanager_config.py index 96721e1884a3..6551d13f3ae2 100644 --- a/test/python/transpiler/test_passmanager_config.py +++ b/test/python/transpiler/test_passmanager_config.py @@ -15,7 +15,7 @@ from qiskit import QuantumRegister from qiskit.providers.backend import Backend from qiskit.test import QiskitTestCase -from qiskit.providers.fake_provider import FakeMelbourne, FakeArmonk, FakeHanoi, FakeHanoiV2 +from qiskit.providers.fake_provider import FakeMelbourne, FakeArmonk, FakeHanoi, FakeGeneric from qiskit.providers.basicaer import QasmSimulatorPy from qiskit.transpiler.coupling import CouplingMap from qiskit.transpiler.passmanager_config import PassManagerConfig @@ -40,7 +40,7 @@ def test_config_from_backend(self): def test_config_from_backend_v2(self): """Test from_backend() with a BackendV2 instance.""" - backend = FakeHanoiV2() + backend = FakeGeneric(basis_gates=["cx", "id", "rz", "sx", "x"], num_qubits=27) config = PassManagerConfig.from_backend(backend) self.assertEqual(config.basis_gates, backend.operation_names) self.assertEqual(config.inst_map, backend.instruction_schedule_map) diff --git a/test/python/transpiler/test_pulse_gate_pass.py b/test/python/transpiler/test_pulse_gate_pass.py index b5f8c03c4711..1b52c15e0abd 100644 --- a/test/python/transpiler/test_pulse_gate_pass.py +++ b/test/python/transpiler/test_pulse_gate_pass.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2021. +# (C) Copyright IBM 2021, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -15,10 +15,14 @@ import ddt from qiskit import pulse, circuit, transpile -from qiskit.providers.fake_provider import FakeAthens, FakeAthensV2 +from qiskit.providers.fake_provider import FakeAthens, FakeGeneric +from qiskit.providers.fake_provider.fake_generic import GenericTarget from qiskit.quantum_info.random import random_unitary +from qiskit.transpiler import CouplingMap from qiskit.test import QiskitTestCase +ATHENS_CMAP = CouplingMap([[0, 1], [1, 0], [1, 2], [2, 1], [2, 3], [3, 2], [3, 4], [4, 3]]) + @ddt.ddt class TestPulseGate(QiskitTestCase): @@ -69,8 +73,9 @@ def test_transpile_with_bare_backend(self): def test_transpile_with_backend_target(self): """Test transpile without custom calibrations from target.""" - backend = FakeAthensV2() - target = backend.target + target = GenericTarget( + num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=ATHENS_CMAP + ) qc = circuit.QuantumCircuit(2) qc.sx(0) @@ -109,8 +114,12 @@ def test_transpile_with_custom_basis_gate(self): def test_transpile_with_custom_basis_gate_in_target(self): """Test transpile with custom calibrations.""" - backend = FakeAthensV2() - target = backend.target + target = GenericTarget( + num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=ATHENS_CMAP + ) + target.add_calibrations_from_instruction_schedule_map( + FakeAthens().defaults().instruction_schedule_map + ) target["sx"][(0,)].calibration = self.custom_sx_q0 target["sx"][(1,)].calibration = self.custom_sx_q1 @@ -282,7 +291,12 @@ def test_transpile_with_both_instmap_and_empty_target(self, opt_level): instmap.add("cx", (0, 1), self.custom_cx_q01) # This doesn't have custom schedule definition - target = FakeAthensV2().target + target = GenericTarget( + num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=ATHENS_CMAP + ) + target.add_calibrations_from_instruction_schedule_map( + FakeAthens().defaults().instruction_schedule_map + ) qc = circuit.QuantumCircuit(2) qc.append(random_unitary(4, seed=123), [0, 1]) @@ -322,9 +336,14 @@ def test_transpile_with_instmap_with_v2backend(self, opt_level): qc.append(random_unitary(4, seed=123), [0, 1]) qc.measure_all() + backend = FakeGeneric(num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"]) + backend.target.add_calibrations_from_instruction_schedule_map( + FakeAthens().defaults().instruction_schedule_map + ) + transpiled_qc = transpile( qc, - FakeAthensV2(), + backend, optimization_level=opt_level, inst_map=instmap, initial_layout=[0, 1], @@ -361,9 +380,10 @@ def test_transpile_with_instmap_with_v2backend_with_custom_gate(self, opt_level) qc.append(gate, [0]) qc.measure_all() + backend = FakeGeneric(num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"]) transpiled_qc = transpile( qc, - FakeAthensV2(), + backend, optimization_level=opt_level, inst_map=instmap, initial_layout=[0], @@ -383,7 +403,7 @@ def test_transpile_with_instmap_not_mutate_backend(self): This should not override the source object since the same backend may be used for future transpile without intention of instruction overriding. """ - backend = FakeAthensV2() + backend = FakeGeneric(num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"]) original_sx0 = backend.target["sx"][(0,)].calibration instmap = FakeAthens().defaults().instruction_schedule_map @@ -395,7 +415,7 @@ def test_transpile_with_instmap_not_mutate_backend(self): transpiled_qc = transpile( qc, - FakeAthensV2(), + backend, inst_map=instmap, initial_layout=[0], ) diff --git a/test/python/transpiler/test_sabre_layout.py b/test/python/transpiler/test_sabre_layout.py index abc48f63ae68..64959df83d82 100644 --- a/test/python/transpiler/test_sabre_layout.py +++ b/test/python/transpiler/test_sabre_layout.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2017, 2020. +# (C) Copyright IBM 2017, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -24,7 +24,7 @@ from qiskit.converters import circuit_to_dag from qiskit.test import QiskitTestCase from qiskit.compiler.transpiler import transpile -from qiskit.providers.fake_provider import FakeAlmaden, FakeAlmadenV2 +from qiskit.providers.fake_provider import FakeAlmaden, FakeGeneric from qiskit.providers.fake_provider import FakeKolkata from qiskit.providers.fake_provider import FakeMontreal from qiskit.transpiler.passes.layout.sabre_pre_layout import SabrePreLayout @@ -160,7 +160,9 @@ def test_6q_circuit_20q_coupling_with_target(self): circuit.cx(qr1[1], qr0[0]) dag = circuit_to_dag(circuit) - target = FakeAlmadenV2().target + target = FakeGeneric( + num_qubits=20, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=self.cmap20 + ).target pass_ = SabreLayout(target, seed=0, swap_trials=32, layout_trials=32) pass_.run(dag) @@ -422,7 +424,10 @@ def test_starting_layout(self): def test_integration_with_pass_manager(self): """Tests SabrePreLayoutIntegration with the rest of PassManager pipeline.""" - backend = FakeAlmadenV2() + cmap20 = FakeAlmaden().configuration().coupling_map + backend = FakeGeneric( + num_qubits=20, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=cmap20, seed=42 + ) pm = generate_preset_pass_manager(1, backend, seed_transpiler=0) pm.pre_layout = PassManager([SabrePreLayout(backend.target)]) qct = pm.run(self.circuit) diff --git a/test/python/transpiler/test_sabre_swap.py b/test/python/transpiler/test_sabre_swap.py index 100e7882c782..1ff5be6d2319 100644 --- a/test/python/transpiler/test_sabre_swap.py +++ b/test/python/transpiler/test_sabre_swap.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2017, 2020. +# (C) Copyright IBM 2017, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -24,7 +24,8 @@ from qiskit.circuit.random import random_circuit from qiskit.compiler.transpiler import transpile from qiskit.converters import circuit_to_dag, dag_to_circuit -from qiskit.providers.fake_provider import FakeMumbai, FakeMumbaiV2 +from qiskit.providers.fake_provider import FakeMumbai +from qiskit.providers.fake_provider.fake_generic import GenericTarget from qiskit.transpiler.passes import SabreSwap, TrivialLayout, CheckMap from qiskit.transpiler import CouplingMap, Layout, PassManager, Target, TranspilerError from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit @@ -32,6 +33,8 @@ from qiskit.test._canonical import canonicalize_control_flow from qiskit.utils import optionals +MUMBAI_CMAP = CouplingMap(FakeMumbai().configuration().coupling_map) + def looping_circuit(uphill_swaps=1, additional_local_minimum_gates=0): """A circuit that causes SabreSwap to loop infinitely. @@ -1325,6 +1328,7 @@ class TestSabreSwapRandomCircuitValidOutput(QiskitTestCase): def setUpClass(cls): super().setUpClass() cls.backend = FakeMumbai() + cls.backend.configuration().coupling_map = MUMBAI_CMAP cls.coupling_edge_set = {tuple(x) for x in cls.backend.configuration().coupling_map} cls.basis_gates = set(cls.backend.configuration().basis_gates) cls.basis_gates.update(["for_loop", "while_loop", "if_else"]) @@ -1385,7 +1389,9 @@ def test_random_circuit_no_control_flow_target(self, size): routing_method="sabre", layout_method="sabre", seed_transpiler=12342, - target=FakeMumbaiV2().target, + target=GenericTarget( + num_qubits=27, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=MUMBAI_CMAP + ), ) self.assert_valid_circuit(tqc) diff --git a/test/python/transpiler/test_stochastic_swap.py b/test/python/transpiler/test_stochastic_swap.py index 6ef3c01f2924..7d98f53f590e 100644 --- a/test/python/transpiler/test_stochastic_swap.py +++ b/test/python/transpiler/test_stochastic_swap.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2017, 2018. +# (C) Copyright IBM 2017, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -26,11 +26,14 @@ from qiskit.test._canonical import canonicalize_control_flow from qiskit.transpiler.passes.utils import CheckMap from qiskit.circuit.random import random_circuit -from qiskit.providers.fake_provider import FakeMumbai, FakeMumbaiV2 +from qiskit.providers.fake_provider import FakeMumbai +from qiskit.providers.fake_provider.fake_generic import GenericTarget from qiskit.compiler.transpiler import transpile from qiskit.circuit import ControlFlowOp, Clbit, CASE_DEFAULT from qiskit.circuit.classical import expr +MUMBAI_CMAP = CouplingMap(FakeMumbai().configuration().coupling_map) + @ddt class TestStochasticSwap(QiskitTestCase): @@ -1531,7 +1534,11 @@ def test_random_circuit_no_control_flow_target(self, size): routing_method="stochastic", layout_method="dense", seed_transpiler=12342, - target=FakeMumbaiV2().target, + target=GenericTarget( + num_qubits=27, + basis_gates=["cx", "id", "rz", "sx", "x"], + coupling_map=MUMBAI_CMAP, + ), ) self.assert_valid_circuit(tqc) diff --git a/test/python/transpiler/test_target.py b/test/python/transpiler/test_target.py index e6eb57042ea7..2f9443f11ba8 100644 --- a/test/python/transpiler/test_target.py +++ b/test/python/transpiler/test_target.py @@ -44,11 +44,11 @@ from qiskit.transpiler import InstructionProperties from qiskit.test import QiskitTestCase from qiskit.providers.fake_provider import ( - FakeBackendV2, + FakeGeneric, FakeMumbaiFractionalCX, FakeVigo, FakeNairobi, - FakeGeneva, + FakeBackendV2, ) @@ -1304,7 +1304,7 @@ def test_timing_constraints(self): ) def test_default_instmap_has_no_custom_gate(self): - backend = FakeGeneva() + backend = FakeGeneric(basis_gates=["cx", "id", "rz", "sx", "x"], num_qubits=27) target = backend.target # This copies .calibraiton of InstructionProperties of each instruction diff --git a/test/python/transpiler/test_vf2_layout.py b/test/python/transpiler/test_vf2_layout.py index b1eb02a489d9..af538356b2e1 100644 --- a/test/python/transpiler/test_vf2_layout.py +++ b/test/python/transpiler/test_vf2_layout.py @@ -29,12 +29,9 @@ from qiskit.converters import circuit_to_dag from qiskit.test import QiskitTestCase from qiskit.providers.fake_provider import ( - FakeTenerife, - FakeVigoV2, - FakeRueschlikon, - FakeManhattan, FakeYorktown, - FakeGuadalupeV2, + FakeManhattan, + FakeGeneric, ) from qiskit.circuit import Measure from qiskit.circuit.library import GraphState, CXGate, XGate, HGate @@ -42,6 +39,32 @@ from qiskit.transpiler.target import InstructionProperties from qiskit.transpiler.preset_passmanagers.common import generate_embed_passmanager +TENERIFE_CMAP = [[1, 0], [2, 0], [2, 1], [3, 2], [3, 4], [4, 2]] +RUESCHLIKON_CMAP = [ + [1, 0], + [1, 2], + [2, 3], + [3, 4], + [3, 14], + [5, 4], + [6, 5], + [6, 7], + [6, 11], + [7, 10], + [8, 7], + [9, 8], + [9, 10], + [11, 10], + [12, 5], + [12, 11], + [12, 13], + [13, 4], + [13, 14], + [15, 0], + [15, 2], + [15, 14], +] + class LayoutTestCase(QiskitTestCase): """VF2Layout assertions""" @@ -332,7 +355,7 @@ def test_5q_circuit_Rueschlikon_no_solution(self): q0[0] q0[3] ↙ ↘ q0[4] """ - cmap16 = FakeRueschlikon().configuration().coupling_map + cmap16 = RUESCHLIKON_CMAP qr = QuantumRegister(5, "q") circuit = QuantumCircuit(qr) @@ -356,7 +379,7 @@ def test_9q_circuit_Rueschlikon_sd(self): ↓ ↑ ↓ ↓ ↑ ↓ ↓ ↑ 0 ← 15 → 14 ← 13 ← 12 → 11 → 10 ← 9 """ - cmap16 = CouplingMap(FakeRueschlikon().configuration().coupling_map) + cmap16 = CouplingMap(RUESCHLIKON_CMAP) qr0 = QuantumRegister(4, "q0") qr1 = QuantumRegister(5, "q1") @@ -379,7 +402,7 @@ def test_4q_circuit_Tenerife_loose_nodes(self): ↑ ↙ 4 """ - cmap5 = CouplingMap(FakeTenerife().configuration().coupling_map) + cmap5 = CouplingMap(TENERIFE_CMAP) qr = QuantumRegister(4, "q") circuit = QuantumCircuit(qr) @@ -399,7 +422,7 @@ def test_3q_circuit_Tenerife_sd(self): ↑ ↙ ↑ ↙ 4 qr0 """ - cmap5 = CouplingMap(FakeTenerife().configuration().coupling_map) + cmap5 = CouplingMap(TENERIFE_CMAP) qr = QuantumRegister(3, "qr") circuit = QuantumCircuit(qr) @@ -423,7 +446,7 @@ def test_9q_circuit_Rueschlikon(self): | | | | | | | | q1_2 - q1_3 - q0_0 - 13 - q0_3 - 11 - q1_4 - q0_2 """ - cmap16 = CouplingMap(FakeRueschlikon().configuration().coupling_map) + cmap16 = CouplingMap(RUESCHLIKON_CMAP) qr0 = QuantumRegister(4, "q0") qr1 = QuantumRegister(5, "q1") @@ -446,7 +469,7 @@ def test_3q_circuit_Tenerife(self): ↑ ↙ | / 4 qr0 """ - cmap5 = CouplingMap(FakeTenerife().configuration().coupling_map) + cmap5 = CouplingMap(TENERIFE_CMAP) qr = QuantumRegister(3, "q") circuit = QuantumCircuit(qr) @@ -461,7 +484,7 @@ def test_3q_circuit_Tenerife(self): def test_3q_circuit_vigo_with_custom_scores(self): """Test custom ErrorMap from analysis pass are used for scoring.""" - backend = FakeVigoV2() + backend = FakeGeneric(basis_gates=["cx", "id", "rz", "sx", "x"], num_qubits=5) target = backend.target class FakeScore(AnalysisPass): @@ -534,7 +557,7 @@ def test_seed(self): seed_1 = 42 seed_2 = 45 - cmap5 = FakeTenerife().configuration().coupling_map + cmap5 = TENERIFE_CMAP qr = QuantumRegister(3, "qr") circuit = QuantumCircuit(qr) @@ -563,7 +586,7 @@ def test_3_q_gate(self): """The pass does not handle gates with more than 2 qubits""" seed_1 = 42 - cmap5 = FakeTenerife().configuration().coupling_map + cmap5 = TENERIFE_CMAP qr = QuantumRegister(3, "qr") circuit = QuantumCircuit(qr) @@ -716,7 +739,7 @@ def test_qregs_valid_layout_output(self): Reproduce from https://github.com/Qiskit/qiskit-terra/issues/8667 """ - backend = FakeGuadalupeV2() + backend = FakeGeneric(basis_gates=["cx", "id", "rz", "sx", "x"], num_qubits=16) qr = QuantumRegister(16, name="qr") cr = ClassicalRegister(5) qc = QuantumCircuit(qr, cr) diff --git a/test/python/transpiler/test_vf2_post_layout.py b/test/python/transpiler/test_vf2_post_layout.py index da0dbb274c2b..5c12ff11a815 100644 --- a/test/python/transpiler/test_vf2_post_layout.py +++ b/test/python/transpiler/test_vf2_post_layout.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2021. +# (C) Copyright IBM 2021, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -21,11 +21,30 @@ from qiskit.transpiler.passes.layout.vf2_post_layout import VF2PostLayout, VF2PostLayoutStopReason from qiskit.converters import circuit_to_dag from qiskit.test import QiskitTestCase -from qiskit.providers.fake_provider import FakeLima, FakeYorktown, FakeLimaV2, FakeYorktownV2 +from qiskit.providers.fake_provider import FakeLima, FakeYorktown, FakeGeneric +from qiskit.providers.fake_provider.fake_generic import GenericTarget from qiskit.circuit import Qubit from qiskit.compiler.transpiler import transpile from qiskit.transpiler.target import Target, InstructionProperties +LIMA_CM = CouplingMap([[0, 1], [1, 0], [1, 2], [1, 3], [2, 1], [3, 1], [3, 4], [4, 3]]) +YORKTOWN_CM = CouplingMap( + [ + [0, 1], + [0, 2], + [1, 0], + [1, 2], + [2, 0], + [2, 1], + [2, 3], + [2, 4], + [3, 2], + [3, 4], + [4, 2], + [4, 3], + ] +) + class TestVF2PostLayout(QiskitTestCase): """Tests the VF2Layout pass""" @@ -114,8 +133,10 @@ def test_empty_circuit(self): def test_empty_circuit_v2(self): """Test no solution found for empty circuit with v2 backend""" qc = QuantumCircuit(2, 2) - backend = FakeLimaV2() - vf2_pass = VF2PostLayout(target=backend.target) + target = GenericTarget( + num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=LIMA_CM + ) + vf2_pass = VF2PostLayout(target=target) vf2_pass.run(circuit_to_dag(qc)) self.assertEqual( vf2_pass.property_set["VF2PostLayout_stop_reason"], @@ -153,8 +174,10 @@ def test_skip_3q_circuit_v2(self): """Test that the pass is a no-op on circuits with >2q gates with a target.""" qc = QuantumCircuit(3) qc.ccx(0, 1, 2) - backend = FakeLimaV2() - vf2_pass = VF2PostLayout(target=backend.target) + target = GenericTarget( + num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=LIMA_CM + ) + vf2_pass = VF2PostLayout(target=target) vf2_pass.run(circuit_to_dag(qc)) self.assertEqual( vf2_pass.property_set["VF2PostLayout_stop_reason"], VF2PostLayoutStopReason.MORE_THAN_2Q @@ -165,8 +188,10 @@ def test_skip_3q_circuit_control_flow_v2(self): qc = QuantumCircuit(3) with qc.for_loop((1,)): qc.ccx(0, 1, 2) - backend = FakeLimaV2() - vf2_pass = VF2PostLayout(target=backend.target) + target = GenericTarget( + num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=LIMA_CM + ) + vf2_pass = VF2PostLayout(target=target) vf2_pass.run(circuit_to_dag(qc)) self.assertEqual( vf2_pass.property_set["VF2PostLayout_stop_reason"], VF2PostLayoutStopReason.MORE_THAN_2Q @@ -274,7 +299,9 @@ def test_2q_circuit_5q_backend_max_trials(self): def test_best_mapping_ghz_state_full_device_multiple_qregs_v2(self): """Test best mappings with multiple registers""" - backend = FakeLimaV2() + backend = FakeGeneric( + num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=LIMA_CM + ) qr_a = QuantumRegister(2) qr_b = QuantumRegister(3) qc = QuantumCircuit(qr_a, qr_b) @@ -297,7 +324,9 @@ def test_2q_circuit_5q_backend_v2(self): 0 - 1 qr1 - qr0 """ - backend = FakeYorktownV2() + backend = FakeGeneric( + num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=YORKTOWN_CM + ) qr = QuantumRegister(2, "qr") circuit = QuantumCircuit(qr) @@ -315,7 +344,9 @@ def test_2q_circuit_5q_backend_v2_control_flow(self): 0 - 1 qr1 - qr0 """ - backend = FakeYorktownV2() + target = GenericTarget( + num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=YORKTOWN_CM + ) circuit = QuantumCircuit(2, 1) with circuit.for_loop((1,)): @@ -328,18 +359,20 @@ def test_2q_circuit_5q_backend_v2_control_flow(self): initial_layout = Layout(dict(enumerate(circuit.qubits))) circuit._layout = initial_layout dag = circuit_to_dag(circuit) - pass_ = VF2PostLayout(target=backend.target, seed=self.seed) + pass_ = VF2PostLayout(target=target, seed=self.seed) pass_.run(dag) - self.assertLayoutV2(dag, backend.target, pass_.property_set) + self.assertLayoutV2(dag, target, pass_.property_set) self.assertNotEqual(pass_.property_set["post_layout"], initial_layout) def test_target_invalid_2q_gate(self): """Test that we don't find a solution with a gate outside target.""" - backend = FakeYorktownV2() + target = GenericTarget( + num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=YORKTOWN_CM + ) qc = QuantumCircuit(2) qc.ecr(0, 1) dag = circuit_to_dag(qc) - pass_ = VF2PostLayout(target=backend.target, seed=self.seed) + pass_ = VF2PostLayout(target=target, seed=self.seed) pass_.run(dag) self.assertEqual( pass_.property_set["VF2PostLayout_stop_reason"], @@ -348,12 +381,15 @@ def test_target_invalid_2q_gate(self): def test_target_invalid_2q_gate_control_flow(self): """Test that we don't find a solution with a gate outside target.""" - backend = FakeYorktownV2() + # from qiskit.providers.fake_provider import FakeYorktownV2 + target = GenericTarget( + num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=YORKTOWN_CM + ) qc = QuantumCircuit(2) with qc.for_loop((1,)): qc.ecr(0, 1) dag = circuit_to_dag(qc) - pass_ = VF2PostLayout(target=backend.target, seed=self.seed) + pass_ = VF2PostLayout(target=target, seed=self.seed) pass_.run(dag) self.assertEqual( pass_.property_set["VF2PostLayout_stop_reason"], @@ -438,8 +474,10 @@ def test_empty_score(self): bit_map = {} reverse_bit_map = {} im_graph = rustworkx.PyDiGraph() - backend = FakeYorktownV2() - vf2_pass = VF2PostLayout(target=backend.target) + target = GenericTarget( + num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=YORKTOWN_CM + ) + vf2_pass = VF2PostLayout(target=target) layout = Layout() score = vf2_pass._score_layout(layout, bit_map, reverse_bit_map, im_graph) self.assertEqual(0, score) @@ -451,8 +489,19 @@ def test_all_1q_score(self): im_graph = rustworkx.PyDiGraph() im_graph.add_node({"sx": 1}) im_graph.add_node({"sx": 1}) - backend = FakeYorktownV2() - vf2_pass = VF2PostLayout(target=backend.target) + + target = GenericTarget( + num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=YORKTOWN_CM + ) + + target.update_instruction_properties( + "sx", (0,), InstructionProperties(duration=3.56e-08, error=0.0013043388897769352) + ) + target.update_instruction_properties( + "sx", (1,), InstructionProperties(duration=3.56e-08, error=0.0016225037300878712) + ) + + vf2_pass = VF2PostLayout(target=target) layout = Layout(bit_map) score = vf2_pass._score_layout(layout, bit_map, reverse_bit_map, im_graph) self.assertAlmostEqual(0.002925, score, places=5) @@ -526,8 +575,10 @@ def test_empty_circuit(self): def test_empty_circuit_v2(self): """Test no solution found for empty circuit with v2 backend""" qc = QuantumCircuit(2, 2) - backend = FakeLimaV2() - vf2_pass = VF2PostLayout(target=backend.target, strict_direction=False) + target = GenericTarget( + num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=LIMA_CM + ) + vf2_pass = VF2PostLayout(target=target, strict_direction=False) vf2_pass.run(circuit_to_dag(qc)) self.assertEqual( vf2_pass.property_set["VF2PostLayout_stop_reason"], @@ -544,18 +595,22 @@ def test_skip_3q_circuit(self): vf2_pass = VF2PostLayout(coupling_map=cmap, properties=props, strict_direction=False) vf2_pass.run(circuit_to_dag(qc)) self.assertEqual( - vf2_pass.property_set["VF2PostLayout_stop_reason"], VF2PostLayoutStopReason.MORE_THAN_2Q + vf2_pass.property_set["VF2PostLayout_stop_reason"], + VF2PostLayoutStopReason.MORE_THAN_2Q, ) def test_skip_3q_circuit_v2(self): """Test that the pass is a no-op on circuits with >2q gates with a target.""" qc = QuantumCircuit(3) qc.ccx(0, 1, 2) - backend = FakeLimaV2() - vf2_pass = VF2PostLayout(target=backend.target, strict_direction=False) + target = GenericTarget( + num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=LIMA_CM + ) + vf2_pass = VF2PostLayout(target=target, strict_direction=False) vf2_pass.run(circuit_to_dag(qc)) self.assertEqual( - vf2_pass.property_set["VF2PostLayout_stop_reason"], VF2PostLayoutStopReason.MORE_THAN_2Q + vf2_pass.property_set["VF2PostLayout_stop_reason"], + VF2PostLayoutStopReason.MORE_THAN_2Q, ) def test_best_mapping_ghz_state_full_device_multiple_qregs(self): @@ -606,7 +661,10 @@ def test_2q_circuit_5q_backend(self): def test_best_mapping_ghz_state_full_device_multiple_qregs_v2(self): """Test best mappings with multiple registers""" - backend = FakeLimaV2() + + backend = FakeGeneric( + num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=LIMA_CM + ) qr_a = QuantumRegister(2) qr_b = QuantumRegister(3) qc = QuantumCircuit(qr_a, qr_b) @@ -629,7 +687,9 @@ def test_2q_circuit_5q_backend_v2(self): 0 - 1 qr1 - qr0 """ - backend = FakeYorktownV2() + backend = FakeGeneric( + num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], coupling_map=YORKTOWN_CM + ) qr = QuantumRegister(2, "qr") circuit = QuantumCircuit(qr)