diff --git a/docs/apidoc/index.rst b/docs/apidoc/index.rst index d24b7514487e..e718a5be507e 100644 --- a/docs/apidoc/index.rst +++ b/docs/apidoc/index.rst @@ -50,7 +50,6 @@ Primitives and providers: providers providers_basic_provider providers_fake_provider - providers_models Results and visualizations: @@ -83,5 +82,4 @@ Other: compiler exceptions - qobj utils diff --git a/docs/apidoc/providers_models.rst b/docs/apidoc/providers_models.rst deleted file mode 100644 index cd0857a58e3e..000000000000 --- a/docs/apidoc/providers_models.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _qiskit-providers-models: - -.. automodule:: qiskit.providers.models - :no-members: - :no-inherited-members: - :no-special-members: diff --git a/docs/apidoc/qobj.rst b/docs/apidoc/qobj.rst deleted file mode 100644 index 1c03706e4d42..000000000000 --- a/docs/apidoc/qobj.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _qiskit-qobj: - -.. automodule:: qiskit.qobj - :no-members: - :no-inherited-members: - :no-special-members: diff --git a/qiskit/compiler/transpiler.py b/qiskit/compiler/transpiler.py index a57f740fec4e..cc3c22707d0f 100644 --- a/qiskit/compiler/transpiler.py +++ b/qiskit/compiler/transpiler.py @@ -22,7 +22,6 @@ from qiskit.circuit.quantumcircuit import QuantumCircuit from qiskit.dagcircuit import DAGCircuit from qiskit.providers.backend import Backend -from qiskit.providers.backend_compat import BackendV2Converter from qiskit.pulse import Schedule from qiskit.transpiler import Layout, CouplingMap, PropertySet from qiskit.transpiler.basepasses import BasePass @@ -95,21 +94,20 @@ def transpile( # pylint: disable=too-many-return-statements (``basis_gates``, ``coupling_map``, ``instruction_durations``, ``dt`` or ``timing_constraints``). If a ``backend`` is provided together with any loose constraint from the list above, the loose constraint will take priority over the corresponding backend - constraint. This behavior is independent of whether the ``backend`` instance is of type - :class:`.BackendV1` or :class:`.BackendV2`, as summarized in the table below. The first column + constraint. This behavior is summarized in the table below. The first column in the table summarizes the potential user-provided constraints, and each cell shows whether the priority is assigned to that specific constraint input or another input - (`target`/`backend(V1)`/`backend(V2)`). - - ============================ ========= ======================== ======================= - User Provided target backend(V1) backend(V2) - ============================ ========= ======================== ======================= - **basis_gates** target basis_gates basis_gates - **coupling_map** target coupling_map coupling_map - **instruction_durations** target instruction_durations instruction_durations - **dt** target dt dt - **timing_constraints** target timing_constraints timing_constraints - ============================ ========= ======================== ======================= + (`target`/`backend(V2)`). + + ============================ ========= ======================== + User Provided target backend(V2) + ============================ ========= ======================== + **basis_gates** target basis_gates + **coupling_map** target coupling_map + **instruction_durations** target instruction_durations + **dt** target dt + **timing_constraints** target timing_constraints + ============================ ========= ======================== Args: circuits: Circuit(s) to transpile @@ -320,30 +318,6 @@ def callback_func(**kwargs): config = user_config.get_config() optimization_level = config.get("transpile_optimization_level", 2) - if backend is not None and getattr(backend, "version", 0) <= 1: - warnings.warn( - "The `transpile` function will stop supporting inputs of " - f"type `BackendV1` ( {backend} ) in the `backend` parameter in a future " - "release no earlier than 2.0. `BackendV1` is deprecated and implementations " - "should move to `BackendV2`.", - category=DeprecationWarning, - stacklevel=2, - ) - with warnings.catch_warnings(): - # This is a temporary conversion step to allow for a smoother transition - # to a fully target-based transpiler pipeline while maintaining the behavior - # of `transpile` with BackendV1 inputs. - # TODO BackendV1 is deprecated and this path can be - # removed once it gets removed: - # https://github.com/Qiskit/qiskit/pull/12850 - warnings.filterwarnings( - "ignore", - category=DeprecationWarning, - message=r".+qiskit\.providers\.backend_compat\.BackendV2Converter.+", - module="qiskit", - ) - backend = BackendV2Converter(backend) - if ( scheduling_method is not None and backend is None diff --git a/qiskit/primitives/backend_estimator_v2.py b/qiskit/primitives/backend_estimator_v2.py index b95e27dca84a..f2874639ec0a 100644 --- a/qiskit/primitives/backend_estimator_v2.py +++ b/qiskit/primitives/backend_estimator_v2.py @@ -23,7 +23,7 @@ from qiskit.circuit import ClassicalRegister, QuantumCircuit, QuantumRegister from qiskit.exceptions import QiskitError -from qiskit.providers import BackendV1, BackendV2 +from qiskit.providers import BackendV2 from qiskit.quantum_info import Pauli, PauliList from qiskit.result import Counts, Result from qiskit.transpiler import PassManager, PassManagerConfig @@ -38,7 +38,7 @@ def _run_circuits( circuits: QuantumCircuit | list[QuantumCircuit], - backend: BackendV1 | BackendV2, + backend: BackendV2, clear_metadata: bool = True, **run_options, ) -> tuple[list[Result], list[dict]]: @@ -59,9 +59,7 @@ def _run_circuits( metadata.append(circ.metadata) if clear_metadata: circ.metadata = {} - if isinstance(backend, BackendV1): - max_circuits = getattr(backend.configuration(), "max_experiments", None) - elif isinstance(backend, BackendV2): + if isinstance(backend, BackendV2): max_circuits = backend.max_circuits else: raise RuntimeError("Backend version not supported") @@ -174,7 +172,7 @@ class BackendEstimatorV2(BaseEstimatorV2): The :class:`~.BackendEstimatorV2` class is a generic implementation of the :class:`~.BaseEstimatorV2` interface that is used to wrap a :class:`~.BackendV2` - (or :class:`~.BackendV1`) object in the :class:`~.BaseEstimatorV2` API. It + object in the :class:`~.BaseEstimatorV2` API. It facilitates using backends that do not provide a native :class:`~.BaseEstimatorV2` implementation in places that work with :class:`~.BaseEstimatorV2`. However, @@ -225,7 +223,7 @@ class BackendEstimatorV2(BaseEstimatorV2): def __init__( self, *, - backend: BackendV1 | BackendV2, + backend: BackendV2, options: dict | None = None, ): """ @@ -251,7 +249,7 @@ def options(self) -> Options: return self._options @property - def backend(self) -> BackendV1 | BackendV2: + def backend(self) -> BackendV2: """Returns the backend which this sampler object based on.""" return self._backend diff --git a/qiskit/primitives/backend_sampler_v2.py b/qiskit/primitives/backend_sampler_v2.py index a71f99300aae..ccaed445250f 100644 --- a/qiskit/primitives/backend_sampler_v2.py +++ b/qiskit/primitives/backend_sampler_v2.py @@ -36,7 +36,7 @@ from qiskit.primitives.containers.bit_array import _min_num_bytes from qiskit.primitives.containers.sampler_pub import SamplerPub from qiskit.primitives.primitive_job import PrimitiveJob -from qiskit.providers.backend import BackendV1, BackendV2 +from qiskit.providers.backend import BackendV2 from qiskit.result import Result @@ -83,7 +83,7 @@ class BackendSamplerV2(BaseSamplerV2): The :class:`~.BackendSamplerV2` class is a generic implementation of the :class:`~.BaseSamplerV2` interface that is used to wrap a :class:`~.BackendV2` - (or :class:`~.BackendV1`) object in the class :class:`~.BaseSamplerV2` API. It + object in the class :class:`~.BaseSamplerV2` API. It facilitates using backends that do not provide a native :class:`~.BaseSamplerV2` implementation in places that work with :class:`~.BaseSamplerV2`. However, @@ -119,7 +119,7 @@ class BackendSamplerV2(BaseSamplerV2): def __init__( self, *, - backend: BackendV1 | BackendV2, + backend: BackendV2, options: dict | None = None, ): """ @@ -132,7 +132,7 @@ def __init__( self._options = Options(**options) if options else Options() @property - def backend(self) -> BackendV1 | BackendV2: + def backend(self) -> BackendV2: """Returns the backend which this sampler object based on.""" return self._backend diff --git a/qiskit/providers/__init__.py b/qiskit/providers/__init__.py index f32b0d1f9164..c011693b2102 100644 --- a/qiskit/providers/__init__.py +++ b/qiskit/providers/__init__.py @@ -21,7 +21,7 @@ provider is anything that provides an external service to Qiskit. The typical example of this is a Backend provider which provides :class:`~qiskit.providers.Backend` objects which can be used for executing -:class:`~qiskit.circuit.QuantumCircuit` and/or :class:`~qiskit.pulse.Schedule` +:class:`~qiskit.circuit.QuantumCircuit` objects. This module contains the abstract classes which are used to define the interface between a provider and Qiskit. @@ -82,11 +82,8 @@ :toctree: ../stubs/ Backend - BackendV1 BackendV2 QubitProperties - BackendV2Converter - convert_to_target Options ------- @@ -117,10 +114,8 @@ ---------- .. autoexception:: QiskitBackendNotFoundError -.. autoexception:: BackendPropertyError .. autoexception:: JobError .. autoexception:: JobTimeoutError -.. autoexception:: BackendConfigurationError Writing a New Backend ===================== @@ -559,7 +554,7 @@ def _default_options(cls): For some backends (mainly local simulators) the execution of circuits is a synchronous operation and there is no need to return a handle to a running job elsewhere. For sync jobs its expected that the -:obj:`~qiskit.providers.BackendV1.run` method on the backend will block until a +:obj:`~qiskit.providers.BackendV2.run` method on the backend will block until a :class:`~qiskit.result.Result` object is generated and the sync job will return with that inner :class:`~qiskit.result.Result` object. @@ -603,7 +598,6 @@ def result(self, timeout=None, wait=5): 'backend_name': self._backend.configuration().backend_name, 'backend_version': self._backend.configuration().backend_version, 'job_id': self._job_id, - 'qobj_id': ', '.join(x.name for x in self.circuits), 'success': True, }) @@ -666,118 +660,12 @@ def status(self): implementations. Also, the built-in implementations: :class:`~.Sampler`, :class:`~.Estimator`, :class:`~.BackendSampler`, and :class:`~.BackendEstimator` can serve as references/models on how to implement these as well. - -Migrating from BackendV1 to BackendV2 -===================================== - -The :obj:`~BackendV2` class re-defined user access for most properties of a -backend to make them work with native Qiskit data structures and have flatter -access patterns. However this means when using a provider that upgrades -from :obj:`~BackendV1` to :obj:`~BackendV2` existing access patterns will need -to be adjusted. It is expected for existing providers to deprecate the old -access where possible to provide a graceful migration, but eventually users -will need to adjust code. The biggest change to adapt to in :obj:`~BackendV2` is -that most of the information accessible about a backend is contained in its -:class:`~qiskit.transpiler.Target` object and the backend's attributes often query -its :attr:`~qiskit.providers.BackendV2.target` -attribute to return information, however in many cases the attributes only provide -a subset of information the target can contain. For example, ``backend.coupling_map`` -returns a :class:`~qiskit.transpiler.CouplingMap` constructed from the -:class:`~qiskit.transpiler.Target` accessible in the -:attr:`~qiskit.providers.BackendV2.target` attribute, however the target may contain -instructions that operate on more than two qubits (which can't be represented in a -:class:`~qiskit.transpiler.CouplingMap`) or has instructions that only operate on -a subset of qubits (or two qubit links for a two qubit instruction) which won't be -detailed in the full coupling map returned by -:attr:`~qiskit.providers.BackendV2.coupling_map`. So depending on your use case -it might be necessary to look deeper than just the equivalent access with -:obj:`~BackendV2`. - -Below is a table of example access patterns in :obj:`~BackendV1` and the new form -with :obj:`~BackendV2`: - -.. list-table:: Migrate from :obj:`~BackendV1` to :obj:`~BackendV2` - :header-rows: 1 - - * - :obj:`~BackendV1` - - :obj:`~BackendV2` - - Notes - * - ``backend.configuration().n_qubits`` - - ``backend.num_qubits`` - - - * - ``backend.configuration().coupling_map`` - - ``backend.coupling_map`` - - The return from :obj:`~BackendV2` is a :class:`~qiskit.transpiler.CouplingMap` object. - while in :obj:`~BackendV1` it is an edge list. Also this is just a view of - the information contained in ``backend.target`` which may only be a subset of the - information contained in :class:`~qiskit.transpiler.Target` object. - * - ``backend.configuration().backend_name`` - - ``backend.name`` - - - * - ``backend.configuration().backend_version`` - - ``backend.backend_version`` - - The :attr:`~qiskit.providers.BackendV2.version` attribute represents - the version of the abstract :class:`~qiskit.providers.Backend` interface - the object implements while :attr:`~qiskit.providers.BackendV2.backend_version` - is metadata about the version of the backend itself. - * - ``backend.configuration().basis_gates`` - - ``backend.operation_names`` - - The :obj:`~BackendV2` return is a list of operation names contained in the - ``backend.target`` attribute. The :class:`~qiskit.transpiler.Target` may contain more - information that can be expressed by this list of names. For example, that some - operations only work on a subset of qubits or that some names implement the same gate - with different parameters. - * - ``backend.configuration().dt`` - - ``backend.dt`` - - - * - ``backend.configuration().dtm`` - - ``backend.dtm`` - - - * - ``backend.configuration().max_experiments`` - - ``backend.max_circuits`` - - - * - ``backend.configuration().online_date`` - - ``backend.online_date`` - - - * - ``InstructionDurations.from_backend(backend)`` - - ``backend.instruction_durations`` - - - * - ``backend.defaults().instruction_schedule_map`` - - ``backend.instruction_schedule_map`` - - - * - ``backend.properties().t1(0)`` - - ``backend.qubit_properties(0).t1`` - - - * - ``backend.properties().t2(0)`` - - ``backend.qubit_properties(0).t2`` - - - * - ``backend.properties().frequency(0)`` - - ``backend.qubit_properties(0).frequency`` - - - * - ``backend.properties().readout_error(0)`` - - ``backend.target["measure"][(0,)].error`` - - In :obj:`~BackendV2` the error rate for the :class:`~qiskit.circuit.library.Measure` - operation on a given qubit is used to model the readout error. However a - :obj:`~BackendV2` can implement multiple measurement types and list them - separately in a :class:`~qiskit.transpiler.Target`. - * - ``backend.properties().readout_length(0)`` - - ``backend.target["measure"][(0,)].duration`` - - In :obj:`~BackendV2` the duration for the :class:`~qiskit.circuit.library.Measure` - operation on a given qubit is used to model the readout length. However, a - :obj:`~BackendV2` can implement multiple measurement types and list them - separately in a :class:`~qiskit.transpiler.Target`. - -There is also a :class:`~.BackendV2Converter` class available that enables you -to wrap a :class:`~.BackendV1` object with a :class:`~.BackendV2` interface. """ # Providers interface from qiskit.providers.backend import Backend -from qiskit.providers.backend import BackendV1 from qiskit.providers.backend import BackendV2 from qiskit.providers.backend import QubitProperties -from qiskit.providers.backend_compat import BackendV2Converter -from qiskit.providers.backend_compat import convert_to_target from qiskit.providers.options import Options from qiskit.providers.job import Job from qiskit.providers.job import JobV1 @@ -786,7 +674,5 @@ def status(self): JobError, JobTimeoutError, QiskitBackendNotFoundError, - BackendPropertyError, - BackendConfigurationError, ) from qiskit.providers.jobstatus import JobStatus diff --git a/qiskit/providers/backend.py b/qiskit/providers/backend.py index b6b52ceec118..fcf067017f37 100644 --- a/qiskit/providers/backend.py +++ b/qiskit/providers/backend.py @@ -20,9 +20,7 @@ import datetime from typing import List, Union, Iterable, Tuple -from qiskit.providers.models.backendstatus import BackendStatus from qiskit.circuit.gate import Instruction -from qiskit.utils import deprecate_func from qiskit.utils.deprecate_pulse import deprecate_pulse_dependency @@ -38,213 +36,6 @@ class Backend: version = 0 -class BackendV1(Backend, ABC): - """Abstract class for Backends - - This abstract class is to be used for Backend objects. - There are several classes of information contained in a Backend. - The first are the attributes of the class itself. These should be used to - define the immutable characteristics of the backend. The ``options`` - attribute of the backend is used to contain the dynamic user configurable - options of the backend. It should be used more for runtime options - that configure how the backend is used. For example, something like a - ``shots`` field for a backend that runs experiments which would contain an - int for how many shots to execute. The ``properties`` attribute is - optionally defined :class:`~qiskit.providers.models.BackendProperties` - object and is used to return measured properties, or properties - of a backend that may change over time. The simplest example of this would - be a version string, which will change as a backend is updated, but also - could be something like noise parameters for backends that run experiments. - - This first version of the Backend abstract class is written to be mostly - backwards compatible with the legacy providers interface. This includes reusing - the model objects :class:`~qiskit.providers.models.BackendProperties` and - :class:`~qiskit.providers.models.BackendConfiguration`. This was done to - ease the transition for users and provider maintainers to the new versioned providers. - Expect, future versions of this abstract class to change the data model and - interface. - - Subclasses of this should override the public method :meth:`run` and the internal - :meth:`_default_options`: - - .. automethod:: _default_options - """ - - version = 1 - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="If the backend only encapsulates a hardware description, " - "consider constructing a Target directly. If it is part of a provider " - "that gives access to execution, consider using Primitives instead. " - "Alternatively, consider moving to BackendV2 (see https://qisk.it/backendV1-to-V2).", - ) - def __init__(self, configuration, provider=None, **fields): - """Initialize a backend class - - Args: - configuration (BackendConfiguration): A backend configuration - object for the backend object. - provider: Optionally, the provider - object that this Backend comes from. - fields: kwargs for the values to use to override the default - options. - Raises: - AttributeError: if input field not a valid options - - .. - This next bit is necessary just because autosummary generally won't summarise private - methods; changing that behavior would have annoying knock-on effects through all the - rest of the documentation, so instead we just hard-code the automethod directive. - """ - self._configuration = configuration - self._options = self._default_options() - self._provider = provider - if fields: - for field in fields: - if field not in self._options.data: - raise AttributeError(f"Options field {field} is not valid for this backend") - self._options.update_config(**fields) - - @classmethod - @abstractmethod - def _default_options(cls): - """Return the default options - - This method will return a :class:`qiskit.providers.Options` - subclass object that will be used for the default options. These - should be the default parameters to use for the options of the - backend. - - Returns: - qiskit.providers.Options: A options object with - default values set - """ - - def set_options(self, **fields): - """Set the options fields for the backend - - This method is used to update the options of a backend. If - you need to change any of the options prior to running just - pass in the kwarg with the new value for the options. - - Args: - fields: The fields to update the options - - Raises: - AttributeError: If the field passed in is not part of the - options - """ - for field in fields: - if not hasattr(self._options, field): - raise AttributeError(f"Options field {field} is not valid for this backend") - self._options.update_options(**fields) - - def configuration(self): - """Return the backend configuration. - - Returns: - BackendConfiguration: the configuration for the backend. - """ - return self._configuration - - def properties(self): - """Return the backend properties. - - Returns: - BackendProperties: the configuration for the backend. If the backend - does not support properties, it returns ``None``. - """ - return None - - def provider(self): - """Return the backend provider. - - Returns: - provider: the provider responsible for the backend. - """ - return self._provider - - def status(self): - """Return the backend status. - - Returns: - BackendStatus: the status of the backend. - """ - return BackendStatus( - backend_name=self.name(), - backend_version="1", - operational=True, - pending_jobs=0, - status_msg="", - ) - - def name(self): - """Return the backend name. - - Returns: - str: the name of the backend. - """ - return self._configuration.backend_name - - def __str__(self): - return self.name() - - def __repr__(self): - """Official string representation of a Backend. - - Note that, by Qiskit convention, it is consciously *not* a fully valid - Python expression. Subclasses should provide 'a string of the form - <...some useful description...>'. [0] - - [0] https://docs.python.org/3/reference/datamodel.html#object.__repr__ - """ - return f"<{self.__class__.__name__}('{self.name()}')>" - - @property - def options(self): - """Return the options for the backend - - The options of a backend are the dynamic parameters defining - how the backend is used. These are used to control the :meth:`run` - method. - """ - return self._options - - @abstractmethod - def run(self, run_input, **options): - """Run on the backend. - - This method returns a :class:`~qiskit.providers.Job` object - that runs circuits. Depending on the backend this may be either an async - or sync call. It is at the discretion of the provider to decide whether - running should block until the execution is finished or not: the Job - class can handle either situation. - - Args: - run_input (QuantumCircuit or Schedule or list): An individual or a - list of :class:`~qiskit.circuit.QuantumCircuit` or - :class:`~qiskit.pulse.Schedule` objects to run on the backend. - For legacy providers migrating to the new versioned providers, - provider interface a :class:`~qiskit.qobj.QasmQobj` or - :class:`~qiskit.qobj.PulseQobj` objects should probably be - supported too (but deprecated) for backwards compatibility. Be - sure to update the docstrings of subclasses implementing this - method to document that. New provider implementations should not - do this though as :mod:`qiskit.qobj` will be deprecated and - removed along with the legacy providers interface. - 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 - """ - pass - - class QubitProperties: """A representation of the properties of a qubit on a backend. @@ -289,13 +80,6 @@ class BackendV2(Backend, ABC): something like a ``shots`` field for a backend that runs experiments which would contain an int for how many shots to execute. - If migrating a provider from :class:`~qiskit.providers.BackendV1` - one thing to keep in mind is for - backwards compatibility you might need to add a configuration method that - will build a :class:`~qiskit.providers.models.BackendConfiguration` object - and :class:`~qiskit.providers.models.BackendProperties` from the attributes - defined in this class for backwards compatibility. - A backend object can optionally contain methods named ``get_translation_stage_plugin`` and ``get_scheduling_stage_plugin``. If these methods are present on a backend object and this object is used for diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py deleted file mode 100644 index b4c34ee5b55c..000000000000 --- a/qiskit/providers/backend_compat.py +++ /dev/null @@ -1,374 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2024. -# -# 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. - -"""Backend abstract interface for providers.""" - -from __future__ import annotations -import logging -import warnings -from typing import List, Any, Dict, Optional - -from qiskit.providers.backend import BackendV1, BackendV2 -from qiskit.providers.backend import QubitProperties -from qiskit.providers.models.backendconfiguration import BackendConfiguration -from qiskit.providers.models.backendproperties import BackendProperties -from qiskit.circuit.controlflow import CONTROL_FLOW_OP_NAMES, get_control_flow_name_mapping -from qiskit.providers.options import Options -from qiskit.providers.exceptions import BackendPropertyError - - -logger = logging.getLogger(__name__) - - -def convert_to_target( - configuration: BackendConfiguration, - properties: BackendProperties = None, - custom_name_mapping: Optional[Dict[str, Any]] = None, - add_delay: bool = True, - filter_faulty: bool = True, -): - """Decode transpiler target from backend data set. - - This function generates :class:`.Target`` instance from intermediate - legacy objects such as :class:`.BackendProperties`. - These objects are usually components of the legacy :class:`.BackendV1` model. - - Args: - configuration: Backend configuration as ``BackendConfiguration`` - properties: Backend property dictionary or ``BackendProperties`` - custom_name_mapping: A name mapping must be supplied for the operation - not included in Qiskit Standard Gate name mapping, otherwise the operation - will be dropped in the resulting ``Target`` object. - add_delay: If True, adds delay to the instruction set. - filter_faulty: If True, this filters the non-operational qubits. - - Returns: - A ``Target`` instance. - """ - # importing packages where they are needed, to avoid cyclic-import. - # pylint: disable=cyclic-import - from qiskit.transpiler.target import ( - Target, - InstructionProperties, - ) - from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping - from qiskit.circuit.parameter import Parameter - from qiskit.circuit.gate import Gate - - required = ["measure", "delay"] - - # Load Qiskit object representation - qiskit_inst_mapping = get_standard_gate_name_mapping() - if custom_name_mapping: - qiskit_inst_mapping.update(custom_name_mapping) - - qiskit_control_flow_mapping = get_control_flow_name_mapping() - - in_data = {"num_qubits": configuration.num_qubits} - - # Parse global configuration properties - if hasattr(configuration, "dt"): - in_data["dt"] = configuration.dt - if hasattr(configuration, "timing_constraints"): - in_data.update(configuration.timing_constraints) - - # Create instruction property placeholder from backend configuration - basis_gates = set(getattr(configuration, "basis_gates", [])) - supported_instructions = set(getattr(configuration, "supported_instructions", [])) - gate_configs = {gate.name: gate for gate in configuration.gates} - all_instructions = set.union( - basis_gates, set(required), supported_instructions.intersection(CONTROL_FLOW_OP_NAMES) - ) - inst_name_map = {} # type: Dict[str, Instruction] - - faulty_ops = set() - faulty_qubits = set() - unsupported_instructions = [] - - # Create name to Qiskit instruction object repr mapping - for name in all_instructions: - if name in qiskit_control_flow_mapping: - continue - if name in qiskit_inst_mapping: - inst_name_map[name] = qiskit_inst_mapping[name] - elif name in gate_configs: - # GateConfig model is a translator of QASM opcode. - # This doesn't have quantum definition, so Qiskit transpiler doesn't perform - # any optimization in quantum domain. - # Usually GateConfig counterpart should exist in Qiskit namespace so this is rarely called. - this_config = gate_configs[name] - params = list(map(Parameter, getattr(this_config, "parameters", []))) - coupling_map = getattr(this_config, "coupling_map", []) - inst_name_map[name] = Gate( - name=name, - num_qubits=len(coupling_map[0]) if coupling_map else 0, - params=params, - ) - else: - warnings.warn( - f"No gate definition for {name} can be found and is being excluded " - "from the generated target. You can use `custom_name_mapping` to provide " - "a definition for this operation.", - RuntimeWarning, - ) - unsupported_instructions.append(name) - - for name in unsupported_instructions: - all_instructions.remove(name) - - # Create inst properties placeholder - # Without any assignment, properties value is None, - # which defines a global instruction that can be applied to any qubit(s). - # The None value behaves differently from an empty dictionary. - # See API doc of Target.add_instruction for details. - prop_name_map = dict.fromkeys(all_instructions) - for name in all_instructions: - if name in gate_configs: - if coupling_map := getattr(gate_configs[name], "coupling_map", None): - # Respect operational qubits that gate configuration defines - # This ties instruction to particular qubits even without properties information. - # Note that each instruction is considered to be ideal unless - # its spec (e.g. error, duration) is bound by the properties object. - prop_name_map[name] = dict.fromkeys(map(tuple, coupling_map)) - - # Populate instruction properties - if properties: - - def _get_value(prop_dict, prop_name): - if ndval := prop_dict.get(prop_name, None): - return ndval[0] - return None - - # is_qubit_operational is a bit of expensive operation so precache the value - faulty_qubits = { - q for q in range(configuration.num_qubits) if not properties.is_qubit_operational(q) - } - - qubit_properties = [] - for qi in range(0, configuration.num_qubits): - # TODO faulty qubit handling might be needed since - # faulty qubit reporting qubit properties doesn't make sense. - try: - prop_dict = properties.qubit_property(qubit=qi) - except KeyError: - continue - qubit_properties.append( - QubitProperties( - t1=prop_dict.get("T1", (None, None))[0], - t2=prop_dict.get("T2", (None, None))[0], - frequency=prop_dict.get("frequency", (None, None))[0], - ) - ) - in_data["qubit_properties"] = qubit_properties - - for name in all_instructions: - try: - for qubits, params in properties.gate_property(name).items(): - if filter_faulty and ( - set.intersection(faulty_qubits, qubits) - or not properties.is_gate_operational(name, qubits) - ): - try: - # Qubits might be pre-defined by the gate config - # However properties objects says the qubits is non-operational - del prop_name_map[name][qubits] - except KeyError: - pass - faulty_ops.add((name, qubits)) - continue - if prop_name_map[name] is None: - # This instruction is tied to particular qubits - # i.e. gate config is not provided, and instruction has been globally defined. - prop_name_map[name] = {} - prop_name_map[name][qubits] = InstructionProperties( - error=_get_value(params, "gate_error"), - duration=_get_value(params, "gate_length"), - ) - if isinstance(prop_name_map[name], dict) and any( - v is None for v in prop_name_map[name].values() - ): - # Properties provides gate properties only for subset of qubits - # Associated qubit set might be defined by the gate config here - logger.info( - "Gate properties of instruction %s are not provided for every qubits. " - "This gate is ideal for some qubits and the rest is with finite error. " - "Created backend target may confuse error-aware circuit optimization.", - name, - ) - except BackendPropertyError: - # This gate doesn't report any property - continue - - # Measure instruction property is stored in qubit property - prop_name_map["measure"] = {} - - for qubit_idx in range(configuration.num_qubits): - if filter_faulty and (qubit_idx in faulty_qubits): - continue - qubit_prop = properties.qubit_property(qubit_idx) - prop_name_map["measure"][(qubit_idx,)] = InstructionProperties( - error=_get_value(qubit_prop, "readout_error"), - duration=_get_value(qubit_prop, "readout_length"), - ) - - for op in required: - # Map required ops to each operational qubit - if prop_name_map[op] is None: - prop_name_map[op] = { - (q,): None - for q in range(configuration.num_qubits) - if not filter_faulty or (q not in faulty_qubits) - } - - # Add parsed properties to target - target = Target(**in_data) - for inst_name in all_instructions: - if inst_name == "delay" and not add_delay: - continue - if inst_name in qiskit_control_flow_mapping: - # Control flow operator doesn't have gate property. - target.add_instruction( - instruction=qiskit_control_flow_mapping[inst_name], - name=inst_name, - ) - else: - target.add_instruction( - instruction=inst_name_map[inst_name], - properties=prop_name_map.get(inst_name, None), - name=inst_name, - ) - - return target - - -def qubit_props_list_from_props( - properties: BackendProperties, -) -> List[QubitProperties]: - """Uses BackendProperties to construct - and return a list of QubitProperties. - """ - qubit_props: List[QubitProperties] = [] - for qubit, _ in enumerate(properties.qubits): - try: - t_1 = properties.t1(qubit) - except BackendPropertyError: - t_1 = None - try: - t_2 = properties.t2(qubit) - except BackendPropertyError: - t_2 = None - try: - frequency = properties.frequency(qubit) - except BackendPropertyError: - frequency = None - qubit_props.append( - QubitProperties( # type: ignore[no-untyped-call] - t1=t_1, - t2=t_2, - frequency=frequency, - ) - ) - return qubit_props - - -class BackendV2Converter(BackendV2): - """A converter class that takes a :class:`~.BackendV1` instance and wraps it in a - :class:`~.BackendV2` interface. - - This class implements the :class:`~.BackendV2` interface and is used to enable - common access patterns between :class:`~.BackendV1` and :class:`~.BackendV2`. This - class should only be used if you need a :class:`~.BackendV2` and still need - compatibility with :class:`~.BackendV1`. - """ - - def __init__( - self, - backend: BackendV1, - name_mapping: Optional[Dict[str, Any]] = None, - add_delay: bool = True, - filter_faulty: bool = True, - ): - """Initialize a BackendV2 converter instance based on a BackendV1 instance. - - Args: - backend: The input :class:`~.BackendV1` based backend to wrap in a - :class:`~.BackendV2` interface - name_mapping: An optional dictionary that maps custom gate/operation names in - ``backend`` to an :class:`~.Operation` object representing that - gate/operation. By default most standard gates names are mapped to the - standard gate object from :mod:`qiskit.circuit.library` this only needs - to be specified if the input ``backend`` defines gates in names outside - that set. - add_delay: If set to true a :class:`~qiskit.circuit.Delay` operation - will be added to the target as a supported operation for all - qubits - filter_faulty: If the :class:`~.BackendProperties` object (if present) for - ``backend`` has any qubits or gates flagged as non-operational filter - those from the output target. - """ - self._backend = backend - self._config = self._backend.configuration() - super().__init__( - provider=backend.provider, - name=backend.name(), - description=getattr(self._config, "description", None), - online_date=getattr(self._config, "online_date", None), - backend_version=self._config.backend_version, - ) - self._options = self._backend._options - self._properties = None - - with warnings.catch_warnings(): - # The class QobjExperimentHeader is deprecated - warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") - if hasattr(self._backend, "properties"): - self._properties = self._backend.properties() - - self._target = None - self._name_mapping = name_mapping - self._add_delay = add_delay - self._filter_faulty = filter_faulty - - @property - def target(self): - """A :class:`qiskit.transpiler.Target` object for the backend. - - :rtype: Target - """ - if self._target is None: - self._target = convert_to_target( - configuration=self._config, - properties=self._properties, - custom_name_mapping=self._name_mapping, - add_delay=self._add_delay, - filter_faulty=self._filter_faulty, - ) - return self._target - - @property - def max_circuits(self): - return self._config.max_experiments - - @classmethod - def _default_options(cls): - return Options() - - @property - def dtm(self) -> float: - return self._config.dtm - - @property - def meas_map(self) -> List[List[int]]: - return self._config.meas_map - - def run(self, run_input, **options): - return self._backend.run(run_input, **options) diff --git a/qiskit/providers/basic_provider/basic_simulator.py b/qiskit/providers/basic_provider/basic_simulator.py index d9d72a2b883e..c2927481975d 100644 --- a/qiskit/providers/basic_provider/basic_simulator.py +++ b/qiskit/providers/basic_provider/basic_simulator.py @@ -491,7 +491,6 @@ def _run_job(self, job_id: str, run_input) -> Result: result = { "backend_name": self.name, "backend_version": self.backend_version, - "qobj_id": None, "job_id": job_id, "results": result_list, "status": "COMPLETED", diff --git a/qiskit/providers/exceptions.py b/qiskit/providers/exceptions.py index 3fddacb92fec..45d0b7973411 100644 --- a/qiskit/providers/exceptions.py +++ b/qiskit/providers/exceptions.py @@ -31,15 +31,3 @@ class QiskitBackendNotFoundError(QiskitError): """Base class for errors raised while looking for a backend.""" pass - - -class BackendPropertyError(QiskitError): - """Base class for errors raised while looking for a backend property.""" - - pass - - -class BackendConfigurationError(QiskitError): - """Base class for errors raised by the BackendConfiguration.""" - - pass diff --git a/qiskit/providers/fake_provider/utils/backend_converter.py b/qiskit/providers/fake_provider/utils/backend_converter.py deleted file mode 100644 index c5268dc2f414..000000000000 --- a/qiskit/providers/fake_provider/utils/backend_converter.py +++ /dev/null @@ -1,150 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# 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. - -""" -Utilities for constructing Target object from configuration, properties and -pulse defaults json files -""" - -from qiskit.transpiler.target import Target, InstructionProperties -from qiskit.providers.backend import QubitProperties -from qiskit.utils.units import apply_prefix -from qiskit.circuit.library.standard_gates import IGate, SXGate, XGate, CXGate, RZGate -from qiskit.circuit.parameter import Parameter -from qiskit.circuit.gate import Gate -from qiskit.circuit.delay import Delay -from qiskit.circuit.measure import Measure -from qiskit.circuit.reset import Reset -from qiskit.providers.models.pulsedefaults import PulseDefaults - - -def convert_to_target(conf_dict: dict, props_dict: dict = None, defs_dict: dict = None) -> Target: - """Uses configuration, properties and pulse defaults dicts - to construct and return Target class. - """ - name_mapping = { - "id": IGate(), - "sx": SXGate(), - "x": XGate(), - "cx": CXGate(), - "rz": RZGate(Parameter("λ")), - "reset": Reset(), - } - custom_gates = {} - qubit_props = None - if props_dict: - qubit_props = qubit_props_from_props(props_dict) - target = Target(qubit_properties=qubit_props, concurrent_measurements=conf_dict.get("meas_map")) - # Parse from properties if it exists - if props_dict is not None: - # Parse instructions - gates = {} - for gate in props_dict["gates"]: - name = gate["gate"] - if name in name_mapping: - if name not in gates: - gates[name] = {} - elif name not in custom_gates: - custom_gate = Gate(name, len(gate["qubits"]), []) - custom_gates[name] = custom_gate - gates[name] = {} - - qubits = tuple(gate["qubits"]) - gate_props = {} - for param in gate["parameters"]: - if param["name"] == "gate_error": - gate_props["error"] = param["value"] - if param["name"] == "gate_length": - gate_props["duration"] = apply_prefix(param["value"], param["unit"]) - gates[name][qubits] = InstructionProperties(**gate_props) - for gate, props in gates.items(): - if gate in name_mapping: - inst = name_mapping.get(gate) - else: - inst = custom_gates[gate] - target.add_instruction(inst, props) - # Create measurement instructions: - measure_props = {} - count = 0 - for qubit in props_dict["qubits"]: - qubit_prop = {} - for prop in qubit: - if prop["name"] == "readout_length": - qubit_prop["duration"] = apply_prefix(prop["value"], prop["unit"]) - if prop["name"] == "readout_error": - qubit_prop["error"] = prop["value"] - measure_props[(count,)] = InstructionProperties(**qubit_prop) - count += 1 - target.add_instruction(Measure(), measure_props) - # Parse from configuration because properties doesn't exist - else: - for gate in conf_dict["gates"]: - name = gate["name"] - gate_props = {tuple(x): None for x in gate["coupling_map"]} - if name in name_mapping: - target.add_instruction(name_mapping[name], gate_props) - else: - custom_gate = Gate(name, len(gate["coupling_map"][0]), []) - target.add_instruction(custom_gate, gate_props) - measure_props = {(n,): None for n in range(conf_dict["n_qubits"])} - target.add_instruction(Measure(), measure_props) - # parse global configuration properties - dt = conf_dict.get("dt") - if dt: - target.dt = dt * 1e-9 - if "timing_constraints" in conf_dict: - target.granularity = conf_dict["timing_constraints"].get("granularity") - target.min_length = conf_dict["timing_constraints"].get("min_length") - target.pulse_alignment = conf_dict["timing_constraints"].get("pulse_alignment") - target.acquire_alignment = conf_dict["timing_constraints"].get("acquire_alignment") - # If pulse defaults exists use that as the source of truth - if defs_dict is not None: - # TODO remove the usage of PulseDefaults as it will be deprecated in the future - pulse_defs = PulseDefaults.from_dict(defs_dict) - inst_map = pulse_defs.instruction_schedule_map - 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 target: - if inst == "measure": - for qubit in qargs: - target[inst][(qubit,)].calibration = calibration_entry - elif qargs in target[inst]: - target[inst][qargs].calibration = calibration_entry - target.add_instruction( - Delay(Parameter("t")), {(bit,): None for bit in range(target.num_qubits)} - ) - return target - - -def qubit_props_from_props(properties: dict) -> list: - """Returns a dictionary of `qiskit.providers.backend.QubitProperties` using - a backend properties dictionary created by loading props.json payload. - """ - qubit_props = [] - for qubit in properties["qubits"]: - qubit_properties = {} - for prop_dict in qubit: - if prop_dict["name"] == "T1": - qubit_properties["t1"] = apply_prefix(prop_dict["value"], prop_dict["unit"]) - elif prop_dict["name"] == "T2": - qubit_properties["t2"] = apply_prefix(prop_dict["value"], prop_dict["unit"]) - elif prop_dict["name"] == "frequency": - qubit_properties["frequency"] = apply_prefix(prop_dict["value"], prop_dict["unit"]) - qubit_props.append(QubitProperties(**qubit_properties)) - return qubit_props diff --git a/qiskit/providers/fake_provider/utils/json_decoder.py b/qiskit/providers/fake_provider/utils/json_decoder.py deleted file mode 100644 index ffd054978cbd..000000000000 --- a/qiskit/providers/fake_provider/utils/json_decoder.py +++ /dev/null @@ -1,109 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# 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. - -""" -Utils to decode fake backend configurations from json -""" - -from typing import Dict, Union, List - -import dateutil.parser - - -def decode_pulse_defaults(defaults: Dict) -> None: - """Decode pulse defaults data. - - Args: - defaults: A ``PulseDefaults`` in dictionary format. - """ - for item in defaults["pulse_library"]: - _decode_pulse_library_item(item) - - for cmd in defaults["cmd_def"]: - if "sequence" in cmd: - for instr in cmd["sequence"]: - _decode_pulse_qobj_instr(instr) - - -def decode_backend_properties(properties: Dict) -> None: - """Decode backend properties. - - Args: - properties: A ``BackendProperties`` in dictionary format. - """ - properties["last_update_date"] = dateutil.parser.isoparse(properties["last_update_date"]) - for qubit in properties["qubits"]: - for nduv in qubit: - nduv["date"] = dateutil.parser.isoparse(nduv["date"]) - for gate in properties["gates"]: - for param in gate["parameters"]: - param["date"] = dateutil.parser.isoparse(param["date"]) - for gen in properties["general"]: - gen["date"] = dateutil.parser.isoparse(gen["date"]) - - -def decode_backend_configuration(config: Dict) -> None: - """Decode backend configuration. - - Args: - config: A ``QasmBackendConfiguration`` or ``PulseBackendConfiguration`` - in dictionary format. - """ - config["online_date"] = dateutil.parser.isoparse(config["online_date"]) - - if "u_channel_lo" in config: - for u_channle_list in config["u_channel_lo"]: - for u_channle_lo in u_channle_list: - u_channle_lo["scale"] = _to_complex(u_channle_lo["scale"]) - - -def _to_complex(value: Union[List[float], complex]) -> complex: - """Convert the input value to type ``complex``. - - Args: - value: Value to be converted. - - Returns: - Input value in ``complex``. - - Raises: - TypeError: If the input value is not in the expected format. - """ - if isinstance(value, list) and len(value) == 2: - return complex(value[0], value[1]) - elif isinstance(value, complex): - return value - - raise TypeError(f"{value} is not in a valid complex number format.") - - -def _decode_pulse_library_item(pulse_library_item: Dict) -> None: - """Decode a pulse library item. - - Args: - pulse_library_item: A ``PulseLibraryItem`` in dictionary format. - """ - pulse_library_item["samples"] = [ - _to_complex(sample) for sample in pulse_library_item["samples"] - ] - - -def _decode_pulse_qobj_instr(pulse_qobj_instr: Dict) -> None: - """Decode a pulse Qobj instruction. - - Args: - pulse_qobj_instr: A ``PulseQobjInstruction`` in dictionary format. - """ - if "val" in pulse_qobj_instr: - pulse_qobj_instr["val"] = _to_complex(pulse_qobj_instr["val"]) - if "parameters" in pulse_qobj_instr and "amp" in pulse_qobj_instr["parameters"]: - pulse_qobj_instr["parameters"]["amp"] = _to_complex(pulse_qobj_instr["parameters"]["amp"]) diff --git a/qiskit/providers/models/__init__.py b/qiskit/providers/models/__init__.py deleted file mode 100644 index d7f8307abb04..000000000000 --- a/qiskit/providers/models/__init__.py +++ /dev/null @@ -1,89 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# 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. - -""" -================================================ -Backend Objects (:mod:`qiskit.providers.models`) -================================================ - -.. currentmodule:: qiskit.providers.models - -Qiskit schema-conformant objects used by the backends and providers. - -Classes -======= - -.. autosummary:: - :toctree: ../stubs/ - - BackendConfiguration - BackendProperties - BackendStatus - QasmBackendConfiguration - PulseBackendConfiguration - UchannelLO - GateConfig - PulseDefaults - Command - JobStatus - GateProperties - Nduv -""" -# pylint: disable=undefined-all-variable -__all__ = [ - "BackendConfiguration", - "PulseBackendConfiguration", - "QasmBackendConfiguration", - "UchannelLO", - "GateConfig", - "BackendProperties", - "GateProperties", - "Nduv", - "BackendStatus", - "JobStatus", - "PulseDefaults", - "Command", -] - -import importlib -import warnings - - -_NAME_MAP = { - # public object name mapped to containing module - "BackendConfiguration": "qiskit.providers.models.backendconfiguration", - "PulseBackendConfiguration": "qiskit.providers.models.backendconfiguration", - "QasmBackendConfiguration": "qiskit.providers.models.backendconfiguration", - "UchannelLO": "qiskit.providers.models.backendconfiguration", - "GateConfig": "qiskit.providers.models.backendconfiguration", - "BackendProperties": "qiskit.providers.models.backendproperties", - "GateProperties": "qiskit.providers.models.backendproperties", - "Nduv": "qiskit.providers.models.backendproperties", - "BackendStatus": "qiskit.providers.models.backendstatus", - "JobStatus": "qiskit.providers.models.jobstatus", - "PulseDefaults": "qiskit.providers.models.pulsedefaults", - "Command": "qiskit.providers.models.pulsedefaults", -} - - -def __getattr__(name): - if (module_name := _NAME_MAP.get(name)) is not None: - warnings.warn( - "qiskit.providers.models is deprecated since Qiskit 1.2 and will be " - "removed in Qiskit 2.0. With the removal of Qobj, there is no need for these " - "schema-conformant objects. If you still need to use them, it could be because " - "you are using a BackendV1, which is also deprecated in favor of BackendV2.", - DeprecationWarning, - stacklevel=2, - ) - return getattr(importlib.import_module(module_name), name) - raise AttributeError(f"module 'qiskit.providers.models' has no attribute '{name}'") diff --git a/qiskit/providers/models/backendconfiguration.py b/qiskit/providers/models/backendconfiguration.py deleted file mode 100644 index a50745c9572c..000000000000 --- a/qiskit/providers/models/backendconfiguration.py +++ /dev/null @@ -1,1040 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# 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. - -"""Backend Configuration Classes.""" -import re -import copy -import numbers -from typing import Dict, List, Any, Iterable, Tuple, Union -from collections import defaultdict - -from qiskit.exceptions import QiskitError -from qiskit.providers.exceptions import BackendConfigurationError -from qiskit.pulse.channels import ( - AcquireChannel, - Channel, - ControlChannel, - DriveChannel, - MeasureChannel, -) -from qiskit.utils import deprecate_func - - -class GateConfig: - """Class representing a Gate Configuration - - Attributes: - name: the gate name as it will be referred to in OpenQASM. - parameters: variable names for the gate parameters (if any). - qasm_def: definition of this gate in terms of OpenQASM 2 primitives U - and CX. - """ - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The models in ``qiskit.providers.models`` are part " - "of the deprecated `BackendV1` workflow and no longer necessary for `BackendV2`. If a user " - "workflow requires these representations it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - stacklevel=3, - ) - def __init__( - self, - name, - parameters, - qasm_def, - coupling_map=None, - latency_map=None, - conditional=None, - description=None, - ): - """Initialize a GateConfig object - - Args: - name (str): the gate name as it will be referred to in OpenQASM. - parameters (list): variable names for the gate parameters (if any) - as a list of strings. - qasm_def (str): definition of this gate in terms of OpenQASM 2 primitives U and CX. - coupling_map (list): An optional coupling map for the gate. In - the form of a list of lists of integers representing the qubit - groupings which are coupled by this gate. - latency_map (list): An optional map of latency for the gate. In the - the form of a list of lists of integers of either 0 or 1 - representing an array of dimension - len(coupling_map) X n_registers that specifies the register - latency (1: fast, 0: slow) conditional operations on the gate - conditional (bool): Optionally specify whether this gate supports - conditional operations (true/false). If this is not specified, - then the gate inherits the conditional property of the backend. - description (str): Description of the gate operation - """ - - self.name = name - self.parameters = parameters - self.qasm_def = qasm_def - # coupling_map with length 0 is invalid - if coupling_map: - self.coupling_map = coupling_map - # latency_map with length 0 is invalid - if latency_map: - self.latency_map = latency_map - if conditional is not None: - self.conditional = conditional - if description is not None: - self.description = description - - @classmethod - def from_dict(cls, data): - """Create a new GateConfig object from a dictionary. - - Args: - data (dict): A dictionary representing the GateConfig to create. - It will be in the same format as output by - :func:`to_dict`. - - Returns: - GateConfig: The GateConfig from the input dictionary. - """ - return cls(**data) - - def to_dict(self): - """Return a dictionary format representation of the GateConfig. - - Returns: - dict: The dictionary form of the GateConfig. - """ - out_dict = { - "name": self.name, - "parameters": self.parameters, - "qasm_def": self.qasm_def, - } - if hasattr(self, "coupling_map"): - out_dict["coupling_map"] = self.coupling_map - if hasattr(self, "latency_map"): - out_dict["latency_map"] = self.latency_map - if hasattr(self, "conditional"): - out_dict["conditional"] = self.conditional - if hasattr(self, "description"): - out_dict["description"] = self.description - return out_dict - - def __eq__(self, other): - if isinstance(other, GateConfig): - if self.to_dict() == other.to_dict(): - return True - return False - - def __repr__(self): - out_str = f"GateConfig({self.name}, {self.parameters}, {self.qasm_def}" - for i in ["coupling_map", "latency_map", "conditional", "description"]: - if hasattr(self, i): - out_str += ", " + repr(getattr(self, i)) - out_str += ")" - return out_str - - -class UchannelLO: - """Class representing a U Channel LO - - Attributes: - q: Qubit that scale corresponds too. - scale: Scale factor for qubit frequency. - """ - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The models in ``qiskit.providers.models`` are part " - "of the deprecated `BackendV1` workflow and no longer necessary for `BackendV2`. If a user " - "workflow requires these representations it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - ) - def __init__(self, q, scale): - """Initialize a UchannelLOSchema object - - Args: - q (int): Qubit that scale corresponds too. Must be >= 0. - scale (complex): Scale factor for qubit frequency. - - Raises: - QiskitError: If q is < 0 - """ - if q < 0: - raise QiskitError("q must be >=0") - self.q = q - self.scale = scale - - @classmethod - def from_dict(cls, data): - """Create a new UchannelLO object from a dictionary. - - Args: - data (dict): A dictionary representing the UChannelLO to - create. It will be in the same format as output by - :func:`to_dict`. - - Returns: - UchannelLO: The UchannelLO from the input dictionary. - """ - return cls(**data) - - def to_dict(self): - """Return a dictionary format representation of the UChannelLO. - - Returns: - dict: The dictionary form of the UChannelLO. - """ - out_dict = { - "q": self.q, - "scale": self.scale, - } - return out_dict - - def __eq__(self, other): - if isinstance(other, UchannelLO): - if self.to_dict() == other.to_dict(): - return True - return False - - def __repr__(self): - return f"UchannelLO({self.q}, {self.scale})" - - -class QasmBackendConfiguration: - """Class representing an OpenQASM 2.0 Backend Configuration. - - Attributes: - backend_name: backend name. - backend_version: backend version in the form X.Y.Z. - n_qubits: number of qubits. - basis_gates: list of basis gates names on the backend. - gates: list of basis gates on the backend. - local: backend is local or remote. - simulator: backend is a simulator. - conditional: backend supports conditional operations. - open_pulse: backend supports open pulse. - memory: backend supports memory. - max_shots: maximum number of shots supported. - """ - - _data = {} - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The models in ``qiskit.providers.models`` are part " - "of the deprecated `BackendV1` workflow and no longer necessary for `BackendV2`. If a user " - "workflow requires these representations it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - stacklevel=3, - ) - def __init__( - self, - backend_name, - backend_version, - n_qubits, - basis_gates, - gates, - local, - simulator, - conditional, - open_pulse, - memory, - max_shots, - coupling_map, - supported_instructions=None, - dynamic_reprate_enabled=False, - rep_delay_range=None, - default_rep_delay=None, - max_experiments=None, - sample_name=None, - n_registers=None, - register_map=None, - configurable=None, - credits_required=None, - online_date=None, - display_name=None, - description=None, - tags=None, - dt=None, - dtm=None, - processor_type=None, - parametric_pulses=None, - **kwargs, - ): - """Initialize a QasmBackendConfiguration Object - - Args: - backend_name (str): The backend name - backend_version (str): The backend version in the form X.Y.Z - n_qubits (int): the number of qubits for the backend - basis_gates (list): The list of strings for the basis gates of the - backends - gates (list): The list of GateConfig objects for the basis gates of - the backend - local (bool): True if the backend is local or False if remote - simulator (bool): True if the backend is a simulator - conditional (bool): True if the backend supports conditional - operations - open_pulse (bool): True if the backend supports OpenPulse - memory (bool): True if the backend supports memory - max_shots (int): The maximum number of shots allowed on the backend - coupling_map (list): The coupling map for the device - supported_instructions (List[str]): Instructions supported by the backend. - dynamic_reprate_enabled (bool): whether delay between programs can be set dynamically - (ie via ``rep_delay``). Defaults to False. - rep_delay_range (List[float]): 2d list defining supported range of repetition - delays for backend in μs. First entry is lower end of the range, second entry is - higher end of the range. Optional, but will be specified when - ``dynamic_reprate_enabled=True``. - default_rep_delay (float): Value of ``rep_delay`` if not specified by user and - ``dynamic_reprate_enabled=True``. - max_experiments (int): The maximum number of experiments per job - sample_name (str): Sample name for the backend - n_registers (int): Number of register slots available for feedback - (if conditional is True) - register_map (list): An array of dimension n_qubits X - n_registers that specifies whether a qubit can store a - measurement in a certain register slot. - configurable (bool): True if the backend is configurable, if the - backend is a simulator - credits_required (bool): True if backend requires credits to run a - job. - online_date (datetime.datetime): The date that the device went online - display_name (str): Alternate name field for the backend - description (str): A description for the backend - tags (list): A list of string tags to describe the backend - dt (float): Qubit drive channel timestep in nanoseconds. - dtm (float): Measurement drive channel timestep in nanoseconds. - processor_type (dict): Processor type for this backend. A dictionary of the - form ``{"family": , "revision": , segment: }`` such as - ``{"family": "Canary", "revision": "1.0", segment: "A"}``. - - - family: Processor family of this backend. - - revision: Revision version of this processor. - - segment: Segment this processor belongs to within a larger chip. - parametric_pulses (list): A list of pulse shapes which are supported on the backend. - For example: ``['gaussian', 'constant']`` - - **kwargs: optional fields - """ - self._data = {} - - self.backend_name = backend_name - self.backend_version = backend_version - self.n_qubits = n_qubits - self.basis_gates = basis_gates - self.gates = gates - self.local = local - self.simulator = simulator - self.conditional = conditional - self.open_pulse = open_pulse - self.memory = memory - self.max_shots = max_shots - self.coupling_map = coupling_map - if supported_instructions: - self.supported_instructions = supported_instructions - - self.dynamic_reprate_enabled = dynamic_reprate_enabled - if rep_delay_range: - self.rep_delay_range = [_rd * 1e-6 for _rd in rep_delay_range] # convert to sec - if default_rep_delay is not None: - self.default_rep_delay = default_rep_delay * 1e-6 # convert to sec - - # max_experiments must be >=1 - if max_experiments: - self.max_experiments = max_experiments - if sample_name is not None: - self.sample_name = sample_name - # n_registers must be >=1 - if n_registers: - self.n_registers = 1 - # register_map must have at least 1 entry - if register_map: - self.register_map = register_map - if configurable is not None: - self.configurable = configurable - if credits_required is not None: - self.credits_required = credits_required - if online_date is not None: - self.online_date = online_date - if display_name is not None: - self.display_name = display_name - if description is not None: - self.description = description - if tags is not None: - self.tags = tags - # Add pulse properties here because some backends do not - # fit within the Qasm / Pulse backend partitioning in Qiskit - if dt is not None: - self.dt = dt * 1e-9 - if dtm is not None: - self.dtm = dtm * 1e-9 - if processor_type is not None: - self.processor_type = processor_type - if parametric_pulses is not None: - self.parametric_pulses = parametric_pulses - - # convert lo range from GHz to Hz - if "qubit_lo_range" in kwargs: - kwargs["qubit_lo_range"] = [ - [min_range * 1e9, max_range * 1e9] - for (min_range, max_range) in kwargs["qubit_lo_range"] - ] - - if "meas_lo_range" in kwargs: - kwargs["meas_lo_range"] = [ - [min_range * 1e9, max_range * 1e9] - for (min_range, max_range) in kwargs["meas_lo_range"] - ] - - # convert rep_times from μs to sec - if "rep_times" in kwargs: - kwargs["rep_times"] = [_rt * 1e-6 for _rt in kwargs["rep_times"]] - - self._data.update(kwargs) - - def __getattr__(self, name): - try: - return self._data[name] - except KeyError as ex: - raise AttributeError(f"Attribute {name} is not defined") from ex - - @classmethod - def from_dict(cls, data): - """Create a new GateConfig object from a dictionary. - - Args: - data (dict): A dictionary representing the GateConfig to create. - It will be in the same format as output by - :func:`to_dict`. - Returns: - GateConfig: The GateConfig from the input dictionary. - """ - in_data = copy.copy(data) - gates = [GateConfig.from_dict(x) for x in in_data.pop("gates")] - in_data["gates"] = gates - return cls(**in_data) - - def to_dict(self): - """Return a dictionary format representation of the GateConfig. - - Returns: - dict: The dictionary form of the GateConfig. - """ - out_dict = { - "backend_name": self.backend_name, - "backend_version": self.backend_version, - "n_qubits": self.n_qubits, - "basis_gates": self.basis_gates, - "gates": [x.to_dict() for x in self.gates], - "local": self.local, - "simulator": self.simulator, - "conditional": self.conditional, - "open_pulse": self.open_pulse, - "memory": self.memory, - "max_shots": self.max_shots, - "coupling_map": self.coupling_map, - "dynamic_reprate_enabled": self.dynamic_reprate_enabled, - } - - if hasattr(self, "supported_instructions"): - out_dict["supported_instructions"] = self.supported_instructions - - if hasattr(self, "rep_delay_range"): - out_dict["rep_delay_range"] = [_rd * 1e6 for _rd in self.rep_delay_range] - if hasattr(self, "default_rep_delay"): - out_dict["default_rep_delay"] = self.default_rep_delay * 1e6 - - for kwarg in [ - "max_experiments", - "sample_name", - "n_registers", - "register_map", - "configurable", - "credits_required", - "online_date", - "display_name", - "description", - "tags", - "dt", - "dtm", - "processor_type", - "parametric_pulses", - ]: - if hasattr(self, kwarg): - out_dict[kwarg] = getattr(self, kwarg) - - out_dict.update(self._data) - - if "dt" in out_dict: - out_dict["dt"] *= 1e9 - if "dtm" in out_dict: - out_dict["dtm"] *= 1e9 - - # Use GHz in dict - if "qubit_lo_range" in out_dict: - out_dict["qubit_lo_range"] = [ - [min_range * 1e-9, max_range * 1e-9] - for (min_range, max_range) in out_dict["qubit_lo_range"] - ] - - if "meas_lo_range" in out_dict: - out_dict["meas_lo_range"] = [ - [min_range * 1e-9, max_range * 1e-9] - for (min_range, max_range) in out_dict["meas_lo_range"] - ] - - return out_dict - - @property - def num_qubits(self): - """Returns the number of qubits. - - In future, `n_qubits` should be replaced in favor of `num_qubits` for consistent use - throughout Qiskit. Until this is properly refactored, this property serves as intermediate - solution. - """ - return self.n_qubits - - def __eq__(self, other): - if isinstance(other, QasmBackendConfiguration): - if self.to_dict() == other.to_dict(): - return True - return False - - def __contains__(self, item): - return item in self.__dict__ - - -class BackendConfiguration(QasmBackendConfiguration): - """Backwards compatibility shim representing an abstract backend configuration.""" - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The models in ``qiskit.providers.models`` are part " - "of the deprecated `BackendV1` workflow and no longer necessary for `BackendV2`. If a user " - "workflow requires these representations it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - stacklevel=3, - ) - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - -class PulseBackendConfiguration(QasmBackendConfiguration): - """Static configuration state for an OpenPulse enabled backend. This contains information - about the set up of the device which can be useful for building Pulse programs. - """ - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The models in ``qiskit.providers.models`` are part " - "of the deprecated `BackendV1` workflow and no longer necessary for `BackendV2`. If a user " - "workflow requires these representations it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - stacklevel=3, - ) - def __init__( - self, - backend_name: str, - backend_version: str, - n_qubits: int, - basis_gates: List[str], - gates: GateConfig, - local: bool, - simulator: bool, - conditional: bool, - open_pulse: bool, - memory: bool, - max_shots: int, - coupling_map, - n_uchannels: int, - u_channel_lo: List[List[UchannelLO]], - meas_levels: List[int], - qubit_lo_range: List[List[float]], - meas_lo_range: List[List[float]], - dt: float, - dtm: float, - rep_times: List[float], - meas_kernels: List[str], - discriminators: List[str], - hamiltonian: Dict[str, Any] = None, - channel_bandwidth=None, - acquisition_latency=None, - conditional_latency=None, - meas_map=None, - max_experiments=None, - sample_name=None, - n_registers=None, - register_map=None, - configurable=None, - credits_required=None, - online_date=None, - display_name=None, - description=None, - tags=None, - channels: Dict[str, Any] = None, - **kwargs, - ): - """ - Initialize a backend configuration that contains all the extra configuration that is made - available for OpenPulse backends. - - Args: - backend_name: backend name. - backend_version: backend version in the form X.Y.Z. - n_qubits: number of qubits. - basis_gates: list of basis gates names on the backend. - gates: list of basis gates on the backend. - local: backend is local or remote. - simulator: backend is a simulator. - conditional: backend supports conditional operations. - open_pulse: backend supports open pulse. - memory: backend supports memory. - max_shots: maximum number of shots supported. - coupling_map (list): The coupling map for the device - n_uchannels: Number of u-channels. - u_channel_lo: U-channel relationship on device los. - meas_levels: Supported measurement levels. - qubit_lo_range: Qubit lo ranges for each qubit with form (min, max) in GHz. - meas_lo_range: Measurement lo ranges for each qubit with form (min, max) in GHz. - dt: Qubit drive channel timestep in nanoseconds. - dtm: Measurement drive channel timestep in nanoseconds. - rep_times: Supported repetition times (program execution time) for backend in μs. - meas_kernels: Supported measurement kernels. - discriminators: Supported discriminators. - hamiltonian: An optional dictionary with fields characterizing the system hamiltonian. - channel_bandwidth (list): Bandwidth of all channels - (qubit, measurement, and U) - acquisition_latency (list): Array of dimension - n_qubits x n_registers. Latency (in units of dt) to write a - measurement result from qubit n into register slot m. - conditional_latency (list): Array of dimension n_channels - [d->u->m] x n_registers. Latency (in units of dt) to do a - conditional operation on channel n from register slot m - meas_map (list): Grouping of measurement which are multiplexed - max_experiments (int): The maximum number of experiments per job - sample_name (str): Sample name for the backend - n_registers (int): Number of register slots available for feedback - (if conditional is True) - register_map (list): An array of dimension n_qubits X - n_registers that specifies whether a qubit can store a - measurement in a certain register slot. - configurable (bool): True if the backend is configurable, if the - backend is a simulator - credits_required (bool): True if backend requires credits to run a - job. - online_date (datetime.datetime): The date that the device went online - display_name (str): Alternate name field for the backend - description (str): A description for the backend - tags (list): A list of string tags to describe the backend - channels: An optional dictionary containing information of each channel -- their - purpose, type, and qubits operated on. - **kwargs: Optional fields. - """ - self.n_uchannels = n_uchannels - self.u_channel_lo = u_channel_lo - self.meas_levels = meas_levels - - # convert from GHz to Hz - self.qubit_lo_range = [ - [min_range * 1e9, max_range * 1e9] for (min_range, max_range) in qubit_lo_range - ] - self.meas_lo_range = [ - [min_range * 1e9, max_range * 1e9] for (min_range, max_range) in meas_lo_range - ] - - self.meas_kernels = meas_kernels - self.discriminators = discriminators - self.hamiltonian = hamiltonian - if hamiltonian is not None: - self.hamiltonian = dict(hamiltonian) - self.hamiltonian["vars"] = { - k: v * 1e9 if isinstance(v, numbers.Number) else v - for k, v in self.hamiltonian["vars"].items() - } - - self.rep_times = [_rt * 1e-6 for _rt in rep_times] # convert to sec - - self.dt = dt * 1e-9 - self.dtm = dtm * 1e-9 - - if channels is not None: - self.channels = channels - - ( - self._qubit_channel_map, - self._channel_qubit_map, - self._control_channels, - ) = self._parse_channels(channels=channels) - else: - self._control_channels = defaultdict(list) - - if channel_bandwidth is not None: - self.channel_bandwidth = [ - [min_range * 1e9, max_range * 1e9] for (min_range, max_range) in channel_bandwidth - ] - if acquisition_latency is not None: - self.acquisition_latency = acquisition_latency - if conditional_latency is not None: - self.conditional_latency = conditional_latency - if meas_map is not None: - self.meas_map = meas_map - super().__init__( - backend_name=backend_name, - backend_version=backend_version, - n_qubits=n_qubits, - basis_gates=basis_gates, - gates=gates, - local=local, - simulator=simulator, - conditional=conditional, - open_pulse=open_pulse, - memory=memory, - max_shots=max_shots, - coupling_map=coupling_map, - max_experiments=max_experiments, - sample_name=sample_name, - n_registers=n_registers, - register_map=register_map, - configurable=configurable, - credits_required=credits_required, - online_date=online_date, - display_name=display_name, - description=description, - tags=tags, - **kwargs, - ) - - @classmethod - def from_dict(cls, data): - """Create a new GateConfig object from a dictionary. - - Args: - data (dict): A dictionary representing the GateConfig to create. - It will be in the same format as output by :func:`to_dict`. - - Returns: - GateConfig: The GateConfig from the input dictionary. - """ - in_data = copy.copy(data) - gates = [GateConfig.from_dict(x) for x in in_data.pop("gates")] - in_data["gates"] = gates - input_uchannels = in_data.pop("u_channel_lo") - u_channels = [] - for channel in input_uchannels: - u_channels.append([UchannelLO.from_dict(x) for x in channel]) - in_data["u_channel_lo"] = u_channels - return cls(**in_data) - - def to_dict(self): - """Return a dictionary format representation of the GateConfig. - - Returns: - dict: The dictionary form of the GateConfig. - """ - out_dict = super().to_dict() - u_channel_lo = [] - for x in self.u_channel_lo: - channel = [] - for y in x: - channel.append(y.to_dict()) - u_channel_lo.append(channel) - out_dict.update( - { - "n_uchannels": self.n_uchannels, - "u_channel_lo": u_channel_lo, - "meas_levels": self.meas_levels, - "qubit_lo_range": self.qubit_lo_range, - "meas_lo_range": self.meas_lo_range, - "meas_kernels": self.meas_kernels, - "discriminators": self.discriminators, - "rep_times": self.rep_times, - "dt": self.dt, - "dtm": self.dtm, - } - ) - - if hasattr(self, "channel_bandwidth"): - out_dict["channel_bandwidth"] = self.channel_bandwidth - if hasattr(self, "meas_map"): - out_dict["meas_map"] = self.meas_map - if hasattr(self, "acquisition_latency"): - out_dict["acquisition_latency"] = self.acquisition_latency - if hasattr(self, "conditional_latency"): - out_dict["conditional_latency"] = self.conditional_latency - if "channels" in out_dict: - out_dict.pop("_qubit_channel_map") - out_dict.pop("_channel_qubit_map") - out_dict.pop("_control_channels") - - # Use GHz in dict - if self.qubit_lo_range: - out_dict["qubit_lo_range"] = [ - [min_range * 1e-9, max_range * 1e-9] - for (min_range, max_range) in self.qubit_lo_range - ] - - if self.meas_lo_range: - out_dict["meas_lo_range"] = [ - [min_range * 1e-9, max_range * 1e-9] - for (min_range, max_range) in self.meas_lo_range - ] - - if self.rep_times: - out_dict["rep_times"] = [_rt * 1e6 for _rt in self.rep_times] - - out_dict["dt"] *= 1e9 - out_dict["dtm"] *= 1e9 - - if hasattr(self, "channel_bandwidth"): - out_dict["channel_bandwidth"] = [ - [min_range * 1e-9, max_range * 1e-9] - for (min_range, max_range) in self.channel_bandwidth - ] - - if self.hamiltonian: - hamiltonian = copy.deepcopy(self.hamiltonian) - hamiltonian["vars"] = { - k: v * 1e-9 if isinstance(v, numbers.Number) else v - for k, v in hamiltonian["vars"].items() - } - out_dict["hamiltonian"] = hamiltonian - - if hasattr(self, "channels"): - out_dict["channels"] = self.channels - - return out_dict - - def __eq__(self, other): - if isinstance(other, QasmBackendConfiguration): - if self.to_dict() == other.to_dict(): - return True - return False - - @property - def sample_rate(self) -> float: - """Sample rate of the signal channels in Hz (1/dt).""" - return 1.0 / self.dt - - @property - def control_channels(self) -> Dict[Tuple[int, ...], List]: - """Return the control channels""" - return self._control_channels - - def drive(self, qubit: int) -> DriveChannel: - """ - Return the drive channel for the given qubit. - - Raises: - BackendConfigurationError: If the qubit is not a part of the system. - - Returns: - Qubit drive channel. - """ - if not 0 <= qubit < self.n_qubits: - raise BackendConfigurationError(f"Invalid index for {qubit}-qubit system.") - return DriveChannel(qubit) - - def measure(self, qubit: int) -> MeasureChannel: - """ - Return the measure stimulus channel for the given qubit. - - Raises: - BackendConfigurationError: If the qubit is not a part of the system. - Returns: - Qubit measurement stimulus line. - """ - if not 0 <= qubit < self.n_qubits: - raise BackendConfigurationError(f"Invalid index for {qubit}-qubit system.") - return MeasureChannel(qubit) - - def acquire(self, qubit: int) -> AcquireChannel: - """ - Return the acquisition channel for the given qubit. - - Raises: - BackendConfigurationError: If the qubit is not a part of the system. - Returns: - Qubit measurement acquisition line. - """ - if not 0 <= qubit < self.n_qubits: - raise BackendConfigurationError(f"Invalid index for {qubit}-qubit systems.") - return AcquireChannel(qubit) - - def control(self, qubits: Iterable[int] = None) -> List[ControlChannel]: - """ - Return the secondary drive channel for the given qubit -- typically utilized for - controlling multiqubit interactions. This channel is derived from other channels. - - Args: - qubits: Tuple or list of qubits of the form `(control_qubit, target_qubit)`. - - Raises: - BackendConfigurationError: If the ``qubits`` is not a part of the system or if - the backend does not provide `channels` information in its configuration. - - Returns: - List of control channels. - """ - try: - if isinstance(qubits, list): - qubits = tuple(qubits) - return self._control_channels[qubits] - except KeyError as ex: - raise BackendConfigurationError( - f"Couldn't find the ControlChannel operating on qubits {qubits} on " - f"{self.n_qubits}-qubit system. The ControlChannel information is retrieved " - "from the backend." - ) from ex - except AttributeError as ex: - raise BackendConfigurationError( - f"This backend - '{self.backend_name}' does not provide channel information." - ) from ex - - def get_channel_qubits(self, channel: Channel) -> List[int]: - """ - Return a list of indices for qubits which are operated on directly by the given ``channel``. - - Raises: - BackendConfigurationError: If ``channel`` is not a found or if - the backend does not provide `channels` information in its configuration. - - Returns: - List of qubits operated on my the given ``channel``. - """ - try: - return self._channel_qubit_map[channel] - except KeyError as ex: - raise BackendConfigurationError(f"Couldn't find the Channel - {channel}") from ex - except AttributeError as ex: - raise BackendConfigurationError( - f"This backend - '{self.backend_name}' does not provide channel information." - ) from ex - - def get_qubit_channels(self, qubit: Union[int, Iterable[int]]) -> List[Channel]: - r"""Return a list of channels which operate on the given ``qubit``. - - Raises: - BackendConfigurationError: If ``qubit`` is not a found or if - the backend does not provide `channels` information in its configuration. - - Returns: - List of ``Channel``\s operated on my the given ``qubit``. - """ - channels = set() - try: - if isinstance(qubit, int): - for key, value in self._qubit_channel_map.items(): - if qubit in key: - channels.update(value) - if len(channels) == 0: - raise KeyError - elif isinstance(qubit, list): - qubit = tuple(qubit) - channels.update(self._qubit_channel_map[qubit]) - elif isinstance(qubit, tuple): - channels.update(self._qubit_channel_map[qubit]) - return list(channels) - except KeyError as ex: - raise BackendConfigurationError(f"Couldn't find the qubit - {qubit}") from ex - except AttributeError as ex: - raise BackendConfigurationError( - f"This backend - '{self.backend_name}' does not provide channel information." - ) from ex - - def describe(self, channel: ControlChannel) -> Dict[DriveChannel, complex]: - """ - Return a basic description of the channel dependency. Derived channels are given weights - which describe how their frames are linked to other frames. - For instance, the backend could be configured with this setting:: - - u_channel_lo = [ - [UchannelLO(q=0, scale=1. + 0.j)], - [UchannelLO(q=0, scale=-1. + 0.j), UchannelLO(q=1, scale=1. + 0.j)] - ] - - Then, this method can be used as follows:: - - backend.configuration().describe(ControlChannel(1)) - >>> {DriveChannel(0): -1, DriveChannel(1): 1} - - Args: - channel: The derived channel to describe. - Raises: - BackendConfigurationError: If channel is not a ControlChannel. - Returns: - Control channel derivations. - """ - if not isinstance(channel, ControlChannel): - raise BackendConfigurationError("Can only describe ControlChannels.") - result = {} - for u_chan_lo in self.u_channel_lo[channel.index]: - result[DriveChannel(u_chan_lo.q)] = u_chan_lo.scale - return result - - def _parse_channels(self, channels: Dict[set, Any]) -> Dict[Any, Any]: - r""" - Generates a dictionaries of ``Channel``\s, and tuple of qubit(s) they operate on. - - Args: - channels: An optional dictionary containing information of each channel -- their - purpose, type, and qubits operated on. - - Returns: - qubit_channel_map: Dictionary mapping tuple of qubit(s) to list of ``Channel``\s. - channel_qubit_map: Dictionary mapping ``Channel`` to list of qubit(s). - control_channels: Dictionary mapping tuple of qubit(s), to list of - ``ControlChannel``\s. - """ - qubit_channel_map = defaultdict(list) - channel_qubit_map = defaultdict(list) - control_channels = defaultdict(list) - channels_dict = { - DriveChannel.prefix: DriveChannel, - ControlChannel.prefix: ControlChannel, - MeasureChannel.prefix: MeasureChannel, - "acquire": AcquireChannel, - } - for channel, config in channels.items(): - channel_prefix, index = self._get_channel_prefix_index(channel) - channel_type = channels_dict[channel_prefix] - qubits = tuple(config["operates"]["qubits"]) - if channel_prefix in channels_dict: - qubit_channel_map[qubits].append(channel_type(index)) - channel_qubit_map[(channel_type(index))].extend(list(qubits)) - if channel_prefix == ControlChannel.prefix: - control_channels[qubits].append(channel_type(index)) - return dict(qubit_channel_map), dict(channel_qubit_map), dict(control_channels) - - def _get_channel_prefix_index(self, channel: str) -> str: - """Return channel prefix and index from the given ``channel``. - - Args: - channel: Name of channel. - - Raises: - BackendConfigurationError: If invalid channel name is found. - - Return: - Channel name and index. For example, if ``channel=acquire0``, this method - returns ``acquire`` and ``0``. - """ - channel_prefix = re.match(r"(?P[a-z]+)(?P[0-9]+)", channel) - try: - return channel_prefix.group("channel"), int(channel_prefix.group("index")) - except AttributeError as ex: - raise BackendConfigurationError(f"Invalid channel name - '{channel}' found.") from ex diff --git a/qiskit/providers/models/backendproperties.py b/qiskit/providers/models/backendproperties.py deleted file mode 100644 index 75e7cd18d03a..000000000000 --- a/qiskit/providers/models/backendproperties.py +++ /dev/null @@ -1,517 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# 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. - -"""Backend Properties classes.""" - -import copy -import datetime -from typing import Any, Iterable, Tuple, Union, Dict -import dateutil.parser - -from qiskit.providers.exceptions import BackendPropertyError -from qiskit.utils import deprecate_func -from qiskit.utils.units import apply_prefix - -PropertyT = Tuple[Any, datetime.datetime] - - -class Nduv: - """Class representing name-date-unit-value - - Attributes: - date: date. - name: name. - unit: unit. - value: value. - """ - - def __init__(self, date, name, unit, value): - """Initialize a new name-date-unit-value object - - Args: - date (datetime.datetime): Date field - name (str): Name field - unit (str): Nduv unit - value (float): The value of the Nduv - """ - self.date = date - self.name = name - self.unit = unit - self.value = value - - @classmethod - def from_dict(cls, data): - """Create a new Nduv object from a dictionary. - - Args: - data (dict): A dictionary representing the Nduv to create. - It will be in the same format as output by - :func:`to_dict`. - - Returns: - Nduv: The Nduv from the input dictionary. - """ - return cls(**data) - - def to_dict(self): - """Return a dictionary format representation of the object. - - Returns: - dict: The dictionary form of the Nduv. - """ - out_dict = { - "date": self.date, - "name": self.name, - "unit": self.unit, - "value": self.value, - } - return out_dict - - def __eq__(self, other): - if isinstance(other, Nduv): - if self.to_dict() == other.to_dict(): - return True - return False - - def __repr__(self): - return f"Nduv({repr(self.date)}, {self.name}, {self.unit}, {self.value})" - - -class GateProperties: - """Class representing a gate's properties - - Attributes: - qubits: qubits. - gate: gate. - parameters: parameters. - """ - - _data = {} - - def __init__(self, qubits, gate, parameters, **kwargs): - """Initialize a new :class:`GateProperties` object - - Args: - qubits (list): A list of integers representing qubits - gate (str): The gates name - parameters (list): List of :class:`Nduv` objects for the - name-date-unit-value for the gate - kwargs: Optional additional fields - """ - self._data = {} - self.qubits = qubits - self.gate = gate - self.parameters = parameters - self._data.update(kwargs) - - def __getattr__(self, name): - try: - return self._data[name] - except KeyError as ex: - raise AttributeError(f"Attribute {name} is not defined") from ex - - @classmethod - def from_dict(cls, data): - """Create a new Gate object from a dictionary. - - Args: - data (dict): A dictionary representing the Gate to create. - It will be in the same format as output by - :func:`to_dict`. - - Returns: - GateProperties: The Nduv from the input dictionary. - """ - in_data = {} - for key, value in data.items(): - if key == "parameters": - in_data[key] = list(map(Nduv.from_dict, value)) - else: - in_data[key] = value - return cls(**in_data) - - def to_dict(self): - """Return a dictionary format representation of the BackendStatus. - - Returns: - dict: The dictionary form of the Gate. - """ - out_dict = {} - out_dict["qubits"] = self.qubits - out_dict["gate"] = self.gate - out_dict["parameters"] = [x.to_dict() for x in self.parameters] - out_dict.update(self._data) - return out_dict - - def __eq__(self, other): - if isinstance(other, GateProperties): - if self.to_dict() == other.to_dict(): - return True - return False - - -# Backwards compatibility. -Gate = GateProperties - - -class BackendProperties: - """Class representing backend properties - - This holds backend properties measured by the provider. All properties - which are provided optionally. These properties may describe qubits, gates, - or other general properties of the backend. - """ - - _data = {} - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The models in ``qiskit.providers.models`` and related objects are part " - "of the deprecated `BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " - "workflow requires these representations it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - stacklevel=3, - ) - def __init__( - self, backend_name, backend_version, last_update_date, qubits, gates, general, **kwargs - ): - """Initialize a BackendProperties instance. - - Args: - backend_name (str): Backend name. - backend_version (str): Backend version in the form X.Y.Z. - last_update_date (datetime.datetime or str): Last date/time that a property was - updated. If specified as a ``str``, it must be in ISO format. - qubits (list): System qubit parameters as a list of lists of - :class:`Nduv` objects - gates (list): System gate parameters as a list of :class:`GateProperties` - objects - general (list): General parameters as a list of :class:`Nduv` - objects - kwargs: optional additional fields - """ - self._data = {} - self.backend_name = backend_name - self.backend_version = backend_version - if isinstance(last_update_date, str): - last_update_date = dateutil.parser.isoparse(last_update_date) - self.last_update_date = last_update_date - self.general = general - self.qubits = qubits - self.gates = gates - - self._qubits = {} - for qubit, props in enumerate(qubits): - formatted_props = {} - for prop in props: - value = self._apply_prefix(prop.value, prop.unit) - formatted_props[prop.name] = (value, prop.date) - self._qubits[qubit] = formatted_props - - self._gates = {} - for gate in gates: - if gate.gate not in self._gates: - self._gates[gate.gate] = {} - formatted_props = {} - for param in gate.parameters: - value = self._apply_prefix(param.value, param.unit) - formatted_props[param.name] = (value, param.date) - self._gates[gate.gate][tuple(gate.qubits)] = formatted_props - self._data.update(kwargs) - - def __getattr__(self, name): - try: - return self._data[name] - except KeyError as ex: - raise AttributeError(f"Attribute {name} is not defined") from ex - - @classmethod - def from_dict(cls, data): - """Create a new BackendProperties object from a dictionary. - - Args: - data (dict): A dictionary representing the BackendProperties to create. It will be in - the same format as output by :meth:`to_dict`. - - Returns: - BackendProperties: The BackendProperties from the input dictionary. - """ - in_data = copy.copy(data) - backend_name = in_data.pop("backend_name") - backend_version = in_data.pop("backend_version") - last_update_date = in_data.pop("last_update_date") - qubits = [] - for qubit in in_data.pop("qubits"): - nduvs = [] - for nduv in qubit: - nduvs.append(Nduv.from_dict(nduv)) - qubits.append(nduvs) - gates = [GateProperties.from_dict(x) for x in in_data.pop("gates")] - general = [Nduv.from_dict(x) for x in in_data.pop("general")] - - return cls( - backend_name, backend_version, last_update_date, qubits, gates, general, **in_data - ) - - def to_dict(self): - """Return a dictionary format representation of the BackendProperties. - - Returns: - dict: The dictionary form of the BackendProperties. - """ - out_dict = { - "backend_name": self.backend_name, - "backend_version": self.backend_version, - "last_update_date": self.last_update_date, - } - out_dict["qubits"] = [] - for qubit in self.qubits: - qubit_props = [] - for item in qubit: - qubit_props.append(item.to_dict()) - out_dict["qubits"].append(qubit_props) - out_dict["gates"] = [x.to_dict() for x in self.gates] - out_dict["general"] = [x.to_dict() for x in self.general] - out_dict.update(self._data) - return out_dict - - def __eq__(self, other): - if isinstance(other, BackendProperties): - if self.to_dict() == other.to_dict(): - return True - return False - - def gate_property( - self, - gate: str, - qubits: Union[int, Iterable[int]] = None, - name: str = None, - ) -> Union[ - Dict[Tuple[int, ...], Dict[str, PropertyT]], - Dict[str, PropertyT], - PropertyT, - ]: - """ - Return the property of the given gate. - - Args: - gate: Name of the gate. - qubits: The qubit to find the property for. - name: Optionally used to specify which gate property to return. - - Returns: - Gate property as a tuple of the value and the time it was measured. - - Raises: - BackendPropertyError: If the property is not found or name is - specified but qubit is not. - """ - try: - result = self._gates[gate] - if qubits is not None: - if isinstance(qubits, int): - qubits = (qubits,) - result = result[tuple(qubits)] - if name: - result = result[name] - elif name: - raise BackendPropertyError(f"Provide qubits to get {name} of {gate}") - except KeyError as ex: - raise BackendPropertyError(f"Could not find the desired property for {gate}") from ex - return result - - def faulty_qubits(self): - """Return a list of faulty qubits.""" - faulty = [] - for qubit in self._qubits: - if not self.is_qubit_operational(qubit): - faulty.append(qubit) - return faulty - - def faulty_gates(self): - """Return a list of faulty gates.""" - faulty = [] - for gate in self.gates: - if not self.is_gate_operational(gate.gate, gate.qubits): - faulty.append(gate) - return faulty - - def is_gate_operational(self, gate: str, qubits: Union[int, Iterable[int]] = None) -> bool: - """ - Return the operational status of the given gate. - - Args: - gate: Name of the gate. - qubits: The qubit to find the operational status for. - - Returns: - bool: Operational status of the given gate. True if the gate is operational, - False otherwise. - """ - properties = self.gate_property(gate, qubits) - if "operational" in properties: - return bool(properties["operational"][0]) - return True # if property operational not existent, then True. - - def gate_error(self, gate: str, qubits: Union[int, Iterable[int]]) -> float: - """ - Return gate error estimates from backend properties. - - Args: - gate: The gate for which to get the error. - qubits: The specific qubits for the gate. - - Returns: - Gate error of the given gate and qubit(s). - """ - return self.gate_property(gate, qubits, "gate_error")[0] # Throw away datetime at index 1 - - def gate_length(self, gate: str, qubits: Union[int, Iterable[int]]) -> float: - """ - Return the duration of the gate in units of seconds. - - Args: - gate: The gate for which to get the duration. - qubits: The specific qubits for the gate. - - Returns: - Gate length of the given gate and qubit(s). - """ - return self.gate_property(gate, qubits, "gate_length")[0] # Throw away datetime at index 1 - - def qubit_property( - self, - qubit: int, - name: str = None, - ) -> Union[ - Dict[str, PropertyT], - PropertyT, - ]: - """ - Return the property of the given qubit. - - Args: - qubit: The property to look for. - name: Optionally used to specify within the hierarchy which property to return. - - Returns: - Qubit property as a tuple of the value and the time it was measured. - - Raises: - BackendPropertyError: If the property is not found. - """ - try: - result = self._qubits[qubit] - if name is not None: - result = result[name] - except KeyError as ex: - formatted_name = "y '" + name + "'" if name else "ies" - raise BackendPropertyError( - f"Couldn't find the propert{formatted_name} for qubit {qubit}." - ) from ex - return result - - def t1(self, qubit: int) -> float: # pylint: disable=invalid-name - """ - Return the T1 time of the given qubit. - - Args: - qubit: Qubit for which to return the T1 time of. - - Returns: - T1 time of the given qubit. - """ - return self.qubit_property(qubit, "T1")[0] # Throw away datetime at index 1 - - def t2(self, qubit: int) -> float: # pylint: disable=invalid-name - """ - Return the T2 time of the given qubit. - - Args: - qubit: Qubit for which to return the T2 time of. - - Returns: - T2 time of the given qubit. - """ - return self.qubit_property(qubit, "T2")[0] # Throw away datetime at index 1 - - def frequency(self, qubit: int) -> float: - """ - Return the frequency of the given qubit. - - Args: - qubit: Qubit for which to return frequency of. - - Returns: - Frequency of the given qubit. - """ - return self.qubit_property(qubit, "frequency")[0] # Throw away datetime at index 1 - - def readout_error(self, qubit: int) -> float: - """ - Return the readout error of the given qubit. - - Args: - qubit: Qubit for which to return the readout error of. - - Return: - Readout error of the given qubit. - """ - return self.qubit_property(qubit, "readout_error")[0] # Throw away datetime at index 1 - - def readout_length(self, qubit: int) -> float: - """ - Return the readout length [sec] of the given qubit. - - Args: - qubit: Qubit for which to return the readout length of. - - Return: - Readout length of the given qubit. - """ - return self.qubit_property(qubit, "readout_length")[0] # Throw away datetime at index 1 - - def is_qubit_operational(self, qubit: int) -> bool: - """ - Return the operational status of the given qubit. - - Args: - qubit: Qubit for which to return operational status of. - - Returns: - Operational status of the given qubit. - """ - properties = self.qubit_property(qubit) - if "operational" in properties: - return bool(properties["operational"][0]) - return True # if property operational not existent, then True. - - def _apply_prefix(self, value: float, unit: str) -> float: - """ - Given a SI unit prefix and value, apply the prefix to convert to - standard SI unit. - - Args: - value: The number to apply prefix to. - unit: String prefix. - - Returns: - Converted value. - - Raises: - BackendPropertyError: If the units aren't recognized. - """ - try: - return apply_prefix(value, unit) - except Exception as ex: - raise BackendPropertyError(f"Could not understand units: {unit}") from ex diff --git a/qiskit/providers/models/backendstatus.py b/qiskit/providers/models/backendstatus.py deleted file mode 100644 index 5001bffce5ed..000000000000 --- a/qiskit/providers/models/backendstatus.py +++ /dev/null @@ -1,94 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# 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. - -"""Class for backend status.""" - -import html -from qiskit.exceptions import QiskitError - - -class BackendStatus: - """Class representing Backend Status.""" - - def __init__( - self, - backend_name: str, - backend_version: str, - operational: bool, - pending_jobs: int, - status_msg: str, - ): - """Initialize a BackendStatus object - - Args: - backend_name: The backend's name - backend_version: The backend's version of the form X.Y.Z - operational: True if the backend is operational - pending_jobs: The number of pending jobs on the backend - status_msg: The status msg for the backend - - Raises: - QiskitError: If the backend version is in an invalid format - """ - self.backend_name = backend_name - self.backend_version = backend_version - self.operational = operational - if pending_jobs < 0: - raise QiskitError("Pending jobs must be >=0") - self.pending_jobs = pending_jobs - self.status_msg = status_msg - - @classmethod - def from_dict(cls, data): - """Create a new BackendStatus object from a dictionary. - - Args: - data (dict): A dictionary representing the BaseBakend to create. - It will be in the same format as output by - :func:`to_dict`. - - Returns: - BackendStatus: The BackendStatus from the input dictionary. - """ - return cls(**data) - - def to_dict(self): - """Return a dictionary format representation of the BackendStatus. - - Returns: - dict: The dictionary form of the QobjHeader. - """ - return self.__dict__ - - def __eq__(self, other): - if isinstance(other, BackendStatus): - if self.__dict__ == other.__dict__: - return True - return False - - def _repr_html_(self) -> str: - """Return html representation of the object - - Returns: - Representation used in Jupyter notebook and other IDE's that call the method - - """ - rpr = self.__repr__() - html_code = ( - f"
{html.escape(rpr)}
" - f"name: {self.backend_name}
" - f"version: {self.backend_version}," - f" pending jobs: {self.pending_jobs}
" - f"status: {self.status_msg}
" - ) - - return html_code diff --git a/qiskit/providers/models/jobstatus.py b/qiskit/providers/models/jobstatus.py deleted file mode 100644 index 2fc58e437ab9..000000000000 --- a/qiskit/providers/models/jobstatus.py +++ /dev/null @@ -1,66 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# 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. - -"""Class for job status.""" - - -class JobStatus: - """Model for JobStatus. - - Attributes: - job_id (str): backend job_id. - status (str): status of the job. - status_msg (str): status message. - """ - - _data = {} - - def __init__(self, job_id, status, status_msg, **kwargs): - self._data = {} - self.job_id = job_id - self.status = status - self.status_msg = status_msg - self._data.update(kwargs) - - @classmethod - def from_dict(cls, data): - """Create a new JobStatus object from a dictionary. - - Args: - data (dict): A dictionary representing the JobStatus to create. - It will be in the same format as output by - :meth:`to_dict`. - - Returns: - JobStatus: The ``JobStatus`` from the input dictionary. - """ - return cls(**data) - - def to_dict(self): - """Return a dictionary format representation of the JobStatus. - - Returns: - dict: The dictionary form of the JobStatus. - """ - out_dict = { - "job_id": self.job_id, - "status": self.status, - "status_msg": self.status_msg, - } - out_dict.update(self._data) - return out_dict - - def __getattr__(self, name): - try: - return self._data[name] - except KeyError as ex: - raise AttributeError(f"Attribute {name} is not defined") from ex diff --git a/qiskit/providers/models/pulsedefaults.py b/qiskit/providers/models/pulsedefaults.py deleted file mode 100644 index 8f4101ffc510..000000000000 --- a/qiskit/providers/models/pulsedefaults.py +++ /dev/null @@ -1,305 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2019. -# -# 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. - - -"""Model and schema for pulse defaults.""" -import warnings -from typing import Any, Dict, List - -from qiskit.pulse.instruction_schedule_map import InstructionScheduleMap, PulseQobjDef -from qiskit.qobj import PulseLibraryItem, PulseQobjInstruction -from qiskit.qobj.converters import QobjToInstructionConverter -from qiskit.utils.deprecate_pulse import deprecate_pulse_dependency - - -class MeasurementKernel: - """Class representing a Measurement Kernel.""" - - def __init__(self, name, params): - """Initialize a MeasurementKernel object - - Args: - name (str): The name of the measurement kernel - params: The parameters of the measurement kernel - """ - self.name = name - self.params = params - - def to_dict(self): - """Return a dictionary format representation of the MeasurementKernel. - - Returns: - dict: The dictionary form of the MeasurementKernel. - """ - return {"name": self.name, "params": self.params} - - @classmethod - def from_dict(cls, data): - """Create a new MeasurementKernel object from a dictionary. - - Args: - data (dict): A dictionary representing the MeasurementKernel - to create. It will be in the same format as output by - :meth:`to_dict`. - - Returns: - MeasurementKernel: The MeasurementKernel from the input dictionary. - """ - return cls(**data) - - -class Discriminator: - """Class representing a Discriminator.""" - - def __init__(self, name, params): - """Initialize a Discriminator object - - Args: - name (str): The name of the discriminator - params: The parameters of the discriminator - """ - self.name = name - self.params = params - - def to_dict(self): - """Return a dictionary format representation of the Discriminator. - - Returns: - dict: The dictionary form of the Discriminator. - """ - return {"name": self.name, "params": self.params} - - @classmethod - def from_dict(cls, data): - """Create a new Discriminator object from a dictionary. - - Args: - data (dict): A dictionary representing the Discriminator - to create. It will be in the same format as output by - :meth:`to_dict`. - - Returns: - Discriminator: The Discriminator from the input dictionary. - """ - return cls(**data) - - -class Command: - """Class representing a Command. - - Attributes: - name: Pulse command name. - """ - - _data = {} - - def __init__(self, name: str, qubits=None, sequence=None, **kwargs): - """Initialize a Command object - - Args: - name (str): The name of the command - qubits: The qubits for the command - sequence (PulseQobjInstruction): The sequence for the Command - kwargs: Optional additional fields - """ - self._data = {} - self.name = name - if qubits is not None: - self.qubits = qubits - if sequence is not None: - self.sequence = sequence - self._data.update(kwargs) - - def __getattr__(self, name): - try: - return self._data[name] - except KeyError as ex: - raise AttributeError(f"Attribute {name} is not defined") from ex - - def to_dict(self): - """Return a dictionary format representation of the Command. - - Returns: - dict: The dictionary form of the Command. - """ - out_dict = {"name": self.name} - if hasattr(self, "qubits"): - out_dict["qubits"] = self.qubits - if hasattr(self, "sequence"): - out_dict["sequence"] = [x.to_dict() for x in self.sequence] - out_dict.update(self._data) - return out_dict - - @classmethod - def from_dict(cls, data): - """Create a new Command object from a dictionary. - - Args: - data (dict): A dictionary representing the ``Command`` - to create. It will be in the same format as output by - :meth:`to_dict`. - - Returns: - Command: The ``Command`` from the input dictionary. - """ - # Pulse command data is nested dictionary. - # To avoid deepcopy and avoid mutating the source object, create new dict here. - in_data = {} - for key, value in data.items(): - if key == "sequence": - in_data[key] = list(map(PulseQobjInstruction.from_dict, value)) - else: - in_data[key] = value - return cls(**in_data) - - -class PulseDefaults: - """Description of default settings for Pulse systems. These are instructions or settings that - may be good starting points for the Pulse user. The user may modify these defaults for custom - scheduling. - """ - - _data = {} - - @deprecate_pulse_dependency - def __init__( - self, - qubit_freq_est: List[float], - meas_freq_est: List[float], - buffer: int, - pulse_library: List[PulseLibraryItem], - cmd_def: List[Command], - meas_kernel: MeasurementKernel = None, - discriminator: Discriminator = None, - **kwargs: Dict[str, Any], - ): - """ - Validate and reformat transport layer inputs to initialize. - Args: - qubit_freq_est: Estimated qubit frequencies in GHz. - meas_freq_est: Estimated measurement cavity frequencies in GHz. - buffer: Default buffer time (in units of dt) between pulses. - pulse_library: Pulse name and sample definitions. - cmd_def: Operation name and definition in terms of Commands. - meas_kernel: The measurement kernels - discriminator: The discriminators - **kwargs: Other attributes for the super class. - """ - self._data = {} - self.buffer = buffer - self.qubit_freq_est = [freq * 1e9 for freq in qubit_freq_est] - """Qubit frequencies in Hertz.""" - self.meas_freq_est = [freq * 1e9 for freq in meas_freq_est] - """Measurement frequencies in Hertz.""" - self.pulse_library = pulse_library - self.cmd_def = cmd_def - self.instruction_schedule_map = InstructionScheduleMap() - self.converter = QobjToInstructionConverter(pulse_library) - - for inst in cmd_def: - entry = PulseQobjDef(converter=self.converter, name=inst.name) - entry.define(inst.sequence, user_provided=False) - self.instruction_schedule_map._add( - instruction_name=inst.name, - qubits=tuple(inst.qubits), - entry=entry, - ) - - if meas_kernel is not None: - self.meas_kernel = meas_kernel - if discriminator is not None: - self.discriminator = discriminator - - self._data.update(kwargs) - - def __getattr__(self, name): - try: - return self._data[name] - except KeyError as ex: - raise AttributeError(f"Attribute {name} is not defined") from ex - - def to_dict(self): - """Return a dictionary format representation of the PulseDefaults. - Returns: - dict: The dictionary form of the PulseDefaults. - """ - out_dict = { - "qubit_freq_est": self.qubit_freq_est, - "meas_freq_est": self.qubit_freq_est, - "buffer": self.buffer, - "pulse_library": [x.to_dict() for x in self.pulse_library], - "cmd_def": [x.to_dict() for x in self.cmd_def], - } - if hasattr(self, "meas_kernel"): - out_dict["meas_kernel"] = self.meas_kernel.to_dict() - if hasattr(self, "discriminator"): - out_dict["discriminator"] = self.discriminator.to_dict() - for key, value in self.__dict__.items(): - if key not in [ - "qubit_freq_est", - "meas_freq_est", - "buffer", - "pulse_library", - "cmd_def", - "meas_kernel", - "discriminator", - "converter", - "instruction_schedule_map", - ]: - out_dict[key] = value - out_dict.update(self._data) - - out_dict["qubit_freq_est"] = [freq * 1e-9 for freq in self.qubit_freq_est] - out_dict["meas_freq_est"] = [freq * 1e-9 for freq in self.meas_freq_est] - return out_dict - - @classmethod - def from_dict(cls, data): - """Create a new PulseDefaults object from a dictionary. - - Args: - data (dict): A dictionary representing the PulseDefaults - to create. It will be in the same format as output by - :meth:`to_dict`. - Returns: - PulseDefaults: The PulseDefaults from the input dictionary. - """ - schema = { - "pulse_library": PulseLibraryItem, # The class PulseLibraryItem is deprecated - "cmd_def": Command, - "meas_kernel": MeasurementKernel, - "discriminator": Discriminator, - } - - # Pulse defaults data is nested dictionary. - # To avoid deepcopy and avoid mutating the source object, create new dict here. - in_data = {} - for key, value in data.items(): - if key in schema: - with warnings.catch_warnings(): - # The class PulseLibraryItem is deprecated - warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") - if isinstance(value, list): - in_data[key] = list(map(schema[key].from_dict, value)) - else: - in_data[key] = schema[key].from_dict(value) - else: - in_data[key] = value - - return cls(**in_data) - - def __str__(self): - qubit_freqs = [freq / 1e9 for freq in self.qubit_freq_est] - meas_freqs = [freq / 1e9 for freq in self.meas_freq_est] - qfreq = f"Qubit Frequencies [GHz]\n{qubit_freqs}" - mfreq = f"Measurement Frequencies [GHz]\n{meas_freqs} " - return f"<{self.__class__.__name__}({str(self.instruction_schedule_map)}{qfreq}\n{mfreq})>" diff --git a/qiskit/pulse/calibration_entries.py b/qiskit/pulse/calibration_entries.py index f0c0e4497fa1..0055e48f0682 100644 --- a/qiskit/pulse/calibration_entries.py +++ b/qiskit/pulse/calibration_entries.py @@ -13,7 +13,6 @@ """Internal format of calibration data in target.""" from __future__ import annotations import inspect -import warnings from abc import ABCMeta, abstractmethod from collections.abc import Sequence, Callable from enum import IntEnum @@ -21,9 +20,6 @@ from qiskit.pulse.exceptions import PulseError from qiskit.pulse.schedule import Schedule, ScheduleBlock -from qiskit.qobj.converters import QobjToInstructionConverter -from qiskit.qobj.pulse_qobj import PulseQobjInstruction -from qiskit.exceptions import QiskitError IncompletePulseQobj = object() @@ -285,97 +281,3 @@ def __eq__(self, other): def __str__(self): params_str = ", ".join(self.get_signature().parameters.keys()) return f"Callable {self._definition.__name__}({params_str})" - - -class PulseQobjDef(ScheduleDef): - """Qobj JSON serialized format instruction sequence. - - A JSON serialized program can be converted into Qiskit Pulse program with - the provided qobj converter. Because the Qobj JSON doesn't provide signature, - conversion process occurs when the signature is requested for the first time - and the generated pulse program is cached for performance. - - .. see_also:: - :class:`.CalibrationEntry` for the purpose of this class. - - """ - - def __init__( - self, - arguments: Sequence[str] | None = None, - converter: QobjToInstructionConverter | None = None, - name: str | None = None, - ): - """Define an empty entry. - - Args: - arguments: User provided argument names for this entry, if parameterized. - converter: Optional. Qobj to Qiskit converter. - name: Name of schedule. - """ - super().__init__(arguments=arguments) - - self._converter = converter or QobjToInstructionConverter(pulse_library=[]) - self._name = name - self._source: list[PulseQobjInstruction] | None = None - - def _build_schedule(self): - """Build pulse schedule from cmd-def sequence.""" - with warnings.catch_warnings(): - warnings.simplefilter(action="ignore", category=DeprecationWarning) - # `Schedule` is being deprecated in Qiskit 1.3 - schedule = Schedule(name=self._name) - try: - for qobj_inst in self._source: - for qiskit_inst in self._converter._get_sequences(qobj_inst): - schedule.insert(qobj_inst.t0, qiskit_inst, inplace=True) - self._definition = schedule - self._parse_argument() - except QiskitError as ex: - # When the play waveform data is missing in pulse_lib we cannot build schedule. - # Instead of raising an error, get_schedule should return None. - warnings.warn( - f"Pulse calibration cannot be built and the entry is ignored: {ex.message}.", - UserWarning, - ) - self._definition = IncompletePulseQobj - - def define( - self, - definition: list[PulseQobjInstruction], - user_provided: bool = False, - ): - # This doesn't generate signature immediately, because of lazy schedule build. - self._source = definition - self._user_provided = user_provided - - def get_signature(self) -> inspect.Signature: - if self._definition is None: - self._build_schedule() - return super().get_signature() - - def get_schedule(self, *args, **kwargs) -> Schedule | ScheduleBlock | None: - if self._definition is None: - self._build_schedule() - if self._definition is IncompletePulseQobj: - return None - return super().get_schedule(*args, **kwargs) - - def __eq__(self, other): - if isinstance(other, PulseQobjDef): - # If both objects are Qobj just check Qobj equality. - return self._source == other._source - if isinstance(other, ScheduleDef) and self._definition is None: - # To compare with other schedule def, this also generates schedule object from qobj. - self._build_schedule() - if hasattr(other, "_definition"): - return self._definition == other._definition - return False - - def __str__(self): - if self._definition is None: - # Avoid parsing schedule for pretty print. - return "PulseQobj" - if self._definition is IncompletePulseQobj: - return "None" - return super().__str__() diff --git a/qiskit/pulse/instruction_schedule_map.py b/qiskit/pulse/instruction_schedule_map.py index 75ce3b8ef755..96e8ad0a48a6 100644 --- a/qiskit/pulse/instruction_schedule_map.py +++ b/qiskit/pulse/instruction_schedule_map.py @@ -40,9 +40,6 @@ CalibrationEntry, ScheduleDef, CallableDef, - # for backward compatibility - PulseQobjDef, - CalibrationPublisher, ) from qiskit.pulse.exceptions import PulseError from qiskit.pulse.schedule import Schedule, ScheduleBlock diff --git a/qiskit/pulse/macros.py b/qiskit/pulse/macros.py index a01441dfc2f2..2d73c27e54b7 100644 --- a/qiskit/pulse/macros.py +++ b/qiskit/pulse/macros.py @@ -39,8 +39,6 @@ def measure( .. note:: This function internally dispatches schedule generation logic depending on input backend model. - For the :class:`.BackendV1`, it considers conventional :class:`.InstructionScheduleMap` - and utilizes the backend calibration defined for a group of qubits in the `meas_map`. For the :class:`.BackendV2`, it assembles calibrations of single qubit measurement defined in the backend target to build a composite measurement schedule for `qubits`. diff --git a/qiskit/qobj/__init__.py b/qiskit/qobj/__init__.py deleted file mode 100644 index 5922fdf5dd8b..000000000000 --- a/qiskit/qobj/__init__.py +++ /dev/null @@ -1,75 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# 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. - -""" -========================= -Qobj (:mod:`qiskit.qobj`) -========================= - -.. currentmodule:: qiskit.qobj - -Base -==== - -.. autosummary:: - :toctree: ../stubs/ - - QobjExperimentHeader - QobjHeader - -Qasm -==== - -.. autosummary:: - :toctree: ../stubs/ - - QasmQobj - QasmQobjInstruction - QasmQobjExperimentConfig - QasmQobjExperiment - QasmQobjConfig - QasmExperimentCalibrations - GateCalibration - -Pulse -===== - -.. autosummary:: - :toctree: ../stubs/ - - PulseQobj - PulseQobjInstruction - PulseQobjExperimentConfig - PulseQobjExperiment - PulseQobjConfig - QobjMeasurementOption - PulseLibraryItem -""" - -from qiskit.qobj.common import QobjExperimentHeader -from qiskit.qobj.common import QobjHeader - -from qiskit.qobj.pulse_qobj import PulseQobj -from qiskit.qobj.pulse_qobj import PulseQobjInstruction -from qiskit.qobj.pulse_qobj import PulseQobjExperimentConfig -from qiskit.qobj.pulse_qobj import PulseQobjExperiment -from qiskit.qobj.pulse_qobj import PulseQobjConfig -from qiskit.qobj.pulse_qobj import QobjMeasurementOption -from qiskit.qobj.pulse_qobj import PulseLibraryItem - -from qiskit.qobj.qasm_qobj import GateCalibration -from qiskit.qobj.qasm_qobj import QasmExperimentCalibrations -from qiskit.qobj.qasm_qobj import QasmQobj -from qiskit.qobj.qasm_qobj import QasmQobjInstruction -from qiskit.qobj.qasm_qobj import QasmQobjExperiment -from qiskit.qobj.qasm_qobj import QasmQobjConfig -from qiskit.qobj.qasm_qobj import QasmQobjExperimentConfig diff --git a/qiskit/qobj/common.py b/qiskit/qobj/common.py deleted file mode 100644 index 0f1e2372fd9a..000000000000 --- a/qiskit/qobj/common.py +++ /dev/null @@ -1,81 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# 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. - -"""Module providing definitions of common Qobj classes.""" -from types import SimpleNamespace - -from qiskit.utils import deprecate_func - - -class QobjDictField(SimpleNamespace): - """A class used to represent a dictionary field in Qobj - - Exists as a backwards compatibility shim around a dictionary for Qobjs - previously constructed using marshmallow. - """ - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The `Qobj` class and related functionality are part of the deprecated " - "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " - "workflow requires `Qobj` it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - ) - def __init__(self, **kwargs): - """Instantiate a new Qobj dict field object. - - Args: - kwargs: arbitrary keyword arguments that can be accessed as - attributes of the object. - """ - self.__dict__.update(kwargs) - - def to_dict(self): - """Return a dictionary format representation of the OpenQASM 2 Qobj. - - Returns: - dict: The dictionary form of the QobjHeader. - """ - return self.__dict__ - - @classmethod - def from_dict(cls, data): - """Create a new QobjHeader object from a dictionary. - - Args: - data (dict): A dictionary representing the QobjHeader to create. It - will be in the same format as output by :func:`to_dict`. - - Returns: - QobjDictFieldr: The QobjDictField from the input dictionary. - """ - - return cls(**data) - - def __eq__(self, other): - if isinstance(other, self.__class__): - if self.__dict__ == other.__dict__: - return True - return False - - -class QobjHeader(QobjDictField): - """A class used to represent a dictionary header in Qobj objects.""" - - pass - - -class QobjExperimentHeader(QobjHeader): - """A class representing a header dictionary for a Qobj Experiment.""" - - pass diff --git a/qiskit/qobj/converters/__init__.py b/qiskit/qobj/converters/__init__.py deleted file mode 100644 index 1eed0a90de89..000000000000 --- a/qiskit/qobj/converters/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2019. -# -# 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. - -""" -Helper modules to convert qiskit frontend object to proper qobj model. -""" - -from .pulse_instruction import InstructionToQobjConverter, QobjToInstructionConverter -from .lo_config import LoConfigConverter diff --git a/qiskit/qobj/converters/lo_config.py b/qiskit/qobj/converters/lo_config.py deleted file mode 100644 index a5b5beb80df2..000000000000 --- a/qiskit/qobj/converters/lo_config.py +++ /dev/null @@ -1,177 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2019. -# -# 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. - -"""Helper class used to convert a user LO configuration into a list of frequencies.""" - -from qiskit.pulse.channels import DriveChannel, MeasureChannel -from qiskit.pulse.configuration import LoConfig -from qiskit.exceptions import QiskitError -from qiskit.utils import deprecate_func - - -class LoConfigConverter: - """This class supports to convert LoConfig into ~`lo_freq` attribute of configs. - The format of LO frequency setup can be easily modified by replacing - ``get_qubit_los`` and ``get_meas_los`` to align with your backend. - """ - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The `Qobj` class and related functionality are part of the deprecated " - "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " - "workflow requires `Qobj` it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - ) - def __init__( - self, - qobj_model, - qubit_lo_freq=None, - meas_lo_freq=None, - qubit_lo_range=None, - meas_lo_range=None, - **run_config, - ): - """Create new converter. - - Args: - qobj_model (Union[PulseQobjExperimentConfig, QasmQobjExperimentConfig): qobj model for - experiment config. - qubit_lo_freq (Optional[List[float]]): List of default qubit LO frequencies in Hz. - meas_lo_freq (Optional[List[float]]): List of default meas LO frequencies in Hz. - qubit_lo_range (Optional[List[List[float]]]): List of qubit LO ranges, - each of form ``[range_min, range_max]`` in Hz. - meas_lo_range (Optional[List[List[float]]]): List of measurement LO ranges, - each of form ``[range_min, range_max]`` in Hz. - n_qubits (int): Number of qubits in the system. - run_config (dict): experimental configuration. - """ - self.qobj_model = qobj_model - self.qubit_lo_freq = qubit_lo_freq - self.meas_lo_freq = meas_lo_freq - self.run_config = run_config - self.n_qubits = self.run_config.get("n_qubits", None) - - self.default_lo_config = LoConfig() - - if qubit_lo_range: - for i, lo_range in enumerate(qubit_lo_range): - self.default_lo_config.add_lo_range(DriveChannel(i), lo_range) - - if meas_lo_range: - for i, lo_range in enumerate(meas_lo_range): - self.default_lo_config.add_lo_range(MeasureChannel(i), lo_range) - - def __call__(self, user_lo_config): - """Return experiment config w/ LO values property configured. - - Args: - user_lo_config (LoConfig): A dictionary of LOs to format. - - Returns: - Union[PulseQobjExperimentConfig, QasmQobjExperimentConfig]: Qobj experiment config. - """ - lo_config = {} - - q_los = self.get_qubit_los(user_lo_config) - if q_los: - lo_config["qubit_lo_freq"] = [freq / 1e9 for freq in q_los] - - m_los = self.get_meas_los(user_lo_config) - if m_los: - lo_config["meas_lo_freq"] = [freq / 1e9 for freq in m_los] - - return self.qobj_model(**lo_config) - - def get_qubit_los(self, user_lo_config): - """Set experiment level qubit LO frequencies. Use default values from job level if - experiment level values not supplied. If experiment level and job level values not supplied, - raise an error. If configured LO frequency is the same as default, this method returns - ``None``. - - Args: - user_lo_config (LoConfig): A dictionary of LOs to format. - - Returns: - List[float]: A list of qubit LOs. - - Raises: - QiskitError: When LO frequencies are missing and no default is set at job level. - """ - _q_los = None - - # try to use job level default values - if self.qubit_lo_freq: - _q_los = self.qubit_lo_freq.copy() - # otherwise initialize list with ``None`` entries - elif self.n_qubits: - _q_los = [None] * self.n_qubits - - # fill experiment level LO's - if _q_los: - for channel, lo_freq in user_lo_config.qubit_los.items(): - self.default_lo_config.check_lo(channel, lo_freq) - _q_los[channel.index] = lo_freq - - if _q_los == self.qubit_lo_freq: - return None - - # if ``None`` remains in LO's, indicates default not provided and user value is missing - # raise error - if None in _q_los: - raise QiskitError( - "Invalid experiment level qubit LO's. Must either pass values " - "for all drive channels or pass 'default_qubit_los'." - ) - - return _q_los - - def get_meas_los(self, user_lo_config): - """Set experiment level meas LO frequencies. Use default values from job level if experiment - level values not supplied. If experiment level and job level values not supplied, raise an - error. If configured LO frequency is the same as default, this method returns ``None``. - - Args: - user_lo_config (LoConfig): A dictionary of LOs to format. - - Returns: - List[float]: A list of measurement LOs. - - Raises: - QiskitError: When LO frequencies are missing and no default is set at job level. - """ - _m_los = None - # try to use job level default values - if self.meas_lo_freq: - _m_los = self.meas_lo_freq.copy() - # otherwise initialize list with ``None`` entries - elif self.n_qubits: - _m_los = [None] * self.n_qubits - - # fill experiment level LO's - if _m_los: - for channel, lo_freq in user_lo_config.meas_los.items(): - self.default_lo_config.check_lo(channel, lo_freq) - _m_los[channel.index] = lo_freq - - if _m_los == self.meas_lo_freq: - return None - - # if ``None`` remains in LO's, indicates default not provided and user value is missing - # raise error - if None in _m_los: - raise QiskitError( - "Invalid experiment level measurement LO's. Must either pass " - "values for all measurement channels or pass 'default_meas_los'." - ) - - return _m_los diff --git a/qiskit/qobj/converters/pulse_instruction.py b/qiskit/qobj/converters/pulse_instruction.py deleted file mode 100644 index 1c7b740d92f6..000000000000 --- a/qiskit/qobj/converters/pulse_instruction.py +++ /dev/null @@ -1,901 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2019. -# -# 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. - -# pylint: disable=invalid-name, missing-function-docstring - -"""Helper class used to convert a pulse instruction into PulseQobjInstruction.""" - -import hashlib -import re -import warnings -from enum import Enum -from functools import singledispatchmethod -from typing import Union, List, Iterator, Optional -import numpy as np - -from qiskit.circuit import Parameter, ParameterExpression -from qiskit.pulse import channels, instructions, library -from qiskit.pulse.configuration import Kernel, Discriminator -from qiskit.pulse.exceptions import QiskitError -from qiskit.pulse.parser import parse_string_expr -from qiskit.pulse.schedule import Schedule -from qiskit.qobj import QobjMeasurementOption, PulseLibraryItem, PulseQobjInstruction -from qiskit.qobj.utils import MeasLevel -from qiskit.utils import deprecate_func -from qiskit.utils.deprecate_pulse import deprecate_pulse_dependency - - -class ParametricPulseShapes(Enum): - """Map the assembled pulse names to the pulse module waveforms. - - The enum name is the transport layer name for pulse shapes, the - value is its mapping to the OpenPulse Command in Qiskit. - """ - - gaussian = "Gaussian" - gaussian_square = "GaussianSquare" - gaussian_square_drag = "GaussianSquareDrag" - gaussian_square_echo = "gaussian_square_echo" - drag = "Drag" - constant = "Constant" - - @classmethod - def from_instance( - cls, - instance: library.SymbolicPulse, - ) -> "ParametricPulseShapes": - """Get Qobj name from the pulse class instance. - - Args: - instance: SymbolicPulse class. - - Returns: - Qobj name. - - Raises: - QiskitError: When pulse instance is not recognizable type. - """ - if isinstance(instance, library.SymbolicPulse): - return cls(instance.pulse_type) - - raise QiskitError(f"'{instance}' is not valid pulse type.") - - @classmethod - def to_type(cls, name: str) -> library.SymbolicPulse: - """Get symbolic pulse class from the name. - - Args: - name: Qobj name of the pulse. - - Returns: - Corresponding class. - """ - return getattr(library, cls[name].value) - - -class InstructionToQobjConverter: - """Converts Qiskit Pulse in-memory representation into Qobj data. - - This converter converts the Qiskit Pulse in-memory representation into - the transfer layer format to submit the data from client to the server. - - The transfer layer format must be the text representation that conforms to - the `OpenPulse specification`__. - Extension to the OpenPulse can be achieved by subclassing this this with - extra methods corresponding to each augmented instruction. For example, - - .. plot:: - :include-source: - :nofigs: - - class MyConverter(InstructionToQobjConverter): - - def _convert_NewInstruction(self, instruction, time_offset): - command_dict = { - 'name': 'new_inst', - 't0': time_offset + instruction.start_time, - 'param1': instruction.param1, - 'param2': instruction.param2 - } - return self._qobj_model(**command_dict) - - where ``NewInstruction`` must be a class name of Qiskit Pulse instruction. - """ - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The `Qobj` class and related functionality are part of the deprecated " - "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " - "workflow requires `Qobj` it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - ) - def __init__( - self, - qobj_model: PulseQobjInstruction, - **run_config, - ): - """Create new converter. - - Args: - qobj_model: Transfer layer data schema. - run_config: Run configuration. - """ - self._qobj_model = qobj_model - self._run_config = run_config - - def __call__( - self, - shift: int, - instruction: Union[instructions.Instruction, List[instructions.Acquire]], - ) -> PulseQobjInstruction: - """Convert Qiskit in-memory representation to Qobj instruction. - - Args: - instruction: Instruction data in Qiskit Pulse. - - Returns: - Qobj instruction data. - - Raises: - QiskitError: When list of instruction is provided except for Acquire. - """ - if isinstance(instruction, list): - if all(isinstance(inst, instructions.Acquire) for inst in instruction): - return self._convert_bundled_acquire( - instruction_bundle=instruction, - time_offset=shift, - ) - raise QiskitError("Bundle of instruction is not supported except for Acquire.") - - return self._convert_instruction(instruction, shift) - - @singledispatchmethod - def _convert_instruction( - self, - instruction, - time_offset: int, - ) -> PulseQobjInstruction: - raise QiskitError( - f"Pulse Qobj doesn't support {instruction.__class__.__name__}. " - "This instruction cannot be submitted with Qobj." - ) - - @_convert_instruction.register(instructions.Acquire) - def _convert_acquire( - self, - instruction, - time_offset: int, - ) -> PulseQobjInstruction: - """Return converted `Acquire`. - - Args: - instruction: Qiskit Pulse acquire instruction. - time_offset: Offset time. - - Returns: - Qobj instruction data. - """ - meas_level = self._run_config.get("meas_level", 2) - mem_slot = [] - if instruction.mem_slot: - mem_slot = [instruction.mem_slot.index] - - command_dict = { - "name": "acquire", - "t0": time_offset + instruction.start_time, - "duration": instruction.duration, - "qubits": [instruction.channel.index], - "memory_slot": mem_slot, - } - if meas_level == MeasLevel.CLASSIFIED: - # setup discriminators - if instruction.discriminator: - command_dict.update( - { - "discriminators": [ - QobjMeasurementOption( - name=instruction.discriminator.name, - params=instruction.discriminator.params, - ) - ] - } - ) - # setup register_slots - if instruction.reg_slot: - command_dict.update({"register_slot": [instruction.reg_slot.index]}) - if meas_level in [MeasLevel.KERNELED, MeasLevel.CLASSIFIED]: - # setup kernels - if instruction.kernel: - command_dict.update( - { - "kernels": [ - QobjMeasurementOption( - name=instruction.kernel.name, params=instruction.kernel.params - ) - ] - } - ) - return self._qobj_model(**command_dict) - - @_convert_instruction.register(instructions.SetFrequency) - def _convert_set_frequency( - self, - instruction, - time_offset: int, - ) -> PulseQobjInstruction: - """Return converted `SetFrequency`. - - Args: - instruction: Qiskit Pulse set frequency instruction. - time_offset: Offset time. - - Returns: - Qobj instruction data. - """ - command_dict = { - "name": "setf", - "t0": time_offset + instruction.start_time, - "ch": instruction.channel.name, - "frequency": instruction.frequency / 10**9, - } - return self._qobj_model(**command_dict) - - @_convert_instruction.register(instructions.ShiftFrequency) - def _convert_shift_frequency( - self, - instruction, - time_offset: int, - ) -> PulseQobjInstruction: - """Return converted `ShiftFrequency`. - - Args: - instruction: Qiskit Pulse shift frequency instruction. - time_offset: Offset time. - - Returns: - Qobj instruction data. - """ - command_dict = { - "name": "shiftf", - "t0": time_offset + instruction.start_time, - "ch": instruction.channel.name, - "frequency": instruction.frequency / 10**9, - } - return self._qobj_model(**command_dict) - - @_convert_instruction.register(instructions.SetPhase) - def _convert_set_phase( - self, - instruction, - time_offset: int, - ) -> PulseQobjInstruction: - """Return converted `SetPhase`. - - Args: - instruction: Qiskit Pulse set phase instruction. - time_offset: Offset time. - - Returns: - Qobj instruction data. - """ - command_dict = { - "name": "setp", - "t0": time_offset + instruction.start_time, - "ch": instruction.channel.name, - "phase": instruction.phase, - } - return self._qobj_model(**command_dict) - - @_convert_instruction.register(instructions.ShiftPhase) - def _convert_shift_phase( - self, - instruction, - time_offset: int, - ) -> PulseQobjInstruction: - """Return converted `ShiftPhase`. - - Args: - instruction: Qiskit Pulse shift phase instruction. - time_offset: Offset time. - - Returns: - Qobj instruction data. - """ - command_dict = { - "name": "fc", - "t0": time_offset + instruction.start_time, - "ch": instruction.channel.name, - "phase": instruction.phase, - } - return self._qobj_model(**command_dict) - - @_convert_instruction.register(instructions.Delay) - def _convert_delay( - self, - instruction, - time_offset: int, - ) -> PulseQobjInstruction: - """Return converted `Delay`. - - Args: - instruction: Qiskit Pulse delay instruction. - time_offset: Offset time. - - Returns: - Qobj instruction data. - """ - command_dict = { - "name": "delay", - "t0": time_offset + instruction.start_time, - "ch": instruction.channel.name, - "duration": instruction.duration, - } - return self._qobj_model(**command_dict) - - @_convert_instruction.register(instructions.Play) - def _convert_play( - self, - instruction, - time_offset: int, - ) -> PulseQobjInstruction: - """Return converted `Play`. - - Args: - instruction: Qiskit Pulse play instruction. - time_offset: Offset time. - - Returns: - Qobj instruction data. - """ - if isinstance(instruction.pulse, library.SymbolicPulse): - params = dict(instruction.pulse.parameters) - # IBM backends expect "amp" to be the complex amplitude - if "amp" in params and "angle" in params: - params["amp"] = complex(params["amp"] * np.exp(1j * params["angle"])) - del params["angle"] - - command_dict = { - "name": "parametric_pulse", - "pulse_shape": ParametricPulseShapes.from_instance(instruction.pulse).name, - "t0": time_offset + instruction.start_time, - "ch": instruction.channel.name, - "parameters": params, - } - else: - command_dict = { - "name": instruction.name, - "t0": time_offset + instruction.start_time, - "ch": instruction.channel.name, - } - - return self._qobj_model(**command_dict) - - @_convert_instruction.register(instructions.Snapshot) - def _convert_snapshot( - self, - instruction, - time_offset: int, - ) -> PulseQobjInstruction: - """Return converted `Snapshot`. - - Args: - time_offset: Offset time. - instruction: Qiskit Pulse snapshot instruction. - - Returns: - Qobj instruction data. - """ - command_dict = { - "name": "snapshot", - "t0": time_offset + instruction.start_time, - "label": instruction.label, - "type": instruction.type, - } - return self._qobj_model(**command_dict) - - def _convert_bundled_acquire( - self, - instruction_bundle: List[instructions.Acquire], - time_offset: int, - ) -> PulseQobjInstruction: - """Return converted list of parallel `Acquire` instructions. - - Args: - instruction_bundle: List of Qiskit Pulse acquire instruction. - time_offset: Offset time. - - Returns: - Qobj instruction data. - - Raises: - QiskitError: When instructions are not aligned. - QiskitError: When instructions have different duration. - QiskitError: When discriminator or kernel is missing in a part of instructions. - """ - meas_level = self._run_config.get("meas_level", 2) - - t0 = instruction_bundle[0].start_time - duration = instruction_bundle[0].duration - memory_slots = [] - register_slots = [] - qubits = [] - discriminators = [] - kernels = [] - - for instruction in instruction_bundle: - qubits.append(instruction.channel.index) - - if instruction.start_time != t0: - raise QiskitError( - "The supplied acquire instructions have different starting times. " - "Something has gone wrong calling this code. Please report this " - "issue." - ) - - if instruction.duration != duration: - raise QiskitError( - "Acquire instructions beginning at the same time must have same duration." - ) - - if instruction.mem_slot: - memory_slots.append(instruction.mem_slot.index) - - if meas_level == MeasLevel.CLASSIFIED: - # setup discriminators - if instruction.discriminator: - discriminators.append( - QobjMeasurementOption( - name=instruction.discriminator.name, - params=instruction.discriminator.params, - ) - ) - # setup register_slots - if instruction.reg_slot: - register_slots.append(instruction.reg_slot.index) - - if meas_level in [MeasLevel.KERNELED, MeasLevel.CLASSIFIED]: - # setup kernels - if instruction.kernel: - kernels.append( - QobjMeasurementOption( - name=instruction.kernel.name, params=instruction.kernel.params - ) - ) - command_dict = { - "name": "acquire", - "t0": time_offset + t0, - "duration": duration, - "qubits": qubits, - } - if memory_slots: - command_dict["memory_slot"] = memory_slots - - if register_slots: - command_dict["register_slot"] = register_slots - - if discriminators: - num_discriminators = len(discriminators) - if num_discriminators == len(qubits) or num_discriminators == 1: - command_dict["discriminators"] = discriminators - else: - raise QiskitError( - "A discriminator must be supplied for every acquisition or a single " - "discriminator for all acquisitions." - ) - - if kernels: - num_kernels = len(kernels) - if num_kernels == len(qubits) or num_kernels == 1: - command_dict["kernels"] = kernels - else: - raise QiskitError( - "A kernel must be supplied for every acquisition or a single " - "kernel for all acquisitions." - ) - - return self._qobj_model(**command_dict) - - -class QobjToInstructionConverter: - """Converts Qobj data into Qiskit Pulse in-memory representation. - - This converter converts data from transfer layer into the in-memory representation of - the front-end of Qiskit Pulse. - - The transfer layer format must be the text representation that conforms to - the `OpenPulse specification`__. - Extension to the OpenPulse can be achieved by subclassing this this with - extra methods corresponding to each augmented instruction. For example, - - .. plot:: - :include-source: - :nofigs: - - class MyConverter(QobjToInstructionConverter): - - def get_supported_instructions(self): - instructions = super().get_supported_instructions() - instructions += ["new_inst"] - - return instructions - - def _convert_new_inst(self, instruction): - return NewInstruction(...) - - where ``NewInstruction`` must be a subclass of :class:`~qiskit.pulse.instructions.Instruction`. - """ - - __chan_regex__ = re.compile(r"([a-zA-Z]+)(\d+)") - - @deprecate_pulse_dependency - def __init__( - self, - pulse_library: Optional[List[PulseLibraryItem]] = None, - **run_config, - ): - """Create new converter. - - Args: - pulse_library: Pulse library in Qobj format. - run_config: Run configuration. - """ - pulse_library_dict = {} - for lib_item in pulse_library: - pulse_library_dict[lib_item.name] = lib_item.samples - - self._pulse_library = pulse_library_dict - self._run_config = run_config - - def __call__(self, instruction: PulseQobjInstruction) -> Schedule: - """Convert Qobj instruction to Qiskit in-memory representation. - - Args: - instruction: Instruction data in Qobj format. - - Returns: - Scheduled Qiskit Pulse instruction in Schedule format. - """ - schedule = Schedule() - for inst in self._get_sequences(instruction): - schedule.insert(instruction.t0, inst, inplace=True) - return schedule - - def _get_sequences( - self, - instruction: PulseQobjInstruction, - ) -> Iterator[instructions.Instruction]: - """A method to iterate over pulse instructions without creating Schedule. - - .. note:: - - This is internal fast-path function, and callers other than this converter class - might directly use this method to generate schedule from multiple - Qobj instructions. Because __call__ always returns a schedule with the time offset - parsed instruction, composing multiple Qobj instructions to create - a gate schedule is somewhat inefficient due to composing overhead of schedules. - Directly combining instructions with this method is much performant. - - Args: - instruction: Instruction data in Qobj format. - - Yields: - Qiskit Pulse instructions. - - :meta public: - """ - try: - method = getattr(self, f"_convert_{instruction.name}") - except AttributeError: - method = self._convert_generic - - yield from method(instruction) - - def get_supported_instructions(self) -> List[str]: - """Retrun a list of supported instructions.""" - return [ - "acquire", - "setp", - "fc", - "setf", - "shiftf", - "delay", - "parametric_pulse", - "snapshot", - ] - - def get_channel(self, channel: str) -> channels.PulseChannel: - """Parse and retrieve channel from ch string. - - Args: - channel: String identifier of pulse instruction channel. - - Returns: - Matched channel object. - - Raises: - QiskitError: Is raised if valid channel is not matched - """ - match = self.__chan_regex__.match(channel) - if match: - prefix, index = match.group(1), int(match.group(2)) - - if prefix == channels.DriveChannel.prefix: - return channels.DriveChannel(index) - elif prefix == channels.MeasureChannel.prefix: - return channels.MeasureChannel(index) - elif prefix == channels.ControlChannel.prefix: - return channels.ControlChannel(index) - - raise QiskitError(f"Channel {channel} is not valid") - - @staticmethod - def disassemble_value(value_expr: Union[float, str]) -> Union[float, ParameterExpression]: - """A helper function to format instruction operand. - - If parameter in string representation is specified, this method parses the - input string and generates Qiskit ParameterExpression object. - - Args: - value_expr: Operand value in Qobj. - - Returns: - Parsed operand value. ParameterExpression object is returned if value is not number. - """ - if isinstance(value_expr, str): - str_expr = parse_string_expr(value_expr, partial_binding=False) - value_expr = str_expr(**{pname: Parameter(pname) for pname in str_expr.params}) - - return value_expr - - def _convert_acquire( - self, - instruction: PulseQobjInstruction, - ) -> Iterator[instructions.Instruction]: - """Return converted `Acquire` instruction. - - Args: - instruction: Acquire qobj - - Yields: - Qiskit Pulse acquire instructions - """ - duration = instruction.duration - qubits = instruction.qubits - acquire_channels = [channels.AcquireChannel(qubit) for qubit in qubits] - - mem_slots = [channels.MemorySlot(instruction.memory_slot[i]) for i in range(len(qubits))] - - if hasattr(instruction, "register_slot"): - register_slots = [ - channels.RegisterSlot(instruction.register_slot[i]) for i in range(len(qubits)) - ] - else: - register_slots = [None] * len(qubits) - - discriminators = ( - instruction.discriminators if hasattr(instruction, "discriminators") else None - ) - if not isinstance(discriminators, list): - discriminators = [discriminators] - if any(discriminators[i] != discriminators[0] for i in range(len(discriminators))): - warnings.warn( - "Can currently only support one discriminator per acquire. Defaulting " - "to first discriminator entry." - ) - discriminator = discriminators[0] - if discriminator: - discriminator = Discriminator(name=discriminators[0].name, **discriminators[0].params) - - kernels = instruction.kernels if hasattr(instruction, "kernels") else None - if not isinstance(kernels, list): - kernels = [kernels] - if any(kernels[0] != kernels[i] for i in range(len(kernels))): - warnings.warn( - "Can currently only support one kernel per acquire. Defaulting to first " - "kernel entry." - ) - kernel = kernels[0] - if kernel: - kernel = Kernel(name=kernels[0].name, **kernels[0].params) - - for acquire_channel, mem_slot, reg_slot in zip(acquire_channels, mem_slots, register_slots): - yield instructions.Acquire( - duration, - acquire_channel, - mem_slot=mem_slot, - reg_slot=reg_slot, - kernel=kernel, - discriminator=discriminator, - ) - - def _convert_setp( - self, - instruction: PulseQobjInstruction, - ) -> Iterator[instructions.Instruction]: - """Return converted `SetPhase` instruction. - - Args: - instruction: SetPhase qobj instruction - - Yields: - Qiskit Pulse set phase instructions - """ - channel = self.get_channel(instruction.ch) - phase = self.disassemble_value(instruction.phase) - - yield instructions.SetPhase(phase, channel) - - def _convert_fc( - self, - instruction: PulseQobjInstruction, - ) -> Iterator[instructions.Instruction]: - """Return converted `ShiftPhase` instruction. - - Args: - instruction: ShiftPhase qobj instruction - - Yields: - Qiskit Pulse shift phase schedule instructions - """ - channel = self.get_channel(instruction.ch) - phase = self.disassemble_value(instruction.phase) - - yield instructions.ShiftPhase(phase, channel) - - def _convert_setf( - self, - instruction: PulseQobjInstruction, - ) -> Iterator[instructions.Instruction]: - """Return converted `SetFrequencyInstruction` instruction. - - .. note:: - - We assume frequency value is expressed in string with "GHz". - Operand value is thus scaled by a factor of 10^9. - - Args: - instruction: SetFrequency qobj instruction - - Yields: - Qiskit Pulse set frequency instructions - """ - channel = self.get_channel(instruction.ch) - frequency = self.disassemble_value(instruction.frequency) * 10**9 - - yield instructions.SetFrequency(frequency, channel) - - def _convert_shiftf( - self, - instruction: PulseQobjInstruction, - ) -> Iterator[instructions.Instruction]: - """Return converted `ShiftFrequency` instruction. - - .. note:: - - We assume frequency value is expressed in string with "GHz". - Operand value is thus scaled by a factor of 10^9. - - Args: - instruction: ShiftFrequency qobj instruction - - Yields: - Qiskit Pulse shift frequency schedule instructions - """ - channel = self.get_channel(instruction.ch) - frequency = self.disassemble_value(instruction.frequency) * 10**9 - - yield instructions.ShiftFrequency(frequency, channel) - - def _convert_delay( - self, - instruction: PulseQobjInstruction, - ) -> Iterator[instructions.Instruction]: - """Return converted `Delay` instruction. - - Args: - instruction: Delay qobj instruction - - Yields: - Qiskit Pulse delay instructions - """ - channel = self.get_channel(instruction.ch) - duration = instruction.duration - - yield instructions.Delay(duration, channel) - - def _convert_parametric_pulse( - self, - instruction: PulseQobjInstruction, - ) -> Iterator[instructions.Instruction]: - """Return converted `Play` instruction with parametric pulse operand. - - .. note:: - - If parametric pulse label is not provided by the backend, this method naively generates - a pulse name based on the pulse shape and bound parameters. This pulse name is formatted - to, for example, `gaussian_a4e3`, here the last four digits are a part of - the hash string generated based on the pulse shape and the parameters. - Because we are using a truncated hash for readability, - there may be a small risk of pulse name collision with other pulses. - Basically the parametric pulse name is used just for visualization purpose and - the pulse module should not have dependency on the parametric pulse names. - - Args: - instruction: Play qobj instruction with parametric pulse - - Yields: - Qiskit Pulse play schedule instructions - """ - channel = self.get_channel(instruction.ch) - - try: - pulse_name = instruction.label - except AttributeError: - sorted_params = sorted(instruction.parameters.items(), key=lambda x: x[0]) - base_str = f"{instruction.pulse_shape}_{str(sorted_params)}" - short_pulse_id = hashlib.md5(base_str.encode("utf-8")).hexdigest()[:4] - pulse_name = f"{instruction.pulse_shape}_{short_pulse_id}" - params = dict(instruction.parameters) - if "amp" in params and isinstance(params["amp"], complex): - params["angle"] = np.angle(params["amp"]) - params["amp"] = np.abs(params["amp"]) - pulse = ParametricPulseShapes.to_type(instruction.pulse_shape)(**params, name=pulse_name) - - yield instructions.Play(pulse, channel) - - def _convert_snapshot( - self, - instruction: PulseQobjInstruction, - ) -> Iterator[instructions.Instruction]: - """Return converted `Snapshot` instruction. - - Args: - instruction: Snapshot qobj instruction - - Yields: - Qiskit Pulse snapshot instructions - """ - yield instructions.Snapshot(instruction.label, instruction.type) - - def _convert_generic( - self, - instruction: PulseQobjInstruction, - ) -> Iterator[instructions.Instruction]: - """Convert generic pulse instruction. - - Args: - instruction: Generic qobj instruction - - Yields: - Qiskit Pulse generic instructions - - Raises: - QiskitError: When instruction name not found. - """ - if instruction.name in self._pulse_library: - waveform = library.Waveform( - samples=self._pulse_library[instruction.name], - name=instruction.name, - ) - channel = self.get_channel(instruction.ch) - - yield instructions.Play(waveform, channel) - else: - if qubits := getattr(instruction, "qubits", None): - msg = f"qubits {qubits}" - else: - msg = f"channel {instruction.ch}" - raise QiskitError( - f"Instruction {instruction.name} on {msg} is not found " - "in Qiskit namespace. This instruction cannot be deserialized." - ) diff --git a/qiskit/qobj/pulse_qobj.py b/qiskit/qobj/pulse_qobj.py deleted file mode 100644 index 1d2d33a1fa17..000000000000 --- a/qiskit/qobj/pulse_qobj.py +++ /dev/null @@ -1,709 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019. -# -# 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. - -# pylint: disable=invalid-name,redefined-builtin -# pylint: disable=super-init-not-called - -"""Module providing definitions of Pulse Qobj classes.""" - -import copy -import pprint -from typing import Union, List - -import numpy -from qiskit.qobj.common import QobjDictField -from qiskit.qobj.common import QobjHeader -from qiskit.qobj.common import QobjExperimentHeader -from qiskit.utils import deprecate_func - - -class QobjMeasurementOption: - """An individual measurement option.""" - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The `Qobj` class and related functionality are part of the deprecated " - "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " - "workflow requires `Qobj` it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - ) - def __init__(self, name, params=None): - """Instantiate a new QobjMeasurementOption object. - - Args: - name (str): The name of the measurement option - params (list): The parameters of the measurement option. - """ - self.name = name - if params is not None: - self.params = params - - def to_dict(self): - """Return a dict format representation of the QobjMeasurementOption. - - Returns: - dict: The dictionary form of the QasmMeasurementOption. - """ - out_dict = {"name": self.name} - if hasattr(self, "params"): - out_dict["params"] = self.params - return out_dict - - @classmethod - def from_dict(cls, data): - """Create a new QobjMeasurementOption object from a dictionary. - - Args: - data (dict): A dictionary for the experiment config - - Returns: - QobjMeasurementOption: The object from the input dictionary. - """ - name = data.pop("name") - return cls(name, **data) - - def __eq__(self, other): - if isinstance(other, QobjMeasurementOption): - if self.to_dict() == other.to_dict(): - return True - return False - - -class PulseQobjInstruction: - """A class representing a single instruction in an PulseQobj Experiment.""" - - _COMMON_ATTRS = [ - "ch", - "conditional", - "val", - "phase", - "frequency", - "duration", - "qubits", - "memory_slot", - "register_slot", - "label", - "type", - "pulse_shape", - "parameters", - ] - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The `Qobj` class and related functionality are part of the deprecated " - "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " - "workflow requires `Qobj` it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - ) - def __init__( - self, - name, - t0, - ch=None, - conditional=None, - val=None, - phase=None, - duration=None, - qubits=None, - memory_slot=None, - register_slot=None, - kernels=None, - discriminators=None, - label=None, - type=None, - pulse_shape=None, - parameters=None, - frequency=None, - ): - """Instantiate a new PulseQobjInstruction object. - - Args: - name (str): The name of the instruction - t0 (int): Pulse start time in integer **dt** units. - ch (str): The channel to apply the pulse instruction. - conditional (int): The register to use for a conditional for this - instruction - val (complex): Complex value to apply, bounded by an absolute value - of 1. - phase (float): if a ``fc`` instruction, the frame change phase in - radians. - frequency (float): if a ``sf`` instruction, the frequency in Hz. - duration (int): The duration of the pulse in **dt** units. - qubits (list): A list of ``int`` representing the qubits the - instruction operates on - memory_slot (list): If a ``measure`` instruction this is a list - of ``int`` containing the list of memory slots to store the - measurement results in (must be the same length as qubits). - If a ``bfunc`` instruction this is a single ``int`` of the - memory slot to store the boolean function result in. - register_slot (list): If a ``measure`` instruction this is a list - of ``int`` containing the list of register slots in which to - store the measurement results (must be the same length as - qubits). If a ``bfunc`` instruction this is a single ``int`` - of the register slot in which to store the result. - kernels (list): List of :class:`QobjMeasurementOption` objects - defining the measurement kernels and set of parameters if the - measurement level is 1 or 2. Only used for ``acquire`` - instructions. - discriminators (list): A list of :class:`QobjMeasurementOption` - used to set the discriminators to be used if the measurement - level is 2. Only used for ``acquire`` instructions. - label (str): Label of instruction - type (str): Type of instruction - pulse_shape (str): The shape of the parametric pulse - parameters (dict): The parameters for a parametric pulse - """ - self.name = name - self.t0 = t0 - if ch is not None: - self.ch = ch - if conditional is not None: - self.conditional = conditional - if val is not None: - self.val = val - if phase is not None: - self.phase = phase - if frequency is not None: - self.frequency = frequency - if duration is not None: - self.duration = duration - if qubits is not None: - self.qubits = qubits - if memory_slot is not None: - self.memory_slot = memory_slot - if register_slot is not None: - self.register_slot = register_slot - if kernels is not None: - self.kernels = kernels - if discriminators is not None: - self.discriminators = discriminators - if label is not None: - self.label = label - if type is not None: - self.type = type - if pulse_shape is not None: - self.pulse_shape = pulse_shape - if parameters is not None: - self.parameters = parameters - - def to_dict(self): - """Return a dictionary format representation of the Instruction. - - Returns: - dict: The dictionary form of the PulseQobjInstruction. - """ - out_dict = {"name": self.name, "t0": self.t0} - for attr in self._COMMON_ATTRS: - if hasattr(self, attr): - out_dict[attr] = getattr(self, attr) - if hasattr(self, "kernels"): - out_dict["kernels"] = [x.to_dict() for x in self.kernels] - if hasattr(self, "discriminators"): - out_dict["discriminators"] = [x.to_dict() for x in self.discriminators] - return out_dict - - def __repr__(self): - out = f'PulseQobjInstruction(name="{self.name}", t0={self.t0}' - for attr in self._COMMON_ATTRS: - attr_val = getattr(self, attr, None) - if attr_val is not None: - if isinstance(attr_val, str): - out += f', {attr}="{attr_val}"' - else: - out += f", {attr}={attr_val}" - out += ")" - return out - - def __str__(self): - out = f"Instruction: {self.name}\n" - out += f"\t\tt0: {self.t0}\n" - for attr in self._COMMON_ATTRS: - if hasattr(self, attr): - out += f"\t\t{attr}: {getattr(self, attr)}\n" - return out - - @classmethod - def from_dict(cls, data): - """Create a new PulseQobjExperimentConfig object from a dictionary. - - Args: - data (dict): A dictionary for the experiment config - - Returns: - PulseQobjInstruction: The object from the input dictionary. - """ - schema = { - "discriminators": QobjMeasurementOption, - "kernels": QobjMeasurementOption, - } - skip = ["t0", "name"] - - # Pulse instruction data is nested dictionary. - # To avoid deepcopy and avoid mutating the source object, create new dict here. - in_data = {} - for key, value in data.items(): - if key in skip: - continue - if key == "parameters": - # This is flat dictionary of parametric pulse parameters - formatted_value = value.copy() - if "amp" in formatted_value: - formatted_value["amp"] = _to_complex(formatted_value["amp"]) - in_data[key] = formatted_value - continue - if key in schema: - if isinstance(value, list): - in_data[key] = list(map(schema[key].from_dict, value)) - else: - in_data[key] = schema[key].from_dict(value) - else: - in_data[key] = value - - return cls(data["name"], data["t0"], **in_data) - - def __eq__(self, other): - if isinstance(other, PulseQobjInstruction): - if self.to_dict() == other.to_dict(): - return True - return False - - -def _to_complex(value: Union[List[float], complex]) -> complex: - """Convert the input value to type ``complex``. - Args: - value: Value to be converted. - Returns: - Input value in ``complex``. - Raises: - TypeError: If the input value is not in the expected format. - """ - if isinstance(value, list) and len(value) == 2: - return complex(value[0], value[1]) - elif isinstance(value, complex): - return value - - raise TypeError(f"{value} is not in a valid complex number format.") - - -class PulseQobjConfig(QobjDictField): - """A configuration for a Pulse Qobj.""" - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The `Qobj` class and related functionality are part of the deprecated " - "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " - "workflow requires `Qobj` it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - ) - def __init__( - self, - meas_level, - meas_return, - pulse_library, - qubit_lo_freq, - meas_lo_freq, - memory_slot_size=None, - rep_time=None, - rep_delay=None, - shots=None, - seed_simulator=None, - memory_slots=None, - **kwargs, - ): - """Instantiate a PulseQobjConfig object. - - Args: - meas_level (int): The measurement level to use. - meas_return (int): The level of measurement information to return. - pulse_library (list): A list of :class:`PulseLibraryItem` objects - which define the set of primitive pulses - qubit_lo_freq (list): List of frequencies (as floats) for the qubit - driver LO's in GHz. - meas_lo_freq (list): List of frequencies (as floats) for the' - measurement driver LO's in GHz. - memory_slot_size (int): Size of each memory slot if the output is - Level 0. - rep_time (int): Time per program execution in sec. Must be from the list provided - by the backend (``backend.configuration().rep_times``). Defaults to the first entry - in ``backend.configuration().rep_times``. - rep_delay (float): Delay between programs in sec. Only supported on certain - backends (``backend.configuration().dynamic_reprate_enabled`` ). If supported, - ``rep_delay`` will be used instead of ``rep_time`` and must be from the range - supplied by the backend (``backend.configuration().rep_delay_range``). Default is - ``backend.configuration().default_rep_delay``. - shots (int): The number of shots - seed_simulator (int): the seed to use in the simulator - memory_slots (list): The number of memory slots on the device - kwargs: Additional free form key value fields to add to the - configuration - """ - self.meas_level = meas_level - self.meas_return = meas_return - self.pulse_library = pulse_library - self.qubit_lo_freq = qubit_lo_freq - self.meas_lo_freq = meas_lo_freq - if memory_slot_size is not None: - self.memory_slot_size = memory_slot_size - if rep_time is not None: - self.rep_time = rep_time - if rep_delay is not None: - self.rep_delay = rep_delay - if shots is not None: - self.shots = int(shots) - - if seed_simulator is not None: - self.seed_simulator = int(seed_simulator) - - if memory_slots is not None: - self.memory_slots = int(memory_slots) - - if kwargs: - self.__dict__.update(kwargs) - - def to_dict(self): - """Return a dictionary format representation of the Pulse Qobj config. - - Returns: - dict: The dictionary form of the PulseQobjConfig. - """ - out_dict = copy.copy(self.__dict__) - if hasattr(self, "pulse_library"): - out_dict["pulse_library"] = [x.to_dict() for x in self.pulse_library] - - return out_dict - - @classmethod - def from_dict(cls, data): - """Create a new PulseQobjConfig object from a dictionary. - - Args: - data (dict): A dictionary for the config - - Returns: - PulseQobjConfig: The object from the input dictionary. - """ - if "pulse_library" in data: - pulse_lib = data.pop("pulse_library") - pulse_lib_obj = [PulseLibraryItem.from_dict(x) for x in pulse_lib] - data["pulse_library"] = pulse_lib_obj - return cls(**data) - - -class PulseQobjExperiment: - """A Pulse Qobj Experiment. - - Each instance of this class is used to represent an individual Pulse - experiment as part of a larger Pulse Qobj. - """ - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The `Qobj` class and related functionality are part of the deprecated " - "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " - "workflow requires `Qobj` it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - ) - def __init__(self, instructions, config=None, header=None): - """Instantiate a PulseQobjExperiment. - - Args: - config (PulseQobjExperimentConfig): A config object for the experiment - header (PulseQobjExperimentHeader): A header object for the experiment - instructions (list): A list of :class:`PulseQobjInstruction` objects - """ - if config is not None: - self.config = config - if header is not None: - self.header = header - self.instructions = instructions - - def to_dict(self): - """Return a dictionary format representation of the Experiment. - - Returns: - dict: The dictionary form of the PulseQobjExperiment. - """ - out_dict = {"instructions": [x.to_dict() for x in self.instructions]} - if hasattr(self, "config"): - out_dict["config"] = self.config.to_dict() - if hasattr(self, "header"): - out_dict["header"] = self.header.to_dict() - return out_dict - - def __repr__(self): - instructions_str = [repr(x) for x in self.instructions] - instructions_repr = "[" + ", ".join(instructions_str) + "]" - out = "PulseQobjExperiment(" - out += instructions_repr - if hasattr(self, "config") or hasattr(self, "header"): - out += ", " - if hasattr(self, "config"): - out += "config=" + str(repr(self.config)) + ", " - if hasattr(self, "header"): - out += "header=" + str(repr(self.header)) + ", " - out += ")" - return out - - def __str__(self): - out = "\nPulse Experiment:\n" - if hasattr(self, "config"): - config = pprint.pformat(self.config.to_dict()) - else: - config = "{}" - if hasattr(self, "header"): - header = pprint.pformat(self.header.to_dict() or {}) - else: - header = "{}" - out += f"Header:\n{header}\n" - out += f"Config:\n{config}\n\n" - for instruction in self.instructions: - out += f"\t{instruction}\n" - return out - - @classmethod - def from_dict(cls, data): - """Create a new PulseQobjExperiment object from a dictionary. - - Args: - data (dict): A dictionary for the experiment config - - Returns: - PulseQobjExperiment: The object from the input dictionary. - """ - config = None - if "config" in data: - config = PulseQobjExperimentConfig.from_dict(data.pop("config")) - header = None - if "header" in data: - header = QobjExperimentHeader.from_dict(data.pop("header")) - instructions = None - if "instructions" in data: - instructions = [ - PulseQobjInstruction.from_dict(inst) for inst in data.pop("instructions") - ] - return cls(instructions, config, header) - - def __eq__(self, other): - if isinstance(other, PulseQobjExperiment): - if self.to_dict() == other.to_dict(): - return True - return False - - -class PulseQobjExperimentConfig(QobjDictField): - """A config for a single Pulse experiment in the qobj.""" - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The `Qobj` class and related functionality are part of the deprecated " - "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " - "workflow requires `Qobj` it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - ) - def __init__(self, qubit_lo_freq=None, meas_lo_freq=None, **kwargs): - """Instantiate a PulseQobjExperimentConfig object. - - Args: - qubit_lo_freq (List[float]): List of qubit LO frequencies in GHz. - meas_lo_freq (List[float]): List of meas readout LO frequencies in GHz. - kwargs: Additional free form key value fields to add to the configuration - """ - if qubit_lo_freq is not None: - self.qubit_lo_freq = qubit_lo_freq - if meas_lo_freq is not None: - self.meas_lo_freq = meas_lo_freq - if kwargs: - self.__dict__.update(kwargs) - - -class PulseLibraryItem: - """An item in a pulse library.""" - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The `Qobj` class and related functionality are part of the deprecated " - "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " - "workflow requires `Qobj` it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - ) - def __init__(self, name, samples): - """Instantiate a pulse library item. - - Args: - name (str): A name for the pulse. - samples (list[complex]): A list of complex values defining pulse - shape. - """ - self.name = name - if isinstance(samples[0], list): - self.samples = numpy.array([complex(sample[0], sample[1]) for sample in samples]) - else: - self.samples = samples - - def to_dict(self): - """Return a dictionary format representation of the pulse library item. - - Returns: - dict: The dictionary form of the PulseLibraryItem. - """ - return {"name": self.name, "samples": self.samples} - - @classmethod - def from_dict(cls, data): - """Create a new PulseLibraryItem object from a dictionary. - - Args: - data (dict): A dictionary for the experiment config - - Returns: - PulseLibraryItem: The object from the input dictionary. - """ - return cls(**data) - - def __repr__(self): - return f"PulseLibraryItem({self.name}, {repr(self.samples)})" - - def __str__(self): - return f"Pulse Library Item:\n\tname: {self.name}\n\tsamples: {self.samples}" - - def __eq__(self, other): - if isinstance(other, PulseLibraryItem): - if self.to_dict() == other.to_dict(): - return True - return False - - -class PulseQobj: - """A Pulse Qobj.""" - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The `Qobj` class and related functionality are part of the deprecated " - "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " - "workflow requires `Qobj` it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - ) - def __init__(self, qobj_id, config, experiments, header=None): - """Instantiate a new Pulse Qobj Object. - - Each Pulse Qobj object is used to represent a single payload that will - be passed to a Qiskit provider. It mirrors the Qobj the published - `Qobj specification `_ for Pulse - experiments. - - Args: - qobj_id (str): An identifier for the qobj - config (PulseQobjConfig): A config for the entire run - header (QobjHeader): A header for the entire run - experiments (list): A list of lists of :class:`PulseQobjExperiment` - objects representing an experiment - """ - self.qobj_id = qobj_id - self.config = config - self.header = header or QobjHeader() - self.experiments = experiments - self.type = "PULSE" - self.schema_version = "1.2.0" - - def __repr__(self): - experiments_str = [repr(x) for x in self.experiments] - experiments_repr = "[" + ", ".join(experiments_str) + "]" - return ( - f"PulseQobj(qobj_id='{self.qobj_id}', config={repr(self.config)}, " - f"experiments={experiments_repr}, header={repr(self.header)})" - ) - - def __str__(self): - out = f"Pulse Qobj: {self.qobj_id}:\n" - config = pprint.pformat(self.config.to_dict()) - out += f"Config: {str(config)}\n" - header = pprint.pformat(self.header.to_dict()) - out += f"Header: {str(header)}\n" - out += "Experiments:\n" - for experiment in self.experiments: - out += str(experiment) - return out - - def to_dict(self): - """Return a dictionary format representation of the Pulse Qobj. - - Note this dict is not in the json wire format expected by IBMQ and qobj - specification because complex numbers are still of type complex. Also - this may contain native numpy arrays. When serializing this output - for use with IBMQ you can leverage a json encoder that converts these - as expected. For example: - - .. code-block:: python - - import json - import numpy - - class QobjEncoder(json.JSONEncoder): - def default(self, obj): - if isinstance(obj, numpy.ndarray): - return obj.tolist() - if isinstance(obj, complex): - return (obj.real, obj.imag) - return json.JSONEncoder.default(self, obj) - - json.dumps(qobj.to_dict(), cls=QobjEncoder) - - Returns: - dict: A dictionary representation of the PulseQobj object - """ - out_dict = { - "qobj_id": self.qobj_id, - "header": self.header.to_dict(), - "config": self.config.to_dict(), - "schema_version": self.schema_version, - "type": self.type, - "experiments": [x.to_dict() for x in self.experiments], - } - return out_dict - - @classmethod - def from_dict(cls, data): - """Create a new PulseQobj object from a dictionary. - - Args: - data (dict): A dictionary representing the PulseQobj to create. It - will be in the same format as output by :func:`to_dict`. - - Returns: - PulseQobj: The PulseQobj from the input dictionary. - """ - config = None - if "config" in data: - config = PulseQobjConfig.from_dict(data["config"]) - experiments = None - if "experiments" in data: - experiments = [PulseQobjExperiment.from_dict(exp) for exp in data["experiments"]] - header = None - if "header" in data: - header = QobjHeader.from_dict(data["header"]) - - return cls( - qobj_id=data.get("qobj_id"), config=config, experiments=experiments, header=header - ) - - def __eq__(self, other): - if isinstance(other, PulseQobj): - if self.to_dict() == other.to_dict(): - return True - return False diff --git a/qiskit/qobj/qasm_qobj.py b/qiskit/qobj/qasm_qobj.py deleted file mode 100644 index eef2bb1315c4..000000000000 --- a/qiskit/qobj/qasm_qobj.py +++ /dev/null @@ -1,708 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019. -# -# 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. - -"""Module providing definitions of OpenQASM 2 Qobj classes.""" - -import copy -import pprint -from types import SimpleNamespace -from qiskit.circuit.parameterexpression import ParameterExpression -from qiskit.qobj.pulse_qobj import PulseQobjInstruction, PulseLibraryItem -from qiskit.qobj.common import QobjDictField, QobjHeader -from qiskit.utils import deprecate_func - - -class QasmQobjInstruction: - """A class representing a single instruction in an QasmQobj Experiment.""" - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The `Qobj` class and related functionality are part of the deprecated " - "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " - "workflow requires `Qobj` it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - ) - def __init__( - self, - name, - params=None, - qubits=None, - register=None, - memory=None, - condition=None, - conditional=None, - label=None, - mask=None, - relation=None, - val=None, - snapshot_type=None, - ): - """Instantiate a new QasmQobjInstruction object. - - Args: - name (str): The name of the instruction - params (list): The list of parameters for the gate - qubits (list): A list of ``int`` representing the qubits the - instruction operates on - register (list): If a ``measure`` instruction this is a list - of ``int`` containing the list of register slots in which to - store the measurement results (must be the same length as - qubits). If a ``bfunc`` instruction this is a single ``int`` - of the register slot in which to store the result. - memory (list): If a ``measure`` instruction this is a list - of ``int`` containing the list of memory slots to store the - measurement results in (must be the same length as qubits). - If a ``bfunc`` instruction this is a single ``int`` of the - memory slot to store the boolean function result in. - condition (tuple): A tuple of the form ``(int, int)`` where the - first ``int`` is the control register and the second ``int`` is - the control value if the gate has a condition. - conditional (int): The register index of the condition - label (str): An optional label assigned to the instruction - mask (int): For a ``bfunc`` instruction the hex value which is - applied as an ``AND`` to the register bits. - relation (str): Relational operator for comparing the masked - register to the ``val`` kwarg. Can be either ``==`` (equals) or - ``!=`` (not equals). - val (int): Value to which to compare the masked register. In other - words, the output of the function is ``(register AND mask)`` - snapshot_type (str): For snapshot instructions the type of snapshot - to use - """ - self.name = name - if params is not None: - self.params = params - if qubits is not None: - self.qubits = qubits - if register is not None: - self.register = register - if memory is not None: - self.memory = memory - if condition is not None: - self._condition = condition - if conditional is not None: - self.conditional = conditional - if label is not None: - self.label = label - if mask is not None: - self.mask = mask - if relation is not None: - self.relation = relation - if val is not None: - self.val = val - if snapshot_type is not None: - self.snapshot_type = snapshot_type - - def to_dict(self): - """Return a dictionary format representation of the Instruction. - - Returns: - dict: The dictionary form of the QasmQobjInstruction. - """ - out_dict = {"name": self.name} - for attr in [ - "params", - "qubits", - "register", - "memory", - "_condition", - "conditional", - "label", - "mask", - "relation", - "val", - "snapshot_type", - ]: - if hasattr(self, attr): - # TODO: Remove the param type conversion when Aer understands - # ParameterExpression type - if attr == "params": - params = [] - for param in list(getattr(self, attr)): - if isinstance(param, ParameterExpression): - params.append(float(param)) - else: - params.append(param) - out_dict[attr] = params - else: - out_dict[attr] = getattr(self, attr) - - return out_dict - - def __repr__(self): - out = f"QasmQobjInstruction(name='{self.name}'" - for attr in [ - "params", - "qubits", - "register", - "memory", - "_condition", - "conditional", - "label", - "mask", - "relation", - "val", - "snapshot_type", - ]: - attr_val = getattr(self, attr, None) - if attr_val is not None: - if isinstance(attr_val, str): - out += f', {attr}="{attr_val}"' - else: - out += f", {attr}={attr_val}" - out += ")" - return out - - def __str__(self): - out = f"Instruction: {self.name}\n" - for attr in [ - "params", - "qubits", - "register", - "memory", - "_condition", - "conditional", - "label", - "mask", - "relation", - "val", - "snapshot_type", - ]: - if hasattr(self, attr): - out += f"\t\t{attr}: {getattr(self, attr)}\n" - return out - - @classmethod - def from_dict(cls, data): - """Create a new QasmQobjInstruction object from a dictionary. - - Args: - data (dict): A dictionary for the experiment config - - Returns: - QasmQobjInstruction: The object from the input dictionary. - """ - name = data.pop("name") - return cls(name, **data) - - def __eq__(self, other): - if isinstance(other, QasmQobjInstruction): - if self.to_dict() == other.to_dict(): - return True - return False - - -class QasmQobjExperiment: - """An OpenQASM 2 Qobj Experiment. - - Each instance of this class is used to represent an OpenQASM 2 experiment as - part of a larger OpenQASM 2 qobj. - """ - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The `Qobj` class and related functionality are part of the deprecated " - "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " - "workflow requires `Qobj` it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - ) - def __init__(self, config=None, header=None, instructions=None): - """Instantiate a QasmQobjExperiment. - - Args: - config (QasmQobjExperimentConfig): A config object for the experiment - header (QasmQobjExperimentHeader): A header object for the experiment - instructions (list): A list of :class:`QasmQobjInstruction` objects - """ - self.config = config or QasmQobjExperimentConfig() - self.header = header or QasmQobjExperimentHeader() - self.instructions = instructions or [] - - def __repr__(self): - instructions_str = [repr(x) for x in self.instructions] - instructions_repr = "[" + ", ".join(instructions_str) + "]" - return ( - f"QasmQobjExperiment(config={repr(self.config)}, header={repr(self.header)}," - f" instructions={instructions_repr})" - ) - - def __str__(self): - out = "\nOpenQASM2 Experiment:\n" - config = pprint.pformat(self.config.to_dict()) - header = pprint.pformat(self.header.to_dict()) - out += f"Header:\n{header}\n" - out += f"Config:\n{config}\n\n" - for instruction in self.instructions: - out += f"\t{instruction}\n" - return out - - def to_dict(self): - """Return a dictionary format representation of the Experiment. - - Returns: - dict: The dictionary form of the QasmQObjExperiment. - """ - out_dict = { - "config": self.config.to_dict(), - "header": self.header.to_dict(), - "instructions": [x.to_dict() for x in self.instructions], - } - return out_dict - - @classmethod - def from_dict(cls, data): - """Create a new QasmQobjExperiment object from a dictionary. - - Args: - data (dict): A dictionary for the experiment config - - Returns: - QasmQobjExperiment: The object from the input dictionary. - """ - config = None - if "config" in data: - config = QasmQobjExperimentConfig.from_dict(data.pop("config")) - header = None - if "header" in data: - header = QasmQobjExperimentHeader.from_dict(data.pop("header")) - instructions = None - if "instructions" in data: - instructions = [ - QasmQobjInstruction.from_dict(inst) for inst in data.pop("instructions") - ] - return cls(config, header, instructions) - - def __eq__(self, other): - if isinstance(other, QasmQobjExperiment): - if self.to_dict() == other.to_dict(): - return True - return False - - -class QasmQobjConfig(SimpleNamespace): - """A configuration for an OpenQASM 2 Qobj.""" - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The `Qobj` class and related functionality are part of the deprecated " - "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " - "workflow requires `Qobj` it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - ) - def __init__( - self, - shots=None, - seed_simulator=None, - memory=None, - parameter_binds=None, - meas_level=None, - meas_return=None, - memory_slots=None, - n_qubits=None, - pulse_library=None, - calibrations=None, - rep_delay=None, - qubit_lo_freq=None, - meas_lo_freq=None, - **kwargs, - ): - """Model for RunConfig. - - Args: - shots (int): the number of shots. - seed_simulator (int): the seed to use in the simulator - memory (bool): whether to request memory from backend (per-shot readouts) - parameter_binds (list[dict]): List of parameter bindings - meas_level (int): Measurement level 0, 1, or 2 - meas_return (str): For measurement level < 2, whether single or avg shots are returned - memory_slots (int): The number of memory slots on the device - n_qubits (int): The number of qubits on the device - pulse_library (list): List of :class:`PulseLibraryItem`. - calibrations (QasmExperimentCalibrations): Information required for Pulse gates. - rep_delay (float): Delay between programs in sec. Only supported on certain - backends (``backend.configuration().dynamic_reprate_enabled`` ). Must be from the - range supplied by the backend (``backend.configuration().rep_delay_range``). Default - is ``backend.configuration().default_rep_delay``. - qubit_lo_freq (list): List of frequencies (as floats) for the qubit driver LO's in GHz. - meas_lo_freq (list): List of frequencies (as floats) for the measurement driver LO's in - GHz. - kwargs: Additional free form key value fields to add to the - configuration. - """ - if shots is not None: - self.shots = int(shots) - - if seed_simulator is not None: - self.seed_simulator = int(seed_simulator) - - if memory is not None: - self.memory = bool(memory) - - if parameter_binds is not None: - self.parameter_binds = parameter_binds - - if meas_level is not None: - self.meas_level = meas_level - - if meas_return is not None: - self.meas_return = meas_return - - if memory_slots is not None: - self.memory_slots = memory_slots - - if n_qubits is not None: - self.n_qubits = n_qubits - - if pulse_library is not None: - self.pulse_library = pulse_library - - if calibrations is not None: - self.calibrations = calibrations - - if rep_delay is not None: - self.rep_delay = rep_delay - - if qubit_lo_freq is not None: - self.qubit_lo_freq = qubit_lo_freq - - if meas_lo_freq is not None: - self.meas_lo_freq = meas_lo_freq - - if kwargs: - self.__dict__.update(kwargs) - - def to_dict(self): - """Return a dictionary format representation of the OpenQASM 2 Qobj config. - - Returns: - dict: The dictionary form of the QasmQobjConfig. - """ - out_dict = copy.copy(self.__dict__) - if hasattr(self, "pulse_library"): - out_dict["pulse_library"] = [x.to_dict() for x in self.pulse_library] - - if hasattr(self, "calibrations"): - out_dict["calibrations"] = self.calibrations.to_dict() - - return out_dict - - @classmethod - def from_dict(cls, data): - """Create a new QasmQobjConfig object from a dictionary. - - Args: - data (dict): A dictionary for the config - - Returns: - QasmQobjConfig: The object from the input dictionary. - """ - if "pulse_library" in data: - pulse_lib = data.pop("pulse_library") - pulse_lib_obj = [PulseLibraryItem.from_dict(x) for x in pulse_lib] - data["pulse_library"] = pulse_lib_obj - - if "calibrations" in data: - calibrations = data.pop("calibrations") - data["calibrations"] = QasmExperimentCalibrations.from_dict(calibrations) - - return cls(**data) - - def __eq__(self, other): - if isinstance(other, QasmQobjConfig): - if self.to_dict() == other.to_dict(): - return True - return False - - -class QasmQobjExperimentHeader(QobjDictField): - """A header for a single OpenQASM 2 experiment in the qobj.""" - - pass - - -class QasmQobjExperimentConfig(QobjDictField): - """Configuration for a single OpenQASM 2 experiment in the qobj.""" - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The `Qobj` class and related functionality are part of the deprecated " - "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " - "workflow requires `Qobj` it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - ) - def __init__(self, calibrations=None, qubit_lo_freq=None, meas_lo_freq=None, **kwargs): - """ - Args: - calibrations (QasmExperimentCalibrations): Information required for Pulse gates. - qubit_lo_freq (List[float]): List of qubit LO frequencies in GHz. - meas_lo_freq (List[float]): List of meas readout LO frequencies in GHz. - kwargs: Additional free form key value fields to add to the configuration - """ - if calibrations: - self.calibrations = calibrations - if qubit_lo_freq is not None: - self.qubit_lo_freq = qubit_lo_freq - if meas_lo_freq is not None: - self.meas_lo_freq = meas_lo_freq - - super().__init__(**kwargs) - - def to_dict(self): - out_dict = copy.copy(self.__dict__) - if hasattr(self, "calibrations"): - out_dict["calibrations"] = self.calibrations.to_dict() - return out_dict - - @classmethod - def from_dict(cls, data): - if "calibrations" in data: - calibrations = data.pop("calibrations") - data["calibrations"] = QasmExperimentCalibrations.from_dict(calibrations) - return cls(**data) - - -class QasmExperimentCalibrations: - """A container for any calibrations data. The gates attribute contains a list of - GateCalibrations. - """ - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The `Qobj` class and related functionality are part of the deprecated " - "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " - "workflow requires `Qobj` it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - ) - def __init__(self, gates): - """ - Initialize a container for calibrations. - - Args: - gates (list(GateCalibration)) - """ - self.gates = gates - - def to_dict(self): - """Return a dictionary format representation of the calibrations. - - Returns: - dict: The dictionary form of the GateCalibration. - - """ - out_dict = copy.copy(self.__dict__) - out_dict["gates"] = [x.to_dict() for x in self.gates] - return out_dict - - @classmethod - def from_dict(cls, data): - """Create a new GateCalibration object from a dictionary. - - Args: - data (dict): A dictionary representing the QasmExperimentCalibrations to - create. It will be in the same format as output by :func:`to_dict`. - - Returns: - QasmExperimentCalibrations: The QasmExperimentCalibrations from the input dictionary. - """ - gates = data.pop("gates") - data["gates"] = [GateCalibration.from_dict(x) for x in gates] - return cls(**data) - - -class GateCalibration: - """Each calibration specifies a unique gate by name, qubits and params, and - contains the Pulse instructions to implement it.""" - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The `Qobj` class and related functionality are part of the deprecated " - "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " - "workflow requires `Qobj` it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - ) - def __init__(self, name, qubits, params, instructions): - """ - Initialize a single gate calibration. Instructions may reference waveforms which should be - made available in the pulse_library. - - Args: - name (str): Gate name. - qubits (list(int)): Qubits the gate applies to. - params (list(complex)): Gate parameter values, if any. - instructions (list(PulseQobjInstruction)): The gate implementation. - """ - self.name = name - self.qubits = qubits - self.params = params - self.instructions = instructions - - def __hash__(self): - return hash( - ( - self.name, - tuple(self.qubits), - tuple(self.params), - tuple(str(inst) for inst in self.instructions), - ) - ) - - def to_dict(self): - """Return a dictionary format representation of the Gate Calibration. - - Returns: - dict: The dictionary form of the GateCalibration. - """ - out_dict = copy.copy(self.__dict__) - out_dict["instructions"] = [x.to_dict() for x in self.instructions] - return out_dict - - @classmethod - def from_dict(cls, data): - """Create a new GateCalibration object from a dictionary. - - Args: - data (dict): A dictionary representing the GateCalibration to create. It - will be in the same format as output by :func:`to_dict`. - - Returns: - GateCalibration: The GateCalibration from the input dictionary. - """ - instructions = data.pop("instructions") - data["instructions"] = [PulseQobjInstruction.from_dict(x) for x in instructions] - return cls(**data) - - -class QasmQobj: - """An OpenQASM 2 Qobj.""" - - @deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The `Qobj` class and related functionality are part of the deprecated " - "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " - "workflow requires `Qobj` it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", - ) - def __init__(self, qobj_id=None, config=None, experiments=None, header=None): - """Instantiate a new OpenQASM 2 Qobj Object. - - Each OpenQASM 2 Qobj object is used to represent a single payload that will - be passed to a Qiskit provider. It mirrors the Qobj the published - `Qobj specification `_ for OpenQASM - experiments. - - Args: - qobj_id (str): An identifier for the qobj - config (QasmQobjRunConfig): A config for the entire run - header (QobjHeader): A header for the entire run - experiments (list): A list of lists of :class:`QasmQobjExperiment` - objects representing an experiment - """ - self.header = header or QobjHeader() - self.config = config or QasmQobjConfig() - self.experiments = experiments or [] - self.qobj_id = qobj_id - self.type = "QASM" - self.schema_version = "1.3.0" - - def __repr__(self): - experiments_str = [repr(x) for x in self.experiments] - experiments_repr = "[" + ", ".join(experiments_str) + "]" - return ( - f"QasmQobj(qobj_id='{self.qobj_id}', config={repr(self.config)}," - f" experiments={experiments_repr}, header={repr(self.header)})" - ) - - def __str__(self): - out = f"QASM Qobj: {self.qobj_id}:\n" - config = pprint.pformat(self.config.to_dict()) - out += f"Config: {str(config)}\n" - header = pprint.pformat(self.header.to_dict()) - out += f"Header: {str(header)}\n" - out += "Experiments:\n" - for experiment in self.experiments: - out += str(experiment) - return out - - def to_dict(self): - """Return a dictionary format representation of the OpenQASM 2 Qobj. - - Note this dict is not in the json wire format expected by IBM and Qobj - specification because complex numbers are still of type complex. Also, - this may contain native numpy arrays. When serializing this output - for use with IBM systems, you can leverage a json encoder that converts these - as expected. For example: - - .. code-block:: python - - import json - import numpy - - class QobjEncoder(json.JSONEncoder): - def default(self, obj): - if isinstance(obj, numpy.ndarray): - return obj.tolist() - if isinstance(obj, complex): - return (obj.real, obj.imag) - return json.JSONEncoder.default(self, obj) - - json.dumps(qobj.to_dict(), cls=QobjEncoder) - - Returns: - dict: A dictionary representation of the QasmQobj object - """ - out_dict = { - "qobj_id": self.qobj_id, - "header": self.header.to_dict(), - "config": self.config.to_dict(), - "schema_version": self.schema_version, - "type": "QASM", - "experiments": [x.to_dict() for x in self.experiments], - } - return out_dict - - @classmethod - def from_dict(cls, data): - """Create a new QASMQobj object from a dictionary. - - Args: - data (dict): A dictionary representing the QasmQobj to create. It - will be in the same format as output by :func:`to_dict`. - - Returns: - QasmQobj: The QasmQobj from the input dictionary. - """ - config = None - if "config" in data: - config = QasmQobjConfig.from_dict(data["config"]) - experiments = None - if "experiments" in data: - experiments = [QasmQobjExperiment.from_dict(exp) for exp in data["experiments"]] - header = None - if "header" in data: - header = QobjHeader.from_dict(data["header"]) - - return cls( - qobj_id=data.get("qobj_id"), config=config, experiments=experiments, header=header - ) - - def __eq__(self, other): - if isinstance(other, QasmQobj): - if self.to_dict() == other.to_dict(): - return True - return False diff --git a/qiskit/qobj/utils.py b/qiskit/qobj/utils.py deleted file mode 100644 index 1e691d6324bf..000000000000 --- a/qiskit/qobj/utils.py +++ /dev/null @@ -1,46 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# 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. - -"""Qobj utilities and enums.""" - -from enum import Enum, IntEnum - -from qiskit.utils import deprecate_func - - -@deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="The `Qobj` class and related functionality are part of the deprecated `BackendV1` " - "workflow, and no longer necessary for `BackendV2`. If a user workflow requires `Qobj` it likely " - "relies on deprecated functionality and should be updated to use `BackendV2`.", -) -class QobjType(str, Enum): - """Qobj.type allowed values.""" - - QASM = "QASM" - PULSE = "PULSE" - - -class MeasReturnType(str, Enum): - """PulseQobjConfig meas_return allowed values.""" - - AVERAGE = "avg" - SINGLE = "single" - - -class MeasLevel(IntEnum): - """MeasLevel allowed values.""" - - RAW = 0 - KERNELED = 1 - CLASSIFIED = 2 diff --git a/qiskit/result/__init__.py b/qiskit/result/__init__.py index 979225f9a72d..adc61748d4b5 100644 --- a/qiskit/result/__init__.py +++ b/qiskit/result/__init__.py @@ -47,14 +47,15 @@ ================== .. autofunction:: sampled_expectation_value + """ -from .result import Result +from .counts import Counts +from .distributions import QuasiDistribution, ProbDistribution from .exceptions import ResultError +from .models import MeasLevel, MeasReturnType +from .result import Result +from .sampled_expval import sampled_expectation_value from .utils import marginal_counts from .utils import marginal_distribution from .utils import marginal_memory -from .counts import Counts - -from .distributions import QuasiDistribution, ProbDistribution -from .sampled_expval import sampled_expectation_value diff --git a/qiskit/result/models.py b/qiskit/result/models.py index 93a1954ac7f4..71fda895952d 100644 --- a/qiskit/result/models.py +++ b/qiskit/result/models.py @@ -13,13 +13,26 @@ """Schema and helper models for schema-conformant Results.""" import copy -import warnings +from enum import Enum, IntEnum -from qiskit.qobj.utils import MeasReturnType, MeasLevel -from qiskit.qobj import QobjExperimentHeader from qiskit.exceptions import QiskitError +class MeasReturnType(str, Enum): + """meas_return allowed values defined by legacy PulseQobjConfig object but still used by Result.""" + + AVERAGE = "avg" + SINGLE = "single" + + +class MeasLevel(IntEnum): + """MeasLevel allowed values defined by legacy PulseQobjConfig object but still used by Result.""" + + RAW = 0 + KERNELED = 1 + CLASSIFIED = 2 + + class ExperimentResultData: """Class representing experiment result data""" @@ -132,7 +145,7 @@ def __init__( status (str): The status of the experiment seed (int): The seed used for simulation (if run on a simulator) meas_return (str): The type of measurement returned - header (qiskit.qobj.QobjExperimentHeader): A free form dictionary + header (dict): A free form dictionary header for the experiment kwargs: Arbitrary extra fields @@ -197,7 +210,7 @@ def to_dict(self): "meas_level": self.meas_level, } if hasattr(self, "header"): - out_dict["header"] = self.header.to_dict() + out_dict["header"] = self.header if hasattr(self, "status"): out_dict["status"] = self.status if hasattr(self, "seed"): @@ -223,11 +236,6 @@ def from_dict(cls, data): in_data = copy.copy(data) data_obj = ExperimentResultData.from_dict(in_data.pop("data")) - if "header" in in_data: - with warnings.catch_warnings(): - # The class QobjExperimentHeader is deprecated - warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") - in_data["header"] = QobjExperimentHeader.from_dict(in_data.pop("header")) shots = in_data.pop("shots") success = in_data.pop("success") diff --git a/qiskit/result/result.py b/qiskit/result/result.py index 4a5a928f06d5..2d1677d9e2fc 100644 --- a/qiskit/result/result.py +++ b/qiskit/result/result.py @@ -19,11 +19,9 @@ from qiskit.pulse.schedule import Schedule from qiskit.exceptions import QiskitError from qiskit.quantum_info.states import statevector -from qiskit.result.models import ExperimentResult +from qiskit.result.models import ExperimentResult, MeasLevel from qiskit.result import postprocess from qiskit.result.counts import Counts -from qiskit.qobj.utils import MeasLevel -from qiskit.qobj import QobjHeader class Result: @@ -32,24 +30,23 @@ class Result: Attributes: backend_name (str): backend name. backend_version (str): backend version, in the form X.Y.Z. - qobj_id (str): user-generated Qobj id. job_id (str): unique execution id from the backend. - success (bool): True if complete input qobj executed correctly. (Implies + success (bool): True if complete input executed correctly. (Implies each experiment success) results (list[ExperimentResult]): corresponding results for array of - experiments of the input qobj + experiments of the input """ _metadata = {} def __init__( self, - backend_name, - backend_version, - qobj_id, - job_id, - success, - results, + *, + backend_name=None, + backend_version=None, + job_id=None, + success=None, + results=None, date=None, status=None, header=None, @@ -58,7 +55,6 @@ def __init__( self._metadata = {} self.backend_name = backend_name self.backend_version = backend_version - self.qobj_id = qobj_id self.job_id = job_id self.success = success self.results = results @@ -70,7 +66,7 @@ def __init__( def __repr__(self): out = ( f"Result(backend_name='{self.backend_name}', backend_version='{self.backend_version}'," - f" qobj_id='{self.qobj_id}', job_id='{self.job_id}', success={self.success}," + f" job_id='{self.job_id}', success={self.success}," f" results={self.results}" ) out += f", date={self.date}, status={self.status}, header={self.header}" @@ -94,7 +90,6 @@ def to_dict(self): "backend_version": self.backend_version, "date": self.date, "header": None if self.header is None else self.header.to_dict(), - "qobj_id": self.qobj_id, "job_id": self.job_id, "status": self.status, "success": self.success, @@ -124,10 +119,6 @@ def from_dict(cls, data): in_data = copy.copy(data) in_data["results"] = [ExperimentResult.from_dict(x) for x in in_data.pop("results")] - if in_data.get("header") is not None: - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") - in_data["header"] = QobjHeader.from_dict(in_data.pop("header")) return cls(**in_data) def data(self, experiment=None): @@ -212,7 +203,7 @@ def get_memory(self, experiment=None): exp_result = self._get_experiment(experiment) try: try: # header is not available - header = exp_result.header.to_dict() + header = exp_result.header except (AttributeError, QiskitError): header = None @@ -263,7 +254,7 @@ def get_counts(self, experiment=None): for key in exp_keys: exp = self._get_experiment(key) try: - header = exp.header.to_dict() + header = exp.header except (AttributeError, QiskitError): # header is not available header = None @@ -364,11 +355,12 @@ def _get_experiment(self, key=None): except IndexError as ex: raise QiskitError(f'Result for experiment "{key}" could not be found.') from ex else: - # Look into `result[x].header.name` for the names. + # Look into `result[x].header["name"]` for the names. exp = [ result for result in self.results - if getattr(getattr(result, "header", None), "name", "") == key + if getattr(result, "header", None) is not None + and getattr(result, "header").get("name", "") == key ] if len(exp) == 0: diff --git a/qiskit/result/utils.py b/qiskit/result/utils.py index b531b1bc65b5..ed1dfeeb3dc4 100644 --- a/qiskit/result/utils.py +++ b/qiskit/result/utils.py @@ -24,7 +24,6 @@ from qiskit.result.counts import Counts from qiskit.result.distributions.probability import ProbDistribution from qiskit.result.distributions.quasi import QuasiDistribution - from qiskit.result.postprocess import _bin_to_hex from qiskit._accelerate import results as results_rs # pylint: disable=no-name-in-module @@ -74,10 +73,10 @@ def marginal_counts( experiment_result.data.counts = new_counts_hex if indices is not None: - experiment_result.header.memory_slots = len(indices) - csize = getattr(experiment_result.header, "creg_sizes", None) + experiment_result.header["memory_slots"] = len(indices) + csize = experiment_result.header.get("creg_sizes", None) if csize is not None: - experiment_result.header.creg_sizes = _adjust_creg_sizes(csize, indices) + experiment_result.header["creg_sizes"] = _adjust_creg_sizes(csize, indices) if getattr(experiment_result.data, "memory", None) is not None and indices is not None: if marginalize_memory is False: diff --git a/qiskit/transpiler/passes/layout/vf2_post_layout.py b/qiskit/transpiler/passes/layout/vf2_post_layout.py index f6d5f538e139..e85608c2e3da 100644 --- a/qiskit/transpiler/passes/layout/vf2_post_layout.py +++ b/qiskit/transpiler/passes/layout/vf2_post_layout.py @@ -23,7 +23,6 @@ from qiskit.transpiler.layout import Layout from qiskit.transpiler.basepasses import AnalysisPass from qiskit.transpiler.exceptions import TranspilerError -from qiskit.providers.exceptions import BackendPropertyError from qiskit.transpiler.passes.layout import vf2_utils @@ -374,26 +373,4 @@ def _score_layout(self, layout, bit_map, reverse_bit_map, im_graph): props = self.target[gate][qargs] if props is not None and props.error is not None: fidelity *= (1 - props.error) ** count - else: - for bit, node_index in bit_map.items(): - gate_counts = im_graph[node_index] - for gate, count in gate_counts.items(): - if gate == "measure": - try: - fidelity *= (1 - self.properties.readout_error(bits[bit])) ** count - except BackendPropertyError: - pass - else: - try: - fidelity *= (1 - self.properties.gate_error(gate, bits[bit])) ** count - except BackendPropertyError: - pass - for edge in im_graph.edge_index_map().values(): - qargs = (bits[reverse_bit_map[edge[0]]], bits[reverse_bit_map[edge[1]]]) - gate_counts = edge[2] - for gate, count in gate_counts.items(): - try: - fidelity *= (1 - self.properties.gate_error(gate, qargs)) ** count - except BackendPropertyError: - pass return 1 - fidelity diff --git a/qiskit/transpiler/passmanager_config.py b/qiskit/transpiler/passmanager_config.py index 52c43a24cb9d..c8744e6c306c 100644 --- a/qiskit/transpiler/passmanager_config.py +++ b/qiskit/transpiler/passmanager_config.py @@ -12,11 +12,6 @@ """Pass Manager Configuration class.""" -import warnings - -from qiskit.transpiler.coupling import CouplingMap -from qiskit.transpiler.instruction_durations import InstructionDurations - class PassManagerConfig: """Pass Manager Configuration.""" @@ -108,14 +103,8 @@ def from_backend(cls, backend, _skip_target=False, **pass_manager_options): This method automatically generates a PassManagerConfig object based on the backend's features. User options can be used to overwrite the configuration. - .. deprecated:: 1.3 - The method ``PassManagerConfig.from_backend`` will stop supporting inputs of type - :class:`.BackendV1` in the `backend` parameter in a future release no - earlier than 2.0. :class:`.BackendV1` is deprecated and implementations should move - to :class:`.BackendV2`. - Args: - backend (BackendV1 or BackendV2): The backend that provides the configuration. + backend (BackendV2): The backend that provides the configuration. pass_manager_options: User-defined option-value pairs. Returns: @@ -124,41 +113,15 @@ def from_backend(cls, backend, _skip_target=False, **pass_manager_options): Raises: AttributeError: If the backend does not support a `configuration()` method. """ - backend_version = getattr(backend, "version", 0) - if backend_version == 1: - warnings.warn( - "The method PassManagerConfig.from_backend will stop supporting inputs of " - f"type `BackendV1` ( {backend} ) in the `backend` parameter in a future " - "release no earlier than 2.0. `BackendV1` is deprecated and implementations " - "should move to `BackendV2`.", - category=DeprecationWarning, - stacklevel=2, - ) - if not isinstance(backend_version, int): - backend_version = 0 - if backend_version < 2: - config = backend.configuration() res = cls(**pass_manager_options) if res.basis_gates is None: - if backend_version < 2: - res.basis_gates = getattr(config, "basis_gates", None) - else: - res.basis_gates = backend.operation_names + res.basis_gates = backend.operation_names if res.coupling_map is None: - if backend_version < 2: - cmap_edge_list = getattr(config, "coupling_map", None) - if cmap_edge_list is not None: - res.coupling_map = CouplingMap(cmap_edge_list) - else: - res.coupling_map = backend.coupling_map + res.coupling_map = backend.coupling_map if res.instruction_durations is None: - if backend_version < 2: - res.instruction_durations = InstructionDurations.from_backend(backend) - else: - res.instruction_durations = backend.instruction_durations + res.instruction_durations = backend.instruction_durations if res.target is None and not _skip_target: - if backend_version >= 2: - res.target = backend.target + res.target = backend.target if res.scheduling_method is None and hasattr(backend, "get_scheduling_stage_plugin"): res.scheduling_method = backend.get_scheduling_stage_plugin() if res.translation_method is None and hasattr(backend, "get_translation_stage_plugin"): diff --git a/qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py b/qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py index 38a9ae26f67a..e54e11900a94 100644 --- a/qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py +++ b/qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py @@ -20,7 +20,6 @@ from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping from qiskit.circuit.quantumregister import Qubit from qiskit.providers.backend import Backend -from qiskit.providers.backend_compat import BackendV2Converter from qiskit.transpiler.coupling import CouplingMap from qiskit.transpiler.exceptions import TranspilerError from qiskit.transpiler.instruction_durations import InstructionDurations @@ -88,28 +87,27 @@ def generate_preset_pass_manager( function internally builds and uses. The target constraints for the pass manager construction can be specified through a :class:`.Target` - instance, a :class:`.BackendV1` or :class:`.BackendV2` instance, or via loose constraints + instance, a :class:`.BackendV2` instance, or via loose constraints (``basis_gates``, ``coupling_map``, ``instruction_durations``, ``dt`` or ``timing_constraints``). The order of priorities for target constraints works as follows: if a ``target`` input is provided, it will take priority over any ``backend`` input or loose constraints. If a ``backend`` is provided together with any loose constraint from the list above, the loose constraint will take priority over the corresponding backend - constraint. This behavior is independent of whether the ``backend`` instance is of type - :class:`.BackendV1` or :class:`.BackendV2`, as summarized in the table below. The first column + constraint. This behavior is summarized in the table below. The first column in the table summarizes the potential user-provided constraints, and each cell shows whether the priority is assigned to that specific constraint input or another input (`target`/`backend(V1)`/`backend(V2)`). - ============================ ========= ======================== ======================= - User Provided target backend(V1) backend(V2) - ============================ ========= ======================== ======================= - **basis_gates** target basis_gates basis_gates - **coupling_map** target coupling_map coupling_map - **instruction_durations** target instruction_durations instruction_durations - **dt** target dt dt - **timing_constraints** target timing_constraints timing_constraints - ============================ ========= ======================== ======================= + ============================ ========= ======================== + User Provided target backend(V2) + ============================ ========= ======================== + **basis_gates** target basis_gates + **coupling_map** target coupling_map + **instruction_durations** target instruction_durations + **dt** target dt + **timing_constraints** target timing_constraints + ============================ ========= ======================== Args: optimization_level (int): The optimization level to generate a @@ -259,20 +257,6 @@ def generate_preset_pass_manager( backend = optimization_level optimization_level = 2 - if backend is not None and getattr(backend, "version", 0) <= 1: - # This is a temporary conversion step to allow for a smoother transition - # to a fully target-based transpiler pipeline while maintaining the behavior - # of `transpile` with BackendV1 inputs. - warnings.warn( - "The `generate_preset_pass_manager` function will stop supporting inputs of " - f"type `BackendV1` ( {backend} ) in the `backend` parameter in a future " - "release no earlier than 2.0. `BackendV1` is deprecated and implementations " - "should move to `BackendV2`.", - category=DeprecationWarning, - stacklevel=2, - ) - backend = BackendV2Converter(backend) - # If there are no loose constraints => use backend target if available _no_loose_constraints = ( basis_gates is None diff --git a/qiskit/transpiler/target.py b/qiskit/transpiler/target.py index 6ee764c2fc06..f30f6eb470a7 100644 --- a/qiskit/transpiler/target.py +++ b/qiskit/transpiler/target.py @@ -20,11 +20,9 @@ from __future__ import annotations import itertools -import warnings from typing import Optional, List, Any from collections.abc import Mapping -import datetime import io import logging import inspect @@ -43,13 +41,10 @@ from qiskit.transpiler.exceptions import TranspilerError from qiskit.transpiler.instruction_durations import InstructionDurations from qiskit.transpiler.timing_constraints import TimingConstraints -from qiskit.providers.exceptions import BackendPropertyError # import QubitProperties here to provide convenience alias for building a # full target from qiskit.providers.backend import QubitProperties # pylint: disable=unused-import -from qiskit.providers.models.backendproperties import BackendProperties -from qiskit.utils import deprecate_func logger = logging.getLogger(__name__) @@ -685,7 +680,6 @@ def from_configuration( basis_gates: list[str], num_qubits: int | None = None, coupling_map: CouplingMap | None = None, - backend_properties: BackendProperties | None = None, instruction_durations: InstructionDurations | None = None, concurrent_measurements: Optional[List[List[int]]] = None, dt: float | None = None, @@ -717,14 +711,6 @@ def from_configuration( coupling_map: The coupling map representing connectivity constraints on the backend. If specified all gates from ``basis_gates`` will be supported on all qubits (or pairs of qubits). - backend_properties: The :class:`~.BackendProperties` object which is - used for instruction properties and qubit properties. - If specified and instruction properties are intended to be used - then the ``coupling_map`` argument must be specified. This is - only used to lookup error rates and durations (unless - ``instruction_durations`` is specified which would take - precedence) for instructions specified via ``coupling_map`` and - ``basis_gates``. instruction_durations: Optional instruction durations for instructions. If specified it will take priority for setting the ``duration`` field in the :class:`~InstructionProperties` objects for the instructions in the target. @@ -760,11 +746,6 @@ def from_configuration( acquire_alignment = timing_constraints.acquire_alignment qubit_properties = None - if backend_properties is not None: - # pylint: disable=cyclic-import - from qiskit.providers.backend_compat import qubit_props_list_from_props - - qubit_properties = qubit_props_list_from_props(properties=backend_properties) target = cls( num_qubits=num_qubits, @@ -782,7 +763,7 @@ def from_configuration( # While BackendProperties can also contain coupling information we # rely solely on CouplingMap to determine connectivity. This is because - # in legacy transpiler usage (and implicitly in the BackendV1 data model) + # in legacy transpiler usage # the coupling map is used to define connectivity constraints and # the properties is only used for error rate and duration population. # If coupling map is not specified we ignore the backend_properties @@ -824,16 +805,6 @@ def from_configuration( for qubit in range(num_qubits): error = None duration = None - if backend_properties is not None: - if duration is None: - try: - duration = backend_properties.gate_length(gate, qubit) - except BackendPropertyError: - duration = None - try: - error = backend_properties.gate_error(gate, qubit) - except BackendPropertyError: - error = None # Durations if specified manually should override model objects if instruction_durations is not None: try: @@ -854,16 +825,6 @@ def from_configuration( for edge in edges: error = None duration = None - if backend_properties is not None: - if duration is None: - try: - duration = backend_properties.gate_length(gate, edge) - except BackendPropertyError: - duration = None - try: - error = backend_properties.gate_error(gate, edge) - except BackendPropertyError: - error = None # Durations if specified manually should override model objects if instruction_durations is not None: try: @@ -884,98 +845,3 @@ def from_configuration( Mapping.register(Target) - - -@deprecate_func( - since="1.2", - removal_timeline="in the 2.0 release", - additional_msg="This method is used to build an element from the deprecated " - "``qiskit.providers.models`` module. These models are part of the deprecated `BackendV1` " - "workflow and no longer necessary for `BackendV2`. If a user workflow requires these " - "representations it likely relies on deprecated functionality and " - "should be updated to use `BackendV2`.", -) -def target_to_backend_properties(target: Target): - """Convert a :class:`~.Target` object into a legacy :class:`~.BackendProperties`""" - - properties_dict: dict[str, Any] = { - "backend_name": "", - "backend_version": "", - "last_update_date": None, - "general": [], - } - gates = [] - qubits = [] - for gate, qargs_list in target.items(): - if gate != "measure": - for qargs, props in qargs_list.items(): - property_list = [] - if getattr(props, "duration", None) is not None: - property_list.append( - { - "date": datetime.datetime.now(datetime.timezone.utc), - "name": "gate_length", - "unit": "s", - "value": props.duration, - } - ) - if getattr(props, "error", None) is not None: - property_list.append( - { - "date": datetime.datetime.now(datetime.timezone.utc), - "name": "gate_error", - "unit": "", - "value": props.error, - } - ) - if property_list: - gates.append( - { - "gate": gate, - "qubits": list(qargs), - "parameters": property_list, - "name": gate + "_".join([str(x) for x in qargs]), - } - ) - else: - qubit_props: dict[int, Any] = {} - if target.num_qubits is not None: - qubit_props = {x: None for x in range(target.num_qubits)} - for qargs, props in qargs_list.items(): - if qargs is None: - continue - qubit = qargs[0] - props_list = [] - if getattr(props, "error", None) is not None: - props_list.append( - { - "date": datetime.datetime.now(datetime.timezone.utc), - "name": "readout_error", - "unit": "", - "value": props.error, - } - ) - if getattr(props, "duration", None) is not None: - props_list.append( - { - "date": datetime.datetime.now(datetime.timezone.utc), - "name": "readout_length", - "unit": "s", - "value": props.duration, - } - ) - if not props_list: - qubit_props = {} - break - qubit_props[qubit] = props_list - if qubit_props and all(x is not None for x in qubit_props.values()): - qubits = [qubit_props[i] for i in range(target.num_qubits)] - if gates or qubits: - properties_dict["gates"] = gates - properties_dict["qubits"] = qubits - with warnings.catch_warnings(): - # This raises BackendProperties internally - warnings.filterwarnings("ignore", category=DeprecationWarning) - return BackendProperties.from_dict(properties_dict) - else: - return None diff --git a/qiskit/visualization/bloch.py b/qiskit/visualization/bloch.py index ae7083a9fd61..5c344702fd76 100644 --- a/qiskit/visualization/bloch.py +++ b/qiskit/visualization/bloch.py @@ -138,7 +138,7 @@ def draw(self, renderer): class Bloch: """Class for plotting data on the Bloch sphere. Valid data can be - either points, vectors, or qobj objects. + either points or vectors. Attributes: axes (instance): diff --git a/qiskit/visualization/gate_map.py b/qiskit/visualization/gate_map.py index 06dc3c66e425..2f18a492e343 100644 --- a/qiskit/visualization/gate_map.py +++ b/qiskit/visualization/gate_map.py @@ -21,16 +21,10 @@ from qiskit.exceptions import QiskitError from qiskit.utils import optionals as _optionals -from qiskit.providers.exceptions import BackendPropertyError from qiskit.transpiler.coupling import CouplingMap from .exceptions import VisualizationError -def _get_backend_interface_version(backend): - backend_interface_version = getattr(backend, "version", None) - return backend_interface_version - - @_optionals.HAS_MATPLOTLIB.require_in_call def plot_gate_map( backend, @@ -909,18 +903,9 @@ def plot_gate_map( [24, 26], ] - backend_version = _get_backend_interface_version(backend) - if backend_version <= 1: - if backend.configuration().simulator: - raise QiskitError("Requires a device backend, not simulator.") - config = backend.configuration() - num_qubits = config.n_qubits - coupling_map = CouplingMap(config.coupling_map) - name = backend.name() - else: - num_qubits = backend.num_qubits - coupling_map = backend.coupling_map - name = backend.name + num_qubits = backend.num_qubits + coupling_map = backend.coupling_map + name = backend.name if qubit_coordinates is None and ("ibm" in name or "fake" in name): qubit_coordinates = qubit_coordinates_map.get(num_qubits, None) @@ -1190,15 +1175,9 @@ def plot_circuit_layout(circuit, backend, view="virtual", qubit_coordinates=None if circuit._layout is None: raise QiskitError("Circuit has no layout. Perhaps it has not been transpiled.") - backend_version = _get_backend_interface_version(backend) - if backend_version <= 1: - num_qubits = backend.configuration().n_qubits - cmap = backend.configuration().coupling_map - cmap_len = len(cmap) - else: - num_qubits = backend.num_qubits - cmap = backend.coupling_map - cmap_len = cmap.graph.num_edges() + num_qubits = backend.num_qubits + cmap = backend.coupling_map + cmap_len = cmap.graph.num_edges() qubits = [] qubit_labels = [""] * num_qubits @@ -1290,84 +1269,40 @@ def plot_error_map(backend, figsize=(15, 12), show_title=True, qubit_coordinates color_map = sns.cubehelix_palette(reverse=True, as_cmap=True) - backend_version = _get_backend_interface_version(backend) - if backend_version <= 1: - backend_name = backend.name() - num_qubits = backend.configuration().n_qubits - cmap = backend.configuration().coupling_map - props = backend.properties() - props_dict = props.to_dict() - single_gate_errors = [0] * num_qubits - read_err = [0] * num_qubits - cx_errors = [] - # sx error rates - for gate in props_dict["gates"]: - if gate["gate"] == "sx": - _qubit = gate["qubits"][0] - for param in gate["parameters"]: - if param["name"] == "gate_error": - single_gate_errors[_qubit] = param["value"] - break - else: - raise VisualizationError( - f"Backend '{backend}' did not supply an error for the 'sx' gate." - ) - if cmap: - directed = False - if num_qubits < 20: - for edge in cmap: - if not [edge[1], edge[0]] in cmap: - directed = True - break - - for line in cmap: - for item in props_dict["gates"]: - if item["qubits"] == line: - cx_errors.append(item["parameters"][0]["value"]) - break - for qubit in range(num_qubits): - try: - read_err[qubit] = props.readout_error(qubit) - except BackendPropertyError: - pass - - else: - backend_name = backend.name - num_qubits = backend.num_qubits - cmap = backend.coupling_map - two_q_error_map = {} - single_gate_errors = [0] * num_qubits - read_err = [0] * num_qubits - cx_errors = [] - for gate, prop_dict in backend.target.items(): - if prop_dict is None or None in prop_dict: + backend_name = backend.name + num_qubits = backend.num_qubits + cmap = backend.coupling_map + two_q_error_map = {} + single_gate_errors = [0] * num_qubits + read_err = [0] * num_qubits + cx_errors = [] + for gate, prop_dict in backend.target.items(): + if prop_dict is None or None in prop_dict: + continue + for qargs, inst_props in prop_dict.items(): + if inst_props is None: continue - for qargs, inst_props in prop_dict.items(): - if inst_props is None: - continue - if gate == "measure": - if inst_props.error is not None: - read_err[qargs[0]] = inst_props.error - elif len(qargs) == 1: - if inst_props.error is not None: - single_gate_errors[qargs[0]] = max( - single_gate_errors[qargs[0]], inst_props.error - ) - elif len(qargs) == 2: - if inst_props.error is not None: - two_q_error_map[qargs] = max( - two_q_error_map.get(qargs, 0), inst_props.error - ) - if cmap: - directed = False - if num_qubits < 20: - for edge in cmap: - if not [edge[1], edge[0]] in cmap: - directed = True - break - for line in cmap.get_edges(): - err = two_q_error_map.get(tuple(line), 0) - cx_errors.append(err) + if gate == "measure": + if inst_props.error is not None: + read_err[qargs[0]] = inst_props.error + elif len(qargs) == 1: + if inst_props.error is not None: + single_gate_errors[qargs[0]] = max( + single_gate_errors[qargs[0]], inst_props.error + ) + elif len(qargs) == 2: + if inst_props.error is not None: + two_q_error_map[qargs] = max(two_q_error_map.get(qargs, 0), inst_props.error) + if cmap: + directed = False + if num_qubits < 20: + for edge in cmap: + if not [edge[1], edge[0]] in cmap: + directed = True + break + for line in cmap.get_edges(): + err = two_q_error_map.get(tuple(line), 0) + cx_errors.append(err) # Convert to percent single_gate_errors = 100 * np.asarray(single_gate_errors) diff --git a/qiskit/visualization/pulse_v2/device_info.py b/qiskit/visualization/pulse_v2/device_info.py index 7898f978772c..3a3f2688db59 100644 --- a/qiskit/visualization/pulse_v2/device_info.py +++ b/qiskit/visualization/pulse_v2/device_info.py @@ -39,7 +39,6 @@ class :py:class:``DrawerBackendInfo`` with necessary methods to generate drawing from typing import Dict, List, Union, Optional from qiskit import pulse -from qiskit.providers import BackendConfigurationError from qiskit.providers.backend import Backend, BackendV2 @@ -109,42 +108,7 @@ def create_from_backend(cls, backend: Backend): chan_freqs = {} qubit_channel_map = defaultdict(list) - if hasattr(backend, "configuration") and hasattr(backend, "defaults"): - configuration = backend.configuration() - defaults = backend.defaults() - - name = configuration.backend_name - dt = configuration.dt - - # load frequencies - chan_freqs.update( - { - pulse.DriveChannel(qind): freq - for qind, freq in enumerate(defaults.qubit_freq_est) - } - ) - chan_freqs.update( - { - pulse.MeasureChannel(qind): freq - for qind, freq in enumerate(defaults.meas_freq_est) - } - ) - for qind, u_lo_mappers in enumerate(configuration.u_channel_lo): - temp_val = 0.0 + 0.0j - for u_lo_mapper in u_lo_mappers: - temp_val += defaults.qubit_freq_est[u_lo_mapper.q] * u_lo_mapper.scale - chan_freqs[pulse.ControlChannel(qind)] = temp_val.real - - # load qubit channel mapping - for qind in range(configuration.n_qubits): - qubit_channel_map[qind].append(configuration.drive(qubit=qind)) - qubit_channel_map[qind].append(configuration.measure(qubit=qind)) - for tind in range(configuration.n_qubits): - try: - qubit_channel_map[qind].extend(configuration.control(qubits=(qind, tind))) - except BackendConfigurationError: - pass - elif isinstance(backend, BackendV2): + if isinstance(backend, BackendV2): # Pure V2 model doesn't contain channel frequency information. name = backend.name dt = backend.dt diff --git a/releasenotes/notes/remove-backend-v1-et-al-cccd8f6b71a97ffc.yaml b/releasenotes/notes/remove-backend-v1-et-al-cccd8f6b71a97ffc.yaml new file mode 100644 index 000000000000..341d81571efd --- /dev/null +++ b/releasenotes/notes/remove-backend-v1-et-al-cccd8f6b71a97ffc.yaml @@ -0,0 +1,97 @@ +--- +upgrade_providers: + - | + The ``BackendV1`` model has been removed following its deprecation in + Qiskit 1.2.0. This includes the ``BackendV1`` class as well as related + modules and utils, as they have been superseded by the :class:`.BackendV2` + model. The list of removed items includes: + + * ``BackendV1`` class: the core of the removed model + + * All elements in `qiskit/providers/models`, as they were used to represent + components of the ``BackendV1`` model: + + * ``BackendConfiguration`` + * ``BackendProperties`` + * ``BackendStatus`` + * ``QasmBackendConfiguration`` + * ``PulseBackendConfiguration`` + * ``UchannelLO`` + * ``GateConfig`` + * ``PulseDefaults`` + * ``PulseQobjDef`` + * ``Command`` + * ``GateProperties`` + * ``Nduv`` + * ``JobStatus``: This class has been superseded by the more widely used :class:`.JobStatus` + * ``PulseDefaults`` + + * ``BackendV2Converter`` class: used to convert from ``BackendV1`` to :class:`.BackendV2` + + * ``convert_to_target`` function: used to build a :class:`.Target` instance from + legacy ``BackendV1`` components (such as ``BackendConfiguration`` or ``BackendProperties``) + + * ``BackendPropertyError`` and ``BackendConfigurationError``: exceptions linked to removed classes + +upgrade_primitives: + - | + As a consequence of the removal of the ``BackendV1`` model, the :class:`.BackendSamplerV2` and + :class:`.BackendEstimatorV2` classes no longer accept inputs of type ``BackendV1`` in their + ``backend`` input argument. + +upgrade_transpiler: + - | + As a consequence of the removal of the ``BackendV1`` model, the accepted input types of the following + transpiler objects have been updated: + + * The :func:`.generate_preset_pass_manager` and :func:`.transpile` functions no longer accept inputs of + type ``BackendV1`` in their ``backend`` input argument. + + * The :meth:`.Target.from_configuration` method no longer accepts a ``backend_properties`` argument + + * The :meth:`.Target.target_to_backend_properties` method has been removed + +upgrade_visualization: + - | + As a consequence of the removal of the ``BackendV1`` model, the :func:`.plot_gate_map`, :func:`.plot_error_map` + and :func:`.plot_circuit_layout` functions no longer accept inputs of type ``BackendV1`` in their + ``backend`` input argument. + +upgrade_misc: + - | + The ``Qobj`` structure and related classes, deprecated in Qiskit 1.2.0, + have been removed. They were introduced as part of the ``BackendV1`` + workflow and are no longer necessary for interacting with :class:`.BackendV2` backends. + This removal includes: + + * ``QobjExperimentHeader`` + * ``QobjHeader`` + * ``QasmQobj`` + * ``QasmQobjInstruction`` + * ``QasmQobjExperimentConfig`` + * ``QasmQobjExperiment`` + * ``QasmQobjConfig`` + * ``QasmExperimentCalibrations`` + * ``GateCalibration`` + * ``PulseQobj`` + * ``PulseQobjInstruction`` + * ``PulseQobjExperimentConfig`` + * ``PulseQobjExperiment`` + * ``PulseQobjConfig`` + * ``QobjMeasurementOption`` + * ``PulseLibraryItem`` + + - | + The :class:`.MeasLevel` and :class`.MeasReturnType` classes, previously defined in + ``qobj/utils.py``, have been migrated to ``result/models.py`` following the removal + of the ``qobj`` module. These classes were not part of the public API. + The import path has been updated + from: ``from qiskit.qobj.utils import MeasLevel, MeasReturnType`` to: + ``from qiskit.result import MeasLevel, MeasReturnType``. + + - | + The use of positional arguments in the constructor of :class:`.Result` has been disabled. + Please set all arguments using kwarg syntax, i.e: ``Result(backend_name="name", ....)``. + In addition to this, the ``qobj_id`` argument will no longer be used in the construction + of the :class:`.Result` internals. It is still possible to set ``qobj_id`` as a + generic kwarg, which will land in the metadata field with the other generic kwargs. \ No newline at end of file diff --git a/test/python/circuit/test_hamiltonian_gate.py b/test/python/circuit/test_hamiltonian_gate.py index fa8525561b43..e8ab506f1061 100644 --- a/test/python/circuit/test_hamiltonian_gate.py +++ b/test/python/circuit/test_hamiltonian_gate.py @@ -135,26 +135,6 @@ def test_3q_hamiltonian(self): self.assertEqual(dnode.qargs, (qr[0], qr[1], qr[3])) np.testing.assert_almost_equal(dnode.op.to_matrix(), 1j * matrix.data) - def test_qobj_with_hamiltonian(self): - """test qobj output with hamiltonian - REMOVE once Qobj gets removed""" - qr = QuantumRegister(4) - qc = QuantumCircuit(qr) - qc.rx(np.pi / 4, qr[0]) - matrix = Operator.from_label("XIZ") - theta = Parameter("theta") - uni = HamiltonianGate(matrix, theta, label="XIZ") - qc.append(uni, [qr[0], qr[1], qr[3]]) - qc.cx(qr[3], qr[2]) - qc = qc.assign_parameters({theta: np.pi / 2}) - instr = qc.data[1] - self.assertEqual(instr.name, "hamiltonian") - # Also test label - self.assertEqual(instr.label, "XIZ") - np.testing.assert_array_almost_equal( - np.array(instr.params[0]).astype(np.complex64), matrix.data - ) - def test_decomposes_into_correct_unitary(self): """test 2 qubit hamiltonian""" qc = QuantumCircuit(2) diff --git a/test/python/compiler/test_compiler.py b/test/python/compiler/test_compiler.py index 40dff32c681f..6d4cd0ea3c8e 100644 --- a/test/python/compiler/test_compiler.py +++ b/test/python/compiler/test_compiler.py @@ -448,12 +448,12 @@ def test_random_parameter_circuit(self): coupling_map = CouplingMap([[0, 1], [1, 2], [2, 3], [3, 4]]) coupling_map.make_symmetric() shots = 1024 - qobj = self.backend.run( + job = self.backend.run( transpile(circ, backend=self.backend, coupling_map=coupling_map, seed_transpiler=42), shots=shots, seed_simulator=self.seed_simulator, ) - counts = qobj.result().get_counts() + counts = job.result().get_counts() expected_probs = { "00000": 0.079239867254200971, "00001": 0.032859032998526903, diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py index eac19218580b..32845192a51d 100644 --- a/test/python/compiler/test_transpiler.py +++ b/test/python/compiler/test_transpiler.py @@ -1397,7 +1397,7 @@ def test_scheduling_instruction_constraints(self): def test_scheduling_dt_constraints(self): """Test that scheduling-related loose transpile constraints - work with both BackendV1 and BackendV2.""" + work with BackendV2.""" backend_v2 = GenericBackendV2(num_qubits=2, seed=1) qc = QuantumCircuit(1, 1) diff --git a/test/python/primitives/test_backend_sampler_v2.py b/test/python/primitives/test_backend_sampler_v2.py index 0baafb008305..cd13975f8d9c 100644 --- a/test/python/primitives/test_backend_sampler_v2.py +++ b/test/python/primitives/test_backend_sampler_v2.py @@ -33,7 +33,7 @@ from qiskit.providers.basic_provider import BasicProviderJob, BasicSimulator from qiskit.providers.fake_provider import GenericBackendV2 from qiskit.result import Result -from qiskit.qobj.utils import MeasReturnType, MeasLevel +from qiskit.result.models import MeasReturnType, MeasLevel from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager from qiskit.utils import optionals from ..legacy_cmaps import LAGOS_CMAP diff --git a/test/python/providers/test_options.py b/test/python/providers/test_options.py index 194423fef528..59f69269b309 100644 --- a/test/python/providers/test_options.py +++ b/test/python/providers/test_options.py @@ -17,7 +17,7 @@ import pickle from qiskit.providers import Options -from qiskit.qobj.utils import MeasLevel +from qiskit.result.models import MeasLevel from test import QiskitTestCase # pylint: disable=wrong-import-order diff --git a/test/python/pulse/test_calibration_entries.py b/test/python/pulse/test_calibration_entries.py index 6a112ab854d5..166b03303704 100644 --- a/test/python/pulse/test_calibration_entries.py +++ b/test/python/pulse/test_calibration_entries.py @@ -19,19 +19,14 @@ Schedule, ScheduleBlock, Play, - ShiftPhase, Constant, - Waveform, DriveChannel, ) from qiskit.pulse.calibration_entries import ( ScheduleDef, CallableDef, - PulseQobjDef, ) from qiskit.pulse.exceptions import PulseError -from qiskit.qobj.converters.pulse_instruction import QobjToInstructionConverter -from qiskit.qobj.pulse_qobj import PulseLibraryItem, PulseQobjInstruction from test import QiskitTestCase # pylint: disable=wrong-import-order from qiskit.utils.deprecate_pulse import decorate_test_methods, ignore_pulse_deprecation_warnings @@ -277,232 +272,3 @@ def factory2(): self.assertEqual(entry1, entry3) self.assertNotEqual(entry1, entry2) - - -@decorate_test_methods(ignore_pulse_deprecation_warnings) -class TestPulseQobj(QiskitTestCase): - """Test case for the PulseQobjDef.""" - - def setUp(self): - super().setUp() - with self.assertWarns(DeprecationWarning): - self.converter = QobjToInstructionConverter( - pulse_library=[ - PulseLibraryItem(name="waveform", samples=[0.3, 0.1, 0.2, 0.2, 0.3]), - ] - ) - - def test_add_qobj(self): - """Basic test PulseQobj format.""" - with self.assertWarns(DeprecationWarning): - serialized_program = [ - PulseQobjInstruction( - name="parametric_pulse", - t0=0, - ch="d0", - label="TestPulse", - pulse_shape="constant", - parameters={"amp": 0.1 + 0j, "duration": 10}, - ), - PulseQobjInstruction( - name="waveform", - t0=20, - ch="d0", - ), - ] - - entry = PulseQobjDef(converter=self.converter, name="my_gate") - entry.define(serialized_program) - - signature_to_test = list(entry.get_signature().parameters.keys()) - signature_ref = [] - self.assertListEqual(signature_to_test, signature_ref) - - schedule_to_test = entry.get_schedule() - schedule_ref = Schedule() - schedule_ref.insert( - 0, - Play(Constant(duration=10, amp=0.1, angle=0.0), DriveChannel(0)), - inplace=True, - ) - schedule_ref.insert( - 20, - Play(Waveform([0.3, 0.1, 0.2, 0.2, 0.3]), DriveChannel(0)), - inplace=True, - ) - self.assertEqual(schedule_to_test, schedule_ref) - - def test_missing_waveform(self): - """Test incomplete Qobj should raise warning and calibration returns None.""" - with self.assertWarns(DeprecationWarning): - serialized_program = [ - PulseQobjInstruction( - name="waveform_123456", - t0=20, - ch="d0", - ), - ] - entry = PulseQobjDef(converter=self.converter, name="my_gate") - entry.define(serialized_program) - - with self.assertWarns( - UserWarning, - msg=( - "Pulse calibration cannot be built and the entry is ignored: " - "Instruction waveform_123456 on channel d0 is not found in Qiskit namespace. " - "This instruction cannot be deserialized." - ), - ): - out = entry.get_schedule() - - self.assertIsNone(out) - - def test_parameterized_qobj(self): - """Test adding and managing parameterized qobj. - - Note that pulse parameter cannot be parameterized by convention. - """ - with self.assertWarns(DeprecationWarning): - serialized_program = [ - PulseQobjInstruction( - name="parametric_pulse", - t0=0, - ch="d0", - label="TestPulse", - pulse_shape="constant", - parameters={"amp": 0.1, "duration": 10}, - ), - PulseQobjInstruction( - name="fc", - t0=0, - ch="d0", - phase="P1", - ), - ] - - entry = PulseQobjDef(converter=self.converter, name="my_gate") - entry.define(serialized_program) - - signature_to_test = list(entry.get_signature().parameters.keys()) - signature_ref = ["P1"] - self.assertListEqual(signature_to_test, signature_ref) - - schedule_to_test = entry.get_schedule(P1=1.57) - schedule_ref = Schedule() - schedule_ref.insert( - 0, - Play(Constant(duration=10, amp=0.1, angle=0.0), DriveChannel(0)), - inplace=True, - ) - schedule_ref.insert( - 0, - ShiftPhase(1.57, DriveChannel(0)), - inplace=True, - ) - self.assertEqual(schedule_to_test, schedule_ref) - - def test_equality(self): - """Test equality evaluation between the pulse qobj entries.""" - with self.assertWarns(DeprecationWarning): - serialized_program1 = [ - PulseQobjInstruction( - name="parametric_pulse", - t0=0, - ch="d0", - label="TestPulse", - pulse_shape="constant", - parameters={"amp": 0.1, "duration": 10}, - ) - ] - serialized_program2 = [ - PulseQobjInstruction( - name="parametric_pulse", - t0=0, - ch="d0", - label="TestPulse", - pulse_shape="constant", - parameters={"amp": 0.2, "duration": 10}, - ) - ] - - with self.assertWarns(DeprecationWarning): - entry1 = PulseQobjDef(name="my_gate1") - entry1.define(serialized_program1) - - with self.assertWarns(DeprecationWarning): - entry2 = PulseQobjDef(name="my_gate2") - entry2.define(serialized_program2) - - with self.assertWarns(DeprecationWarning): - entry3 = PulseQobjDef(name="my_gate3") - entry3.define(serialized_program1) - - self.assertEqual(entry1, entry3) - self.assertNotEqual(entry1, entry2) - - def test_equality_with_schedule(self): - """Test equality, but other is schedule entry. - - Because the pulse qobj entry is a subclass of the schedule entry, - these instances can be compared by the generated definition, i.e. Schedule. - """ - with self.assertWarns(DeprecationWarning): - serialized_program = [ - PulseQobjInstruction( - name="parametric_pulse", - t0=0, - ch="d0", - label="TestPulse", - pulse_shape="constant", - parameters={"amp": 0.1, "duration": 10}, - ) - ] - entry1 = PulseQobjDef(name="qobj_entry") - entry1.define(serialized_program) - - program = Schedule() - program.insert( - 0, - Play(Constant(duration=10, amp=0.1, angle=0.0), DriveChannel(0)), - inplace=True, - ) - entry2 = ScheduleDef() - entry2.define(program) - - self.assertEqual(entry1, entry2) - - def test_calibration_missing_waveform(self): - """Test that calibration with missing waveform should become None. - - When a hardware doesn't support waveform payload and Qiskit doesn't have - the corresponding parametric pulse definition, CmdDef with missing waveform - might be input to the QobjConverter. This fails in loading the calibration data - because necessary pulse object cannot be built. - - In this situation, parsed calibration data must become None, - instead of raising an error. - """ - with self.assertWarns(DeprecationWarning): - serialized_program = [ - PulseQobjInstruction( - name="SomeMissingPulse", - t0=0, - ch="d0", - ) - ] - with self.assertWarns(DeprecationWarning): - entry = PulseQobjDef(name="qobj_entry") - entry.define(serialized_program) - - # This is pulse qobj before parsing it - self.assertEqual(str(entry), "PulseQobj") - - # Actual calibration value is None - parsed_output = entry.get_schedule() - self.assertIsNone(parsed_output) - - # Repr becomes None-like after it finds calibration is incomplete - self.assertEqual(str(entry), "None") - - # Signature is also None - self.assertIsNone(entry.get_signature()) diff --git a/test/python/qobj/__init__.py b/test/python/qobj/__init__.py deleted file mode 100644 index e9ca20dd1979..000000000000 --- a/test/python/qobj/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# 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. - -"""Qiskit Qobj tests.""" diff --git a/test/python/qobj/test_pulse_converter.py b/test/python/qobj/test_pulse_converter.py deleted file mode 100644 index 8f920eb96d80..000000000000 --- a/test/python/qobj/test_pulse_converter.py +++ /dev/null @@ -1,523 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2019. -# -# 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. - -"""Converter Test.""" - -import hashlib -import numpy as np - -from qiskit.pulse import LoConfig, Kernel, Discriminator -from qiskit.pulse.channels import ( - DriveChannel, - ControlChannel, - MeasureChannel, - AcquireChannel, - MemorySlot, - RegisterSlot, -) -from qiskit.pulse.instructions import ( - SetPhase, - ShiftPhase, - SetFrequency, - ShiftFrequency, - Play, - Delay, - Acquire, - Snapshot, -) -from qiskit.pulse.library import Waveform, Gaussian, GaussianSquare, Constant, Drag -from qiskit.pulse.schedule import Schedule -from qiskit.qobj import ( - PulseQobjInstruction, - PulseQobjExperimentConfig, - PulseLibraryItem, - QobjMeasurementOption, -) -from qiskit.qobj.converters import ( - InstructionToQobjConverter, - QobjToInstructionConverter, - LoConfigConverter, -) -from qiskit.exceptions import QiskitError -from test import QiskitTestCase # pylint: disable=wrong-import-order - - -class TestInstructionToQobjConverter(QiskitTestCase): - """Pulse converter tests.""" - - def test_drive_instruction(self): - """Test converted qobj from Play.""" - with self.assertWarns(DeprecationWarning): - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) - instruction = Play(Waveform(np.arange(0, 0.01), name="linear"), DriveChannel(0)) - valid_qobj = PulseQobjInstruction(name="linear", ch="d0", t0=0) - self.assertEqual(converter(0, instruction), valid_qobj) - - def test_gaussian_pulse_instruction(self): - """Test that parametric pulses are correctly converted to PulseQobjInstructions.""" - amp = 0.3 - angle = -0.7 - with self.assertWarns(DeprecationWarning): - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) - instruction = Play( - Gaussian(duration=25, sigma=15, amp=amp, angle=angle), DriveChannel(0) - ) - with self.assertWarns(DeprecationWarning): - valid_qobj = PulseQobjInstruction( - name="parametric_pulse", - pulse_shape="gaussian", - ch="d0", - t0=0, - parameters={"duration": 25, "sigma": 15, "amp": amp * np.exp(1j * angle)}, - ) - self.assertEqual(converter(0, instruction), valid_qobj) - - def test_gaussian_square_pulse_instruction(self): - """Test that parametric pulses are correctly converted to PulseQobjInstructions.""" - with self.assertWarns(DeprecationWarning): - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) - amp = 0.7 - angle = -0.6 - with self.assertWarns(DeprecationWarning): - instruction = Play( - GaussianSquare(duration=1500, sigma=15, amp=amp, width=1300, angle=angle), - MeasureChannel(1), - ) - - with self.assertWarns(DeprecationWarning): - valid_qobj = PulseQobjInstruction( - name="parametric_pulse", - pulse_shape="gaussian_square", - ch="m1", - t0=10, - parameters={ - "duration": 1500, - "sigma": 15, - "amp": amp * np.exp(1j * angle), - "width": 1300, - }, - ) - self.assertEqual(converter(10, instruction), valid_qobj) - - def test_constant_pulse_instruction(self): - """Test that parametric pulses are correctly converted to PulseQobjInstructions.""" - with self.assertWarns(DeprecationWarning): - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) - instruction = Play(Constant(duration=25, amp=1, angle=np.pi), ControlChannel(2)) - - with self.assertWarns(DeprecationWarning): - valid_qobj = PulseQobjInstruction( - name="parametric_pulse", - pulse_shape="constant", - ch="u2", - t0=20, - parameters={"duration": 25, "amp": 1 * np.exp(1j * np.pi)}, - ) - self.assertEqual(converter(20, instruction), valid_qobj) - - def test_drag_pulse_instruction(self): - """Test that parametric pulses are correctly converted to PulseQobjInstructions.""" - amp = 0.7 - angle = -0.6 - with self.assertWarns(DeprecationWarning): - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) - instruction = Play( - Drag(duration=25, sigma=15, amp=amp, angle=angle, beta=0.5), DriveChannel(0) - ) - - with self.assertWarns(DeprecationWarning): - valid_qobj = PulseQobjInstruction( - name="parametric_pulse", - pulse_shape="drag", - ch="d0", - t0=30, - parameters={ - "duration": 25, - "sigma": 15, - "amp": amp * np.exp(1j * angle), - "beta": 0.5, - }, - ) - self.assertEqual(converter(30, instruction), valid_qobj) - - def test_frame_change(self): - """Test converted qobj from ShiftPhase.""" - with self.assertWarns(DeprecationWarning): - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) - valid_qobj = PulseQobjInstruction(name="fc", ch="d0", t0=0, phase=0.1) - instruction = ShiftPhase(0.1, DriveChannel(0)) - self.assertEqual(converter(0, instruction), valid_qobj) - - def test_set_phase(self): - """Test converted qobj from ShiftPhase.""" - with self.assertWarns(DeprecationWarning): - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) - instruction = SetPhase(3.14, DriveChannel(0)) - - valid_qobj = PulseQobjInstruction(name="setp", ch="d0", t0=0, phase=3.14) - - self.assertEqual(converter(0, instruction), valid_qobj) - - def test_set_frequency(self): - """Test converted qobj from SetFrequency.""" - with self.assertWarns(DeprecationWarning): - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) - instruction = SetFrequency(8.0e9, DriveChannel(0)) - - valid_qobj = PulseQobjInstruction(name="setf", ch="d0", t0=0, frequency=8.0) - - self.assertEqual(converter(0, instruction), valid_qobj) - - def test_shift_frequency(self): - """Test converted qobj from ShiftFrequency.""" - with self.assertWarns(DeprecationWarning): - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) - instruction = ShiftFrequency(8.0e9, DriveChannel(0)) - - valid_qobj = PulseQobjInstruction(name="shiftf", ch="d0", t0=0, frequency=8.0) - - self.assertEqual(converter(0, instruction), valid_qobj) - - def test_acquire(self): - """Test converted qobj from AcquireInstruction.""" - with self.assertWarns(DeprecationWarning): - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) - instruction = Acquire(10, AcquireChannel(0), MemorySlot(0), RegisterSlot(0)) - - valid_qobj = PulseQobjInstruction( - name="acquire", t0=0, duration=10, qubits=[0], memory_slot=[0], register_slot=[0] - ) - self.assertEqual(converter(0, instruction), valid_qobj) - - # without register - instruction = Acquire(10, AcquireChannel(0), MemorySlot(0)) - valid_qobj = PulseQobjInstruction( - name="acquire", t0=0, duration=10, qubits=[0], memory_slot=[0] - ) - self.assertEqual(converter(0, instruction), valid_qobj) - - def test_snapshot(self): - """Test converted qobj from Snapshot.""" - with self.assertWarns(DeprecationWarning): - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) - instruction = Snapshot(label="label", snapshot_type="type") - - with self.assertWarns(DeprecationWarning): - valid_qobj = PulseQobjInstruction(name="snapshot", t0=0, label="label", type="type") - - self.assertEqual(converter(0, instruction), valid_qobj) - - -class TestQobjToInstructionConverter(QiskitTestCase): - """Pulse converter tests.""" - - def setUp(self): - super().setUp() - with self.assertWarns(DeprecationWarning): - self.linear = Waveform(np.arange(0, 0.01), name="linear") - self.pulse_library = [ - PulseLibraryItem(name=self.linear.name, samples=self.linear.samples.tolist()) - ] - - with self.assertWarns(DeprecationWarning): - self.converter = QobjToInstructionConverter(self.pulse_library, buffer=0) - self.num_qubits = 2 - - def test_drive_instruction(self): - """Test converted qobj from PulseInstruction.""" - with self.assertWarns(DeprecationWarning): - instruction = Play(self.linear, DriveChannel(0)) - qobj = PulseQobjInstruction(name="linear", ch="d0", t0=10) - converted_instruction = self.converter(qobj) - self.assertEqual(converted_instruction.instructions[0][-1], instruction) - - def test_parametric_pulses(self): - """Test converted qobj from ParametricInstruction.""" - with self.assertWarns(DeprecationWarning): - instruction = Play( - Gaussian(duration=25, sigma=15, amp=0.5, angle=np.pi / 2, name="pulse1"), - DriveChannel(0), - ) - qobj = PulseQobjInstruction( - name="parametric_pulse", - label="pulse1", - pulse_shape="gaussian", - ch="d0", - t0=0, - parameters={"duration": 25, "sigma": 15, "amp": 0.5j}, - ) - converted_instruction = self.converter(qobj) - self.assertEqual(converted_instruction.start_time, 0) - self.assertEqual(converted_instruction.duration, 25) - self.assertAlmostEqual(converted_instruction.instructions[0][-1], instruction) - self.assertEqual(converted_instruction.instructions[0][-1].pulse.name, "pulse1") - - def test_parametric_pulses_no_label(self): - """Test converted qobj from ParametricInstruction without label.""" - base_str = "gaussian_[('amp', (-0.5+0.2j)), ('duration', 25), ('sigma', 15)]" - short_pulse_id = hashlib.md5(base_str.encode("utf-8")).hexdigest()[:4] - pulse_name = f"gaussian_{short_pulse_id}" - - with self.assertWarns(DeprecationWarning): - qobj = PulseQobjInstruction( - name="parametric_pulse", - pulse_shape="gaussian", - ch="d0", - t0=0, - parameters={"duration": 25, "sigma": 15, "amp": -0.5 + 0.2j}, - ) - converted_instruction = self.converter(qobj) - self.assertEqual(converted_instruction.instructions[0][-1].pulse.name, pulse_name) - - def test_frame_change(self): - """Test converted qobj from ShiftPhase.""" - with self.assertWarns(DeprecationWarning): - qobj = PulseQobjInstruction(name="fc", ch="m0", t0=0, phase=0.1) - converted_instruction = self.converter(qobj) - - instruction = ShiftPhase(0.1, MeasureChannel(0)) - self.assertEqual(converted_instruction.start_time, 0) - self.assertEqual(converted_instruction.duration, 0) - self.assertEqual(converted_instruction.instructions[0][-1], instruction) - - def test_parameterized_frame_change(self): - """Test converted qobj from ShiftPhase.""" - with self.assertWarns(DeprecationWarning): - instruction = ShiftPhase(4.0, MeasureChannel(0)) - shifted = instruction << 10 - - qobj = PulseQobjInstruction(name="fc", ch="m0", t0=10, phase="P1*2") - converted_instruction = self.converter(qobj) - - self.assertIsInstance(converted_instruction, Schedule) - - bind_dict = {converted_instruction.get_parameters("P1")[0]: 2.0} - evaluated_instruction = converted_instruction.assign_parameters(bind_dict) - - self.assertEqual(evaluated_instruction.start_time, shifted.start_time) - self.assertEqual(evaluated_instruction.duration, shifted.duration) - self.assertEqual(evaluated_instruction.instructions[0][-1], instruction) - - def test_set_phase(self): - """Test converted qobj from SetPhase.""" - with self.assertWarns(DeprecationWarning): - qobj = PulseQobjInstruction(name="setp", ch="m0", t0=0, phase=3.14) - converted_instruction = self.converter(qobj) - - instruction = SetPhase(3.14, MeasureChannel(0)) - self.assertEqual(converted_instruction.start_time, 0) - self.assertEqual(converted_instruction.duration, 0) - self.assertEqual(converted_instruction.instructions[0][-1], instruction) - - def test_parameterized_set_phase(self): - """Test converted qobj from SetPhase, with parameterized phase.""" - with self.assertWarns(DeprecationWarning): - qobj = PulseQobjInstruction(name="setp", ch="m0", t0=0, phase="p/2") - converted_instruction = self.converter(qobj) - self.assertIsInstance(converted_instruction, Schedule) - - bind_dict = {converted_instruction.get_parameters("p")[0]: 3.14} - evaluated_instruction = converted_instruction.assign_parameters(bind_dict) - - with self.assertWarns(DeprecationWarning): - instruction = SetPhase(3.14 / 2, MeasureChannel(0)) - self.assertEqual(evaluated_instruction.start_time, 0) - self.assertEqual(evaluated_instruction.duration, 0) - self.assertEqual(evaluated_instruction.instructions[0][-1], instruction) - - def test_set_frequency(self): - """Test converted qobj from SetFrequency.""" - with self.assertWarns(DeprecationWarning): - instruction = SetFrequency(8.0e9, DriveChannel(0)) - - qobj = PulseQobjInstruction(name="setf", ch="d0", t0=0, frequency=8.0) - converted_instruction = self.converter(qobj) - - self.assertEqual(converted_instruction.start_time, 0) - self.assertEqual(converted_instruction.duration, 0) - self.assertEqual(converted_instruction.instructions[0][-1], instruction) - self.assertTrue("frequency" in qobj.to_dict()) - - def test_parameterized_set_frequency(self): - """Test converted qobj from SetFrequency, when passing a parameterized frequency.""" - with self.assertWarns(DeprecationWarning): - qobj = PulseQobjInstruction(name="setf", ch="d0", t0=2, frequency="f") - self.assertTrue("frequency" in qobj.to_dict()) - - with self.assertWarns(DeprecationWarning): - converted_instruction = self.converter(qobj) - self.assertIsInstance(converted_instruction, Schedule) - - bind_dict = {converted_instruction.get_parameters("f")[0]: 2.0} - evaluated_instruction = converted_instruction.assign_parameters(bind_dict) - - with self.assertWarns(DeprecationWarning): - instruction = SetFrequency(2.0e9, DriveChannel(0)) - - self.assertEqual(evaluated_instruction.start_time, 2) - self.assertEqual(evaluated_instruction.duration, 2) - self.assertEqual(evaluated_instruction.instructions[0][-1], instruction) - - def test_shift_frequency(self): - """Test converted qobj from ShiftFrequency.""" - with self.assertWarns(DeprecationWarning): - instruction = ShiftFrequency(8.0e9, DriveChannel(0)) - - qobj = PulseQobjInstruction(name="shiftf", ch="d0", t0=0, frequency=8.0) - converted_instruction = self.converter(qobj) - - self.assertEqual(converted_instruction.start_time, 0) - self.assertEqual(converted_instruction.duration, 0) - self.assertEqual(converted_instruction.instructions[0][-1], instruction) - self.assertTrue("frequency" in qobj.to_dict()) - - def test_parameterized_shift_frequency(self): - """Test converted qobj from ShiftFrequency, with a parameterized frequency.""" - with self.assertWarns(DeprecationWarning): - qobj = PulseQobjInstruction(name="shiftf", ch="d0", t0=1, frequency="f / 1000") - self.assertTrue("frequency" in qobj.to_dict()) - - with self.assertWarns(DeprecationWarning): - converted_instruction = self.converter(qobj) - self.assertIsInstance(converted_instruction, Schedule) - - bind_dict = {converted_instruction.get_parameters("f")[0]: 3.14} - evaluated_instruction = converted_instruction.assign_parameters(bind_dict) - - with self.assertWarns(DeprecationWarning): - instruction = ShiftFrequency(3.14e6, DriveChannel(0)) - - self.assertEqual(evaluated_instruction.start_time, 1) - self.assertEqual(evaluated_instruction.duration, 1) - self.assertEqual(evaluated_instruction.instructions[0][-1], instruction) - - def test_delay(self): - """Test converted qobj from Delay.""" - with self.assertWarns(DeprecationWarning): - instruction = Delay(10, DriveChannel(0)) - - qobj = PulseQobjInstruction(name="delay", ch="d0", t0=0, duration=10) - converted_instruction = self.converter(qobj) - - self.assertTrue("delay" in qobj.to_dict().values()) - self.assertEqual(converted_instruction.duration, instruction.duration) - self.assertEqual(converted_instruction.instructions[0][-1], instruction) - - def test_acquire(self): - """Test converted qobj from Acquire.""" - with self.assertWarns(DeprecationWarning): - schedule = Schedule() - for i in range(self.num_qubits): - schedule |= Acquire( - 10, - AcquireChannel(i), - MemorySlot(i), - RegisterSlot(i), - kernel=Kernel(name="test_kern", test_params="test"), - discriminator=Discriminator(name="test_disc", test_params=1.0), - ) - qobj = PulseQobjInstruction( - name="acquire", - t0=0, - duration=10, - qubits=[0, 1], - memory_slot=[0, 1], - register_slot=[0, 1], - kernels=[QobjMeasurementOption(name="test_kern", params={"test_params": "test"})], - discriminators=[ - QobjMeasurementOption(name="test_disc", params={"test_params": 1.0}) - ], - ) - with self.assertWarns(DeprecationWarning): - converted_instruction = self.converter(qobj) - - self.assertEqual(converted_instruction.start_time, 0) - self.assertEqual(converted_instruction.duration, 10) - self.assertEqual(converted_instruction.instructions[0][-1].duration, 10) - self.assertEqual( - converted_instruction.instructions[0][-1].kernel.params, {"test_params": "test"} - ) - with self.assertWarns(DeprecationWarning): - self.assertEqual(converted_instruction.instructions[1][-1].channel, AcquireChannel(1)) - - def test_snapshot(self): - """Test converted qobj from SnapShot.""" - with self.assertWarns(DeprecationWarning): - instruction = Snapshot(label="label", snapshot_type="type") - shifted = instruction << 10 - - qobj = PulseQobjInstruction(name="snapshot", t0=10, label="label", type="type") - converted_instruction = self.converter(qobj) - - self.assertEqual(converted_instruction.start_time, shifted.start_time) - self.assertEqual(converted_instruction.duration, shifted.duration) - self.assertEqual(converted_instruction.instructions[0][-1], instruction) - - def test_instruction_name_collision(self): - """Avoid command name collision of pulse library items.""" - with self.assertWarns(DeprecationWarning): - pulse_library_from_backend_x = [ - PulseLibraryItem(name="pulse123", samples=[0.1, 0.1, 0.1]), - PulseLibraryItem(name="pulse456", samples=[0.3, 0.3, 0.3]), - ] - converter_of_backend_x = QobjToInstructionConverter( - pulse_library_from_backend_x, buffer=0 - ) - - pulse_library_from_backend_y = [ - PulseLibraryItem(name="pulse123", samples=[0.2, 0.2, 0.2]) - ] - converter_of_backend_y = QobjToInstructionConverter( - pulse_library_from_backend_y, buffer=0 - ) - - qobj1 = PulseQobjInstruction(name="pulse123", qubits=[0], t0=0, ch="d0") - qobj2 = PulseQobjInstruction(name="pulse456", qubits=[0], t0=0, ch="d0") - - sched_out_x = converter_of_backend_x(qobj1) - sched_out_y = converter_of_backend_y(qobj1) - - # pulse123 have different definition on backend-x and backend-y - self.assertNotEqual(sched_out_x, sched_out_y) - - with self.assertRaises(QiskitError): - with self.assertWarns(DeprecationWarning): - # This should not exist in backend-y command namespace. - converter_of_backend_y(qobj2) - - -class TestLoConverter(QiskitTestCase): - """LO converter tests.""" - - def test_qubit_los(self): - """Test qubit channel configuration.""" - with self.assertWarns(DeprecationWarning): - user_lo_config = LoConfig({DriveChannel(0): 1.3e9}) - converter = LoConfigConverter( - PulseQobjExperimentConfig, [1.2e9], [3.4e9], [(0.0, 5e9)], [(0.0, 5e9)] - ) - valid_qobj = PulseQobjExperimentConfig(qubit_lo_freq=[1.3]) - - with self.assertWarns(DeprecationWarning): - self.assertEqual(converter(user_lo_config), valid_qobj) - - def test_meas_los(self): - """Test measurement channel configuration.""" - with self.assertWarns(DeprecationWarning): - user_lo_config = LoConfig({MeasureChannel(0): 3.5e9}) - converter = LoConfigConverter( - PulseQobjExperimentConfig, [1.2e9], [3.4e9], [(0.0, 5e9)], [(0.0, 5e9)] - ) - - valid_qobj = PulseQobjExperimentConfig(meas_lo_freq=[3.5]) - - self.assertEqual(converter(user_lo_config), valid_qobj) diff --git a/test/python/qobj/test_qobj.py b/test/python/qobj/test_qobj.py deleted file mode 100644 index 21950b0a1486..000000000000 --- a/test/python/qobj/test_qobj.py +++ /dev/null @@ -1,435 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# 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. - - -"""Qobj tests.""" - -import copy - -from qiskit.qobj import ( - QasmQobj, - PulseQobj, - QobjHeader, - PulseQobjInstruction, - PulseQobjExperiment, - PulseQobjConfig, - QobjMeasurementOption, - PulseLibraryItem, - QasmQobjInstruction, - QasmQobjExperiment, - QasmQobjConfig, - QasmExperimentCalibrations, - GateCalibration, -) - -from test import QiskitTestCase # pylint: disable=wrong-import-order - - -class TestQASMQobj(QiskitTestCase): - """Tests for QasmQobj.""" - - def setUp(self): - super().setUp() - with self.assertWarns(DeprecationWarning): - self.valid_qobj = QasmQobj( - qobj_id="12345", - header=QobjHeader(), - config=QasmQobjConfig(shots=1024, memory_slots=2), - experiments=[ - QasmQobjExperiment( - instructions=[ - QasmQobjInstruction(name="u1", qubits=[1], params=[0.4]), - QasmQobjInstruction(name="u2", qubits=[1], params=[0.4, 0.2]), - ] - ) - ], - ) - - self.valid_dict = { - "qobj_id": "12345", - "type": "QASM", - "schema_version": "1.2.0", - "header": {}, - "config": {"memory_slots": 2, "shots": 1024}, - "experiments": [ - { - "instructions": [ - {"name": "u1", "params": [0.4], "qubits": [1]}, - {"name": "u2", "params": [0.4, 0.2], "qubits": [1]}, - ] - } - ], - } - with self.assertWarns(DeprecationWarning): - self.bad_qobj = copy.deepcopy(self.valid_qobj) - self.bad_qobj.experiments = [] - - def test_from_dict_per_class(self): - """Test Qobj and its subclass representations given a dictionary.""" - with self.assertWarns(DeprecationWarning): - test_parameters = { - QasmQobj: (self.valid_qobj, self.valid_dict), - QasmQobjConfig: ( - QasmQobjConfig(shots=1, memory_slots=2), - {"shots": 1, "memory_slots": 2}, - ), - QasmQobjExperiment: ( - QasmQobjExperiment( - instructions=[QasmQobjInstruction(name="u1", qubits=[1], params=[0.4])] - ), - {"instructions": [{"name": "u1", "qubits": [1], "params": [0.4]}]}, - ), - QasmQobjInstruction: ( - QasmQobjInstruction(name="u1", qubits=[1], params=[0.4]), - {"name": "u1", "qubits": [1], "params": [0.4]}, - ), - } - - for qobj_class, (qobj_item, expected_dict) in test_parameters.items(): - with self.subTest(msg=str(qobj_class)): - with self.assertWarns(DeprecationWarning): - qobj = qobj_class.from_dict(expected_dict) - self.assertEqual(qobj_item, qobj) - - def test_snapshot_instruction_to_dict(self): - """Test snapshot instruction to dict.""" - with self.assertWarns(DeprecationWarning): - valid_qobj = QasmQobj( - qobj_id="12345", - header=QobjHeader(), - config=QasmQobjConfig(shots=1024, memory_slots=2), - experiments=[ - QasmQobjExperiment( - instructions=[ - QasmQobjInstruction(name="u1", qubits=[1], params=[0.4]), - QasmQobjInstruction(name="u2", qubits=[1], params=[0.4, 0.2]), - QasmQobjInstruction( - name="snapshot", - qubits=[1], - snapshot_type="statevector", - label="my_snap", - ), - ] - ) - ], - ) - res = valid_qobj.to_dict() - expected_dict = { - "qobj_id": "12345", - "type": "QASM", - "schema_version": "1.3.0", - "header": {}, - "config": {"memory_slots": 2, "shots": 1024}, - "experiments": [ - { - "instructions": [ - {"name": "u1", "params": [0.4], "qubits": [1]}, - {"name": "u2", "params": [0.4, 0.2], "qubits": [1]}, - { - "name": "snapshot", - "qubits": [1], - "snapshot_type": "statevector", - "label": "my_snap", - }, - ], - "config": {}, - "header": {}, - } - ], - } - self.assertEqual(expected_dict, res) - - def test_snapshot_instruction_from_dict(self): - """Test snapshot instruction from dict.""" - with self.assertWarns(DeprecationWarning): - expected_qobj = QasmQobj( - qobj_id="12345", - header=QobjHeader(), - config=QasmQobjConfig(shots=1024, memory_slots=2), - experiments=[ - QasmQobjExperiment( - instructions=[ - QasmQobjInstruction(name="u1", qubits=[1], params=[0.4]), - QasmQobjInstruction(name="u2", qubits=[1], params=[0.4, 0.2]), - QasmQobjInstruction( - name="snapshot", - qubits=[1], - snapshot_type="statevector", - label="my_snap", - ), - ] - ) - ], - ) - qobj_dict = { - "qobj_id": "12345", - "type": "QASM", - "schema_version": "1.2.0", - "header": {}, - "config": {"memory_slots": 2, "shots": 1024}, - "experiments": [ - { - "instructions": [ - {"name": "u1", "params": [0.4], "qubits": [1]}, - {"name": "u2", "params": [0.4, 0.2], "qubits": [1]}, - { - "name": "snapshot", - "qubits": [1], - "snapshot_type": "statevector", - "label": "my_snap", - }, - ] - } - ], - } - with self.assertWarns(DeprecationWarning): - qobj = QasmQobj.from_dict(qobj_dict) - self.assertEqual(expected_qobj, qobj) - - def test_gate_calibrations_to_dict(self): - """Test gate calibrations to dict.""" - with self.assertWarns(DeprecationWarning): - pulse_library = [PulseLibraryItem(name="test", samples=[1j, 1j])] - with self.assertWarns(DeprecationWarning): - valid_qobj = QasmQobj( - qobj_id="12345", - header=QobjHeader(), - config=QasmQobjConfig(shots=1024, memory_slots=2, pulse_library=pulse_library), - experiments=[ - QasmQobjExperiment( - instructions=[QasmQobjInstruction(name="u1", qubits=[1], params=[0.4])], - config=QasmQobjConfig( - calibrations=QasmExperimentCalibrations( - gates=[ - GateCalibration( - name="u1", qubits=[1], params=[0.4], instructions=[] - ) - ] - ) - ), - ) - ], - ) - res = valid_qobj.to_dict() - expected_dict = { - "qobj_id": "12345", - "type": "QASM", - "schema_version": "1.3.0", - "header": {}, - "config": { - "memory_slots": 2, - "shots": 1024, - "pulse_library": [{"name": "test", "samples": [1j, 1j]}], - }, - "experiments": [ - { - "instructions": [{"name": "u1", "params": [0.4], "qubits": [1]}], - "config": { - "calibrations": { - "gates": [ - {"name": "u1", "qubits": [1], "params": [0.4], "instructions": []} - ] - } - }, - "header": {}, - } - ], - } - self.assertEqual(expected_dict, res) - - -class TestPulseQobj(QiskitTestCase): - """Tests for PulseQobj.""" - - def setUp(self): - super().setUp() - with self.assertWarns(DeprecationWarning): - self.valid_qobj = PulseQobj( - qobj_id="12345", - header=QobjHeader(), - config=PulseQobjConfig( - shots=1024, - memory_slots=2, - meas_level=1, - memory_slot_size=8192, - meas_return="avg", - pulse_library=[ - PulseLibraryItem( - name="pulse0", samples=[0.0 + 0.0j, 0.5 + 0.0j, 0.0 + 0.0j] - ) - ], - qubit_lo_freq=[4.9], - meas_lo_freq=[6.9], - rep_time=1000, - ), - experiments=[ - PulseQobjExperiment( - instructions=[ - PulseQobjInstruction(name="pulse0", t0=0, ch="d0"), - PulseQobjInstruction(name="fc", t0=5, ch="d0", phase=1.57), - PulseQobjInstruction(name="fc", t0=5, ch="d0", phase=0.0), - PulseQobjInstruction(name="fc", t0=5, ch="d0", phase="P1"), - PulseQobjInstruction(name="setp", t0=10, ch="d0", phase=3.14), - PulseQobjInstruction(name="setf", t0=10, ch="d0", frequency=8.0), - PulseQobjInstruction(name="shiftf", t0=10, ch="d0", frequency=4.0), - PulseQobjInstruction( - name="acquire", - t0=15, - duration=5, - qubits=[0], - memory_slot=[0], - kernels=[ - QobjMeasurementOption( - name="boxcar", params={"start_window": 0, "stop_window": 5} - ) - ], - ), - ] - ) - ], - ) - self.valid_dict = { - "qobj_id": "12345", - "type": "PULSE", - "schema_version": "1.2.0", - "header": {}, - "config": { - "memory_slots": 2, - "shots": 1024, - "meas_level": 1, - "memory_slot_size": 8192, - "meas_return": "avg", - "pulse_library": [{"name": "pulse0", "samples": [0, 0.5, 0]}], - "qubit_lo_freq": [4.9], - "meas_lo_freq": [6.9], - "rep_time": 1000, - }, - "experiments": [ - { - "instructions": [ - {"name": "pulse0", "t0": 0, "ch": "d0"}, - {"name": "fc", "t0": 5, "ch": "d0", "phase": 1.57}, - {"name": "fc", "t0": 5, "ch": "d0", "phase": 0}, - {"name": "fc", "t0": 5, "ch": "d0", "phase": "P1"}, - {"name": "setp", "t0": 10, "ch": "d0", "phase": 3.14}, - {"name": "setf", "t0": 10, "ch": "d0", "frequency": 8.0}, - {"name": "shiftf", "t0": 10, "ch": "d0", "frequency": 4.0}, - { - "name": "acquire", - "t0": 15, - "duration": 5, - "qubits": [0], - "memory_slot": [0], - "kernels": [ - {"name": "boxcar", "params": {"start_window": 0, "stop_window": 5}} - ], - }, - ] - } - ], - } - - def test_from_dict_per_class(self): - """Test converting to Qobj and its subclass representations given a dictionary.""" - with self.assertWarns(DeprecationWarning): - test_parameters = { - PulseQobj: (self.valid_qobj, self.valid_dict), - PulseQobjConfig: ( - PulseQobjConfig( - meas_level=1, - memory_slot_size=8192, - meas_return="avg", - pulse_library=[PulseLibraryItem(name="pulse0", samples=[0.1 + 0.0j])], - qubit_lo_freq=[4.9], - meas_lo_freq=[6.9], - rep_time=1000, - ), - { - "meas_level": 1, - "memory_slot_size": 8192, - "meas_return": "avg", - "pulse_library": [{"name": "pulse0", "samples": [0.1 + 0j]}], - "qubit_lo_freq": [4.9], - "meas_lo_freq": [6.9], - "rep_time": 1000, - }, - ), - PulseLibraryItem: ( - PulseLibraryItem(name="pulse0", samples=[0.1 + 0.0j]), - {"name": "pulse0", "samples": [0.1 + 0j]}, - ), - PulseQobjExperiment: ( - PulseQobjExperiment( - instructions=[PulseQobjInstruction(name="pulse0", t0=0, ch="d0")] - ), - {"instructions": [{"name": "pulse0", "t0": 0, "ch": "d0"}]}, - ), - PulseQobjInstruction: ( - PulseQobjInstruction(name="pulse0", t0=0, ch="d0"), - {"name": "pulse0", "t0": 0, "ch": "d0"}, - ), - } - - for qobj_class, (qobj_item, expected_dict) in test_parameters.items(): - with self.subTest(msg=str(qobj_class)): - with self.assertWarns(DeprecationWarning): - qobj = qobj_class.from_dict(expected_dict) - self.assertEqual(qobj_item, qobj) - - def test_to_dict_per_class(self): - """Test converting from Qobj and its subclass representations given a dictionary.""" - with self.assertWarns(DeprecationWarning): - test_parameters = { - PulseQobj: (self.valid_qobj, self.valid_dict), - PulseQobjConfig: ( - PulseQobjConfig( - meas_level=1, - memory_slot_size=8192, - meas_return="avg", - pulse_library=[PulseLibraryItem(name="pulse0", samples=[0.1 + 0.0j])], - qubit_lo_freq=[4.9], - meas_lo_freq=[6.9], - rep_time=1000, - ), - { - "meas_level": 1, - "memory_slot_size": 8192, - "meas_return": "avg", - "pulse_library": [{"name": "pulse0", "samples": [0.1 + 0j]}], - "qubit_lo_freq": [4.9], - "meas_lo_freq": [6.9], - "rep_time": 1000, - }, - ), - PulseLibraryItem: ( - PulseLibraryItem(name="pulse0", samples=[0.1 + 0.0j]), - {"name": "pulse0", "samples": [0.1 + 0j]}, - ), - PulseQobjExperiment: ( - PulseQobjExperiment( - instructions=[PulseQobjInstruction(name="pulse0", t0=0, ch="d0")] - ), - {"instructions": [{"name": "pulse0", "t0": 0, "ch": "d0"}]}, - ), - PulseQobjInstruction: ( - PulseQobjInstruction(name="pulse0", t0=0, ch="d0"), - {"name": "pulse0", "t0": 0, "ch": "d0"}, - ), - } - - for qobj_class, (qobj_item, expected_dict) in test_parameters.items(): - with self.subTest(msg=str(qobj_class)): - self.assertEqual(qobj_item.to_dict(), expected_dict) - - -def _nop(): - pass diff --git a/test/python/result/test_result.py b/test/python/result/test_result.py index 89539487158c..f36b19f5660f 100644 --- a/test/python/result/test_result.py +++ b/test/python/result/test_result.py @@ -18,7 +18,6 @@ from qiskit.result import marginal_counts from qiskit.result import marginal_distribution from qiskit.result import Result -from qiskit.qobj import QobjExperimentHeader from qiskit.exceptions import QiskitError from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -30,7 +29,6 @@ def setUp(self): self.base_result_args = { "backend_name": "test_backend", "backend_version": "1.0.0", - "qobj_id": "id-123", "job_id": "job-123", "success": True, } @@ -42,8 +40,7 @@ def generate_qiskit_result(self): memory = [hex(ii) for ii in range(8)] counts = {m: 1 for m in memory} data_1 = models.ExperimentResultData(counts=counts, memory=memory) - with self.assertWarns(DeprecationWarning): - exp_result_header_1 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) + exp_result_header_1 = {"creg_sizes": [["c0", 4]], "memory_slots": 4} exp_result_1 = models.ExperimentResult( shots=8, success=True, data=data_1, header=exp_result_header_1 ) @@ -68,10 +65,7 @@ def test_counts_header(self): raw_counts = {"0x0": 4, "0x2": 10} processed_counts = {"0 0 00": 4, "0 0 10": 10} data = models.ExperimentResultData(counts=raw_counts) - with self.assertWarns(DeprecationWarning): - exp_result_header = QobjExperimentHeader( - creg_sizes=[["c0", 2], ["c0", 1], ["c1", 1]], memory_slots=4 - ) + exp_result_header = {"creg_sizes": [["c0", 2], ["c0", 1], ["c1", 1]], "memory_slots": 4} exp_result = models.ExperimentResult( shots=14, success=True, meas_level=2, data=data, header=exp_result_header ) @@ -84,10 +78,11 @@ def test_counts_by_name(self): raw_counts = {"0x0": 4, "0x2": 10} processed_counts = {"0 0 00": 4, "0 0 10": 10} data = models.ExperimentResultData(counts=raw_counts) - with self.assertWarns(DeprecationWarning): - exp_result_header = QobjExperimentHeader( - creg_sizes=[["c0", 2], ["c0", 1], ["c1", 1]], memory_slots=4, name="a_name" - ) + exp_result_header = { + "creg_sizes": [["c0", 2], ["c0", 1], ["c1", 1]], + "memory_slots": 4, + "name": "a_name", + } exp_result = models.ExperimentResult( shots=14, success=True, meas_level=2, data=data, header=exp_result_header ) @@ -98,8 +93,7 @@ def test_counts_by_name(self): def test_counts_duplicate_name(self): """Test results containing multiple entries of a single name will warn.""" data = models.ExperimentResultData(counts={}) - with self.assertWarns(DeprecationWarning): - exp_result_header = QobjExperimentHeader(name="foo") + exp_result_header = {"name": "foo"} exp_result = models.ExperimentResult( shots=14, success=True, data=data, header=exp_result_header ) @@ -112,21 +106,17 @@ def test_result_repr(self): """Test that repr is constructed correctly for a results object.""" raw_counts = {"0x0": 4, "0x2": 10} data = models.ExperimentResultData(counts=raw_counts) - with self.assertWarns(DeprecationWarning): - exp_result_header = QobjExperimentHeader( - creg_sizes=[["c0", 2], ["c0", 1], ["c1", 1]], memory_slots=4 - ) + exp_result_header = {"creg_sizes": [["c0", 2], ["c0", 1], ["c1", 1]], "memory_slots": 4} exp_result = models.ExperimentResult( shots=14, success=True, meas_level=2, data=data, header=exp_result_header ) result = Result(results=[exp_result], **self.base_result_args) expected = ( "Result(backend_name='test_backend', backend_version='1.0.0', " - "qobj_id='id-123', job_id='job-123', success=True, " - "results=[ExperimentResult(shots=14, success=True, " - "meas_level=2, data=ExperimentResultData(counts={'0x0': 4," - " '0x2': 10}), header=QobjExperimentHeader(creg_sizes=" - "[['c0', 2], ['c0', 1], ['c1', 1]], memory_slots=4))], date=None, " + "job_id='job-123', success=True, results=[ExperimentResult(" + "shots=14, success=True, meas_level=2, data=ExperimentResultData(" + "counts={'0x0': 4, '0x2': 10}), header={'creg_sizes': [['c0', 2], " + "['c0', 1], ['c1', 1]], 'memory_slots': 4})], date=None, " "status=None, header=None)" ) self.assertEqual(expected, repr(result)) @@ -142,8 +132,7 @@ def test_multiple_circuits_counts(self): raw_counts_1 = {"0x0": 5, "0x3": 12, "0x5": 9, "0xD": 6, "0xE": 2} processed_counts_1 = {"0000": 5, "0011": 12, "0101": 9, "1101": 6, "1110": 2} data_1 = models.ExperimentResultData(counts=raw_counts_1) - with self.assertWarns(DeprecationWarning): - exp_result_header_1 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) + exp_result_header_1 = {"creg_sizes": [["c0", 4]], "memory_slots": 4} exp_result_1 = models.ExperimentResult( shots=14, success=True, meas_level=2, data=data_1, header=exp_result_header_1 ) @@ -151,8 +140,7 @@ def test_multiple_circuits_counts(self): raw_counts_2 = {"0x1": 0, "0x4": 3, "0x6": 6, "0xA": 1, "0xB": 2} processed_counts_2 = {"0001": 0, "0100": 3, "0110": 6, "1010": 1, "1011": 2} data_2 = models.ExperimentResultData(counts=raw_counts_2) - with self.assertWarns(DeprecationWarning): - exp_result_header_2 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) + exp_result_header_2 = {"creg_sizes": [["c0", 4]], "memory_slots": 4} exp_result_2 = models.ExperimentResult( shots=14, success=True, meas_level=2, data=data_2, header=exp_result_header_2 ) @@ -160,8 +148,7 @@ def test_multiple_circuits_counts(self): raw_counts_3 = {"0xC": 27, "0xF": 20} processed_counts_3 = {"1100": 27, "1111": 20} data_3 = models.ExperimentResultData(counts=raw_counts_3) - with self.assertWarns(DeprecationWarning): - exp_result_header_3 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) + exp_result_header_3 = {"creg_sizes": [["c0", 4]], "memory_slots": 4} exp_result_3 = models.ExperimentResult( shots=14, success=True, meas_level=2, data=data_3, header=exp_result_header_3 ) @@ -180,8 +167,7 @@ def test_marginal_counts(self): """Test that counts are marginalized correctly.""" raw_counts = {"0x0": 4, "0x1": 7, "0x2": 10, "0x6": 5, "0x9": 11, "0xD": 9, "0xE": 8} data = models.ExperimentResultData(counts=raw_counts) - with self.assertWarns(DeprecationWarning): - exp_result_header = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) + exp_result_header = {"creg_sizes": [["c0", 4]], "memory_slots": 4} exp_result = models.ExperimentResult( shots=54, success=True, data=data, header=exp_result_header ) @@ -195,8 +181,7 @@ def test_marginal_distribution(self): """Test that counts are marginalized correctly.""" raw_counts = {"0x0": 4, "0x1": 7, "0x2": 10, "0x6": 5, "0x9": 11, "0xD": 9, "0xE": 8} data = models.ExperimentResultData(counts=raw_counts) - with self.assertWarns(DeprecationWarning): - exp_result_header = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) + exp_result_header = {"creg_sizes": [["c0", 4]], "memory_slots": 4} exp_result = models.ExperimentResult( shots=54, success=True, data=data, header=exp_result_header ) @@ -210,10 +195,7 @@ def test_marginal_distribution(self): self.assertEqual(marginal_distribution(result.get_counts(), [1, 0]), expected_reverse) # test with register spacing, bitstrings are in form of "00 00" for register split data = models.ExperimentResultData(counts=raw_counts) - with self.assertWarns(DeprecationWarning): - exp_result_header = QobjExperimentHeader( - creg_sizes=[["c0", 2], ["c1", 2]], memory_slots=4 - ) + exp_result_header = {"creg_sizes": [["c0", 2], ["c1", 2]], "memory_slots": 4} exp_result = models.ExperimentResult( shots=54, success=True, data=data, header=exp_result_header ) @@ -227,16 +209,14 @@ def test_marginal_counts_result(self): """Test that a Result object containing counts marginalizes correctly.""" raw_counts_1 = {"0x0": 4, "0x1": 7, "0x2": 10, "0x6": 5, "0x9": 11, "0xD": 9, "0xE": 8} data_1 = models.ExperimentResultData(counts=raw_counts_1) - with self.assertWarns(DeprecationWarning): - exp_result_header_1 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) + exp_result_header_1 = {"creg_sizes": [["c0", 4]], "memory_slots": 4} exp_result_1 = models.ExperimentResult( shots=54, success=True, data=data_1, header=exp_result_header_1 ) raw_counts_2 = {"0x2": 5, "0x3": 8} data_2 = models.ExperimentResultData(counts=raw_counts_2) - with self.assertWarns(DeprecationWarning): - exp_result_header_2 = QobjExperimentHeader(creg_sizes=[["c0", 2]], memory_slots=2) + exp_result_header_2 = {"creg_sizes": [["c0", 2]], "memory_slots": 2} exp_result_2 = models.ExperimentResult( shots=13, success=True, data=data_2, header=exp_result_header_2 ) @@ -255,20 +235,14 @@ def test_marginal_counts_result(self): "1110": 8, } - with self.assertWarns(DeprecationWarning): - self.assertEqual( - marginal_counts(result, [0, 1]).get_counts(0), expected_marginal_counts_1 - ) - self.assertEqual(marginal_counts(result, [0]).get_counts(1), expected_marginal_counts_2) - self.assertEqual( - marginal_counts(result, None).get_counts(0), expected_marginal_counts_none - ) + self.assertEqual(marginal_counts(result, [0, 1]).get_counts(0), expected_marginal_counts_1) + self.assertEqual(marginal_counts(result, [0]).get_counts(1), expected_marginal_counts_2) + self.assertEqual(marginal_counts(result, None).get_counts(0), expected_marginal_counts_none) def test_marginal_counts_result_memory(self): """Test that a Result object containing memory marginalizes correctly.""" result = self.generate_qiskit_result() - with self.assertWarns(DeprecationWarning): - marginal_result = marginal_counts(result, indices=[0]) + marginal_result = marginal_counts(result, indices=[0]) marginal_memory = marginal_result.results[0].data.memory self.assertEqual(marginal_memory, [hex(ii % 2) for ii in range(8)]) @@ -276,8 +250,7 @@ def test_marginal_counts_result_memory_nonzero_indices(self): """Test that a Result object containing memory marginalizes correctly.""" result = self.generate_qiskit_result() index = 2 - with self.assertWarns(DeprecationWarning): - marginal_result = marginal_counts(result, indices=[index]) + marginal_result = marginal_counts(result, indices=[index]) marginal_memory = marginal_result.results[0].data.memory mask = 1 << index expected = [hex((ii & mask) >> index) for ii in range(8)] @@ -288,8 +261,7 @@ def test_marginal_counts_result_memory_indices_None(self): result = self.generate_qiskit_result() memory = "should not be touched" result.results[0].data.memory = memory - with self.assertWarns(DeprecationWarning): - marginal_result = marginal_counts(result, indices=None) + marginal_result = marginal_counts(result, indices=None) marginal_memory = marginal_result.results[0].data.memory self.assertEqual(marginal_memory, memory) @@ -320,20 +292,17 @@ def test_marginal_counts_result_marginalize_memory(self): self.assertTrue(hasattr(marginal_result.results[0].data, "memory")) result = self.generate_qiskit_result() - with self.assertWarns(DeprecationWarning): - marginal_result = marginal_counts( - result, indices=[0], inplace=False, marginalize_memory=False - ) + marginal_result = marginal_counts( + result, indices=[0], inplace=False, marginalize_memory=False + ) self.assertFalse(hasattr(marginal_result.results[0].data, "memory")) - with self.assertWarns(DeprecationWarning): - marginal_result = marginal_counts( - result, indices=[0], inplace=False, marginalize_memory=None - ) + marginal_result = marginal_counts( + result, indices=[0], inplace=False, marginalize_memory=None + ) self.assertTrue(hasattr(marginal_result.results[0].data, "memory")) - with self.assertWarns(DeprecationWarning): - marginal_result = marginal_counts( - result, indices=[0], inplace=False, marginalize_memory=True - ) + marginal_result = marginal_counts( + result, indices=[0], inplace=False, marginalize_memory=True + ) self.assertTrue(hasattr(marginal_result.results[0].data, "memory")) def test_marginal_counts_result_inplace(self): @@ -349,10 +318,7 @@ def test_marginal_counts_result_creg_sizes(self): """Test that marginal_counts with Result input properly changes creg_sizes.""" raw_counts = {"0x0": 4, "0x1": 7, "0x2": 10, "0x6": 5, "0x9": 11, "0xD": 9, "0xE": 8} data = models.ExperimentResultData(counts=raw_counts) - with self.assertWarns(DeprecationWarning): - exp_result_header = QobjExperimentHeader( - creg_sizes=[["c0", 1], ["c1", 3]], memory_slots=4 - ) + exp_result_header = {"creg_sizes": [["c0", 1], ["c1", 3]], "memory_slots": 4} exp_result = models.ExperimentResult( shots=54, success=True, data=data, header=exp_result_header ) @@ -362,11 +328,12 @@ def test_marginal_counts_result_creg_sizes(self): expected_marginal_counts = {"0 0": 14, "0 1": 18, "1 0": 13, "1 1": 9} expected_creg_sizes = [["c0", 1], ["c1", 1]] expected_memory_slots = 2 - with self.assertWarns(DeprecationWarning): - marginal_counts_result = marginal_counts(result, [0, 2]) - self.assertEqual(marginal_counts_result.results[0].header.creg_sizes, expected_creg_sizes) + marginal_counts_result = marginal_counts(result, [0, 2]) + self.assertEqual( + marginal_counts_result.results[0].header["creg_sizes"], expected_creg_sizes + ) self.assertEqual( - marginal_counts_result.results[0].header.memory_slots, expected_memory_slots + marginal_counts_result.results[0].header["memory_slots"], expected_memory_slots ) self.assertEqual(marginal_counts_result.get_counts(0), expected_marginal_counts) @@ -374,10 +341,7 @@ def test_marginal_counts_result_format(self): """Test that marginal_counts with format_marginal true properly formats output.""" raw_counts_1 = {"0x0": 4, "0x1": 7, "0x2": 10, "0x6": 5, "0x9": 11, "0xD": 9, "0x12": 8} data_1 = models.ExperimentResultData(counts=raw_counts_1) - with self.assertWarns(DeprecationWarning): - exp_result_header_1 = QobjExperimentHeader( - creg_sizes=[["c0", 2], ["c1", 3]], memory_slots=5 - ) + exp_result_header_1 = {"creg_sizes": [["c0", 2], ["c1", 3]], "memory_slots": 5} exp_result_1 = models.ExperimentResult( shots=54, success=True, data=data_1, header=exp_result_header_1 ) @@ -400,16 +364,14 @@ def test_marginal_counts_inplace_true(self): """Test marginal_counts(Result, inplace = True)""" raw_counts_1 = {"0x0": 4, "0x1": 7, "0x2": 10, "0x6": 5, "0x9": 11, "0xD": 9, "0xE": 8} data_1 = models.ExperimentResultData(counts=raw_counts_1) - with self.assertWarns(DeprecationWarning): - exp_result_header_1 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) + exp_result_header_1 = {"creg_sizes": [["c0", 4]], "memory_slots": 4} exp_result_1 = models.ExperimentResult( shots=54, success=True, data=data_1, header=exp_result_header_1 ) raw_counts_2 = {"0x2": 5, "0x3": 8} data_2 = models.ExperimentResultData(counts=raw_counts_2) - with self.assertWarns(DeprecationWarning): - exp_result_header_2 = QobjExperimentHeader(creg_sizes=[["c0", 2]], memory_slots=2) + exp_result_header_2 = {"creg_sizes": [["c0", 2]], "memory_slots": 2} exp_result_2 = models.ExperimentResult( shots=13, success=True, data=data_2, header=exp_result_header_2 ) @@ -427,16 +389,14 @@ def test_marginal_counts_inplace_false(self): """Test marginal_counts(Result, inplace=False)""" raw_counts_1 = {"0x0": 4, "0x1": 7, "0x2": 10, "0x6": 5, "0x9": 11, "0xD": 9, "0xE": 8} data_1 = models.ExperimentResultData(counts=raw_counts_1) - with self.assertWarns(DeprecationWarning): - exp_result_header_1 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) + exp_result_header_1 = {"creg_sizes": [["c0", 4]], "memory_slots": 4} exp_result_1 = models.ExperimentResult( shots=54, success=True, data=data_1, header=exp_result_header_1 ) raw_counts_2 = {"0x2": 5, "0x3": 8} data_2 = models.ExperimentResultData(counts=raw_counts_2) - with self.assertWarns(DeprecationWarning): - exp_result_header_2 = QobjExperimentHeader(creg_sizes=[["c0", 2]], memory_slots=2) + exp_result_header_2 = {"creg_sizes": [["c0", 2]], "memory_slots": 2} exp_result_2 = models.ExperimentResult( shots=13, success=True, data=data_2, header=exp_result_header_2 ) @@ -445,10 +405,9 @@ def test_marginal_counts_inplace_false(self): expected_marginal_counts = {"0": 27, "1": 27} - with self.assertWarns(DeprecationWarning): - self.assertEqual( - marginal_counts(result, [0], inplace=False).get_counts(0), expected_marginal_counts - ) + self.assertEqual( + marginal_counts(result, [0], inplace=False).get_counts(0), expected_marginal_counts + ) self.assertNotEqual(result.get_counts(0), expected_marginal_counts) def test_marginal_counts_with_dict(self): @@ -501,10 +460,7 @@ def test_memory_counts_header(self): "0 0 10", ] data = models.ExperimentResultData(memory=raw_memory) - with self.assertWarns(DeprecationWarning): - exp_result_header = QobjExperimentHeader( - creg_sizes=[["c0", 2], ["c0", 1], ["c1", 1]], memory_slots=4 - ) + exp_result_header = {"creg_sizes": [["c0", 2], ["c0", 1], ["c1", 1]], "memory_slots": 4} exp_result = models.ExperimentResult( shots=14, success=True, meas_level=2, memory=True, data=data, header=exp_result_header ) @@ -717,7 +673,6 @@ def setUp(self): self.base_result_args = { "backend_name": "test_backend", "backend_version": "1.0.0", - "qobj_id": "id-123", "job_id": "job-123", "success": True, } @@ -740,10 +695,11 @@ def test_counts_name_out(self): """Test that fails when get_count is called with a nonexistent name.""" raw_counts = {"0x0": 4, "0x2": 10} data = models.ExperimentResultData(counts=raw_counts) - with self.assertWarns(DeprecationWarning): - exp_result_header = QobjExperimentHeader( - creg_sizes=[["c0", 2], ["c0", 1], ["c1", 1]], memory_slots=4, name="a_name" - ) + exp_result_header = { + "creg_sizes": [["c0", 2], ["c0", 1], ["c1", 1]], + "memory_slots": 4, + "name": "a_name", + } exp_result = models.ExperimentResult( shots=14, success=True, meas_level=2, data=data, header=exp_result_header ) @@ -774,15 +730,13 @@ def test_marginal_counts_no_cregs(self): """Test that marginal_counts without cregs See qiskit-terra/6430.""" raw_counts_1 = {"0x0": 4, "0x1": 7, "0x2": 10, "0x6": 5, "0x9": 11, "0xD": 9, "0x12": 8} data_1 = models.ExperimentResultData(counts=raw_counts_1) - with self.assertWarns(DeprecationWarning): - exp_result_header_1 = QobjExperimentHeader(memory_slots=5) + exp_result_header_1 = {"memory_slots": 5} exp_result_1 = models.ExperimentResult( shots=54, success=True, data=data_1, header=exp_result_header_1 ) result = Result(results=[exp_result_1], **self.base_result_args) - with self.assertWarns(DeprecationWarning): - _ = marginal_counts(result, indices=[0]) - marginal_counts_result = marginal_counts(result, indices=[0]) + _ = marginal_counts(result, indices=[0]) + marginal_counts_result = marginal_counts(result, indices=[0]) self.assertEqual(marginal_counts_result.get_counts(), {"0": 27, "1": 27}) diff --git a/test/python/transpiler/test_preset_passmanagers.py b/test/python/transpiler/test_preset_passmanagers.py index 18e10c2ba3a2..cda09a6e169a 100644 --- a/test/python/transpiler/test_preset_passmanagers.py +++ b/test/python/transpiler/test_preset_passmanagers.py @@ -712,7 +712,7 @@ class TestInitialLayouts(QiskitTestCase): @data(0, 1, 2, 3) def test_layout_1711(self, level): """Test that a user-given initial layout is respected - in the qobj. + in the output. See: https://github.com/Qiskit/qiskit-terra/issues/1711 """