From 4c6eed9b6ad4204b306c51600f0ac0a7e0b81a5a Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Tue, 9 Jan 2024 14:23:45 +0000 Subject: [PATCH 1/9] Fix broadcasting of `QuantumCircuit.delay` (#11447) The object is supposed to "broadcast" like a gate; one instruction per qubit. This silently did the wrong thing when given a `set` as its argument, previously. --- qiskit/circuit/delay.py | 6 ++-- qiskit/circuit/quantumcircuit.py | 29 ++----------------- .../fix-delay-broadcast-e8762b01dfd7e94f.yaml | 7 +++++ test/python/circuit/test_delay.py | 11 +++++++ 4 files changed, 24 insertions(+), 29 deletions(-) create mode 100644 releasenotes/notes/fix-delay-broadcast-e8762b01dfd7e94f.yaml diff --git a/qiskit/circuit/delay.py b/qiskit/circuit/delay.py index 97bc81099e75..68d76dc03efb 100644 --- a/qiskit/circuit/delay.py +++ b/qiskit/circuit/delay.py @@ -16,6 +16,7 @@ import numpy as np from qiskit.circuit.exceptions import CircuitError from qiskit.circuit.instruction import Instruction +from qiskit.circuit.gate import Gate from qiskit.circuit.parameterexpression import ParameterExpression @@ -29,13 +30,12 @@ def __init__(self, duration, unit="dt"): super().__init__("delay", 1, 0, params=[duration], unit=unit) + broadcast_arguments = Gate.broadcast_arguments + def inverse(self): """Special case. Return self.""" return self - def broadcast_arguments(self, qargs, cargs): - yield [qarg for sublist in qargs for qarg in sublist], [] - def c_if(self, classical, val): raise CircuitError("Conditional Delay is not yet implemented.") diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 4cfc06d153a4..698f04b8513b 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -3351,32 +3351,9 @@ def delay( Raises: CircuitError: if arguments have bad format. """ - qubits: list[QubitSpecifier] = [] - if qarg is None: # -> apply delays to all qubits - for q in self.qubits: - qubits.append(q) - else: - if isinstance(qarg, QuantumRegister): - qubits.extend([qarg[j] for j in range(qarg.size)]) - elif isinstance(qarg, list): - qubits.extend(qarg) - elif isinstance(qarg, (range, tuple)): - qubits.extend(list(qarg)) - elif isinstance(qarg, slice): - qubits.extend(self.qubits[qarg]) - else: - qubits.append(qarg) - - instructions = InstructionSet( - resource_requester=self._current_scope().resolve_classical_resource - ) - for q in qubits: - inst: tuple[ - Instruction, Sequence[QubitSpecifier] | None, Sequence[ClbitSpecifier] | None - ] = (Delay(duration, unit), [q], []) - self.append(*inst) - instructions.add(*inst) - return instructions + if qarg is None: + qarg = self.qubits + return self.append(Delay(duration, unit=unit), [qarg], []) def h(self, qubit: QubitSpecifier) -> InstructionSet: """Apply :class:`~qiskit.circuit.library.HGate`. diff --git a/releasenotes/notes/fix-delay-broadcast-e8762b01dfd7e94f.yaml b/releasenotes/notes/fix-delay-broadcast-e8762b01dfd7e94f.yaml new file mode 100644 index 000000000000..2facbc643c14 --- /dev/null +++ b/releasenotes/notes/fix-delay-broadcast-e8762b01dfd7e94f.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + The qubit-argument broadcasting of :meth:`.QuantumCircuit.delay` now correctly produces + individual :class:`~.circuit.Delay` instructions for each qubit, as intended. Previously, when + given certain iterables (such as :class:`set`\ s), it would instead silently produce an invalid + circuit that might fail in unusual locations. diff --git a/test/python/circuit/test_delay.py b/test/python/circuit/test_delay.py index e4b0c0193308..f7b1d3f770d8 100644 --- a/test/python/circuit/test_delay.py +++ b/test/python/circuit/test_delay.py @@ -82,6 +82,17 @@ def test_add_delay_on_multiple_qubits_to_circuit(self): expected.delay(300, 2) self.assertEqual(qc, expected) + def test_delay_set(self): + """Test that a set argument to `delay` works.""" + qc = QuantumCircuit(5) + qc.delay(8, {0, 1, 3, 4}) + expected = QuantumCircuit(5) + expected.delay(8, 0) + expected.delay(8, 1) + expected.delay(8, 3) + expected.delay(8, 4) + self.assertEqual(qc, expected) + def test_to_matrix_return_identity_matrix(self): actual = np.array(Delay(100)) expected = np.array([[1, 0], [0, 1]], dtype=complex) From c70ece9898160b7fb678deb8633238d01629c4ac Mon Sep 17 00:00:00 2001 From: Julien Gacon Date: Tue, 9 Jan 2024 15:32:24 +0100 Subject: [PATCH 2/9] Do not filter Aer warnings that are fixed by now (#11518) The affected warnings have been fixed in the Aer 0.13 release --- qiskit/test/base.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/qiskit/test/base.py b/qiskit/test/base.py index 1dde885bd67f..e9b9e8e2f710 100644 --- a/qiskit/test/base.py +++ b/qiskit/test/base.py @@ -229,20 +229,6 @@ def setUpClass(cls): for msg in allow_DeprecationWarning_message: warnings.filterwarnings("default", category=DeprecationWarning, message=msg) - allow_aer_DeprecationWarning_message = [ - # This warning should be fixed once Qiskit/qiskit-aer#1761 is in a release version of Aer. - "Setting metadata to None.*", - # and this one once Qiskit/qiskit-aer#1945 is merged and released. - r"The method ``qiskit\.circuit\.quantumcircuit\.QuantumCircuit\.i\(\)`` is " - r"deprecated as of qiskit 0\.45\.0\. It will be removed no earlier than 3 " - r"months after the release date\. Use QuantumCircuit\.id as direct replacement\.", - ] - - for msg in allow_aer_DeprecationWarning_message: - warnings.filterwarnings( - "default", category=DeprecationWarning, module="qiskit_aer.*", message=msg - ) - class FullQiskitTestCase(QiskitTestCase): """Terra-specific further additions for test cases, if ``testtools`` is available. From 2d04bad46a46aa3c28a8955f2c86d7741a8bd92c Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Tue, 9 Jan 2024 18:01:39 +0000 Subject: [PATCH 3/9] Add other primitives team members to CODEOWNERS (#11525) * Add other primitives team members to CODEOWNERS * Switch to using a team Co-authored-by: Matthew Treinish --------- Co-authored-by: Matthew Treinish --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 20af254e76cb..13003c7e83b3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -30,7 +30,7 @@ pulse/ @Qiskit/terra-core @eggerdj @wshanks synthesis/ @Qiskit/terra-core @alexanderivrii @ShellyGarion scheduler/ @Qiskit/terra-core @eggerdj @wshanks visualization/ @Qiskit/terra-core @nonhermitian -primitives/ @Qiskit/terra-core @ikkoham @t-imamichi +primitives/ @Qiskit/terra-core @Qiskit/qiskit-primitives # Override the release notes directories to have _no_ code owners, so any review # from somebody with write access is acceptable. /releasenotes/notes From f663a646b88b401c036c32ce653c579deb6300ff Mon Sep 17 00:00:00 2001 From: "Christopher J. Wood" Date: Tue, 9 Jan 2024 16:14:24 -0500 Subject: [PATCH 4/9] Add V2 primitive container classes (#11524) * Add V2 primitive container classes Add required container classes for V2 primitive implementations. Separated out from 11227 to unblock other PRs Co-Authored-By: Ikko Hamamura Co-Authored-By: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com> Co-Authored-By: Ian Hincks <2229105+ihincks@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Ian Hincks * Update copyright to 2024 * Fix linting errors --------- Co-authored-by: Ikko Hamamura Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com> Co-authored-by: Ian Hincks <2229105+ihincks@users.noreply.github.com> Co-authored-by: Ian Hincks --- qiskit/primitives/__init__.py | 12 +- qiskit/primitives/containers/__init__.py | 21 + .../primitives/containers/bindings_array.py | 354 ++++++++++++++++ qiskit/primitives/containers/data_bin.py | 89 ++++ qiskit/primitives/containers/object_array.py | 94 +++++ .../containers/observables_array.py | 265 ++++++++++++ .../primitives/containers/primitive_result.py | 52 +++ qiskit/primitives/containers/pub_result.py | 45 ++ qiskit/primitives/containers/shape.py | 129 ++++++ qiskit/primitives/primitive_job.py | 5 +- test/python/primitives/containers/__init__.py | 13 + .../containers/test_bindings_array.py | 393 ++++++++++++++++++ .../primitives/containers/test_data_bin.py | 64 +++ .../containers/test_observables_array.py | 306 ++++++++++++++ .../containers/test_primitive_result.py | 44 ++ .../primitives/containers/test_shape.py | 105 +++++ 16 files changed, 1984 insertions(+), 7 deletions(-) create mode 100644 qiskit/primitives/containers/__init__.py create mode 100644 qiskit/primitives/containers/bindings_array.py create mode 100644 qiskit/primitives/containers/data_bin.py create mode 100644 qiskit/primitives/containers/object_array.py create mode 100644 qiskit/primitives/containers/observables_array.py create mode 100644 qiskit/primitives/containers/primitive_result.py create mode 100644 qiskit/primitives/containers/pub_result.py create mode 100644 qiskit/primitives/containers/shape.py create mode 100644 test/python/primitives/containers/__init__.py create mode 100644 test/python/primitives/containers/test_bindings_array.py create mode 100644 test/python/primitives/containers/test_data_bin.py create mode 100644 test/python/primitives/containers/test_observables_array.py create mode 100644 test/python/primitives/containers/test_primitive_result.py create mode 100644 test/python/primitives/containers/test_shape.py diff --git a/qiskit/primitives/__init__.py b/qiskit/primitives/__init__.py index cc12c9fbf083..ecc24757f815 100644 --- a/qiskit/primitives/__init__.py +++ b/qiskit/primitives/__init__.py @@ -50,13 +50,15 @@ EstimatorResult SamplerResult + PrimitiveResult + PubResult """ -from .base import BaseEstimator -from .base import BaseSampler from .backend_estimator import BackendEstimator -from .estimator import Estimator -from .base.estimator_result import EstimatorResult from .backend_sampler import BackendSampler -from .sampler import Sampler +from .base import BaseEstimator, BaseSampler +from .base.estimator_result import EstimatorResult from .base.sampler_result import SamplerResult +from .containers import BindingsArray, ObservablesArray, PrimitiveResult, PubResult +from .estimator import Estimator +from .sampler import Sampler diff --git a/qiskit/primitives/containers/__init__.py b/qiskit/primitives/containers/__init__.py new file mode 100644 index 000000000000..5367d4594008 --- /dev/null +++ b/qiskit/primitives/containers/__init__.py @@ -0,0 +1,21 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Data containers for primitives. +""" + +from .bindings_array import BindingsArray +from .data_bin import make_data_bin +from .observables_array import ObservablesArray +from .primitive_result import PrimitiveResult +from .pub_result import PubResult diff --git a/qiskit/primitives/containers/bindings_array.py b/qiskit/primitives/containers/bindings_array.py new file mode 100644 index 000000000000..14efabdc396d --- /dev/null +++ b/qiskit/primitives/containers/bindings_array.py @@ -0,0 +1,354 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 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. + +""" +Bindings array class +""" +from __future__ import annotations + +from collections.abc import Iterable, Mapping, Sequence +from itertools import chain +from typing import Union + +import numpy as np +from numpy.typing import ArrayLike + +from qiskit.circuit import Parameter, QuantumCircuit +from qiskit.circuit.parameterexpression import ParameterValueType + +from .shape import ShapedMixin, ShapeInput, shape_tuple + +ParameterLike = Union[Parameter, str] + + +class BindingsArray(ShapedMixin): + r"""Stores parameter binding value sets for a :class:`qiskit.QuantumCircuit`. + + A single parameter binding set provides numeric values to bind to a circuit with free + :class:`qiskit.circuit.Parameter`\s. An instance of this class stores an array-valued + collection of such sets. The simplest example is a 0-d array consisting of a single + parameter binding set, whereas an n-d array of parameter binding sets represents an + n-d sweep over values. + + The storage format is a list of arrays, ``[vals0, vals1, ...]``, as well as a dictionary of + arrays attached to parameters, ``{params0: kwvals0, ...}``. A convention is used + where the last dimension of each array indexes (a subset of) circuit parameters. For + example, if the last dimension of ``vals1`` is 25, then it represents an array of + possible binding values for 25 distinct parameters, where its leading shape is the + array :attr:`~.shape` of its binding array. This implies a degeneracy of the storage + format: ``[vals, vals1[..., :10], vals1[..., 10:], ...]`` is exactly equivalent to + ``[vals0, vals1, ...]`` in the bindings it specifies. This allows flexibility about whether + values for different parameters are stored in one big array, or across several smaller + arrays. It also allows different parameters to use different dtypes. + + .. code-block:: python + + # 0-d array (i.e. only one binding) + BindingsArray([1, 2, 3], {"a": 4, ("b", "c"): [5, 6]}) + + # single array, last index is parameters + BindingsArray(np.empty((10, 10, 100))) + + # multiple arrays, where each last index is parameters. notice that it's smart enough to + # figure out that a missing last dimension corresponds to a single parameter. + BindingsArray( + [np.empty((10, 10, 100)), np.empty((10, 10)), np.empty((10, 10, 20), dtype=complex)], + {("c", "a"): np.empty((10, 10, 2)), "b": np.empty((10, 10))} + ) + """ + __slots__ = ("_vals", "_kwvals") + + def __init__( + self, + vals: ArrayLike | Iterable[ArrayLike] | None = None, + kwvals: Mapping[ParameterLike, Iterable[ParameterValueType]] | ArrayLike | None = None, + shape: ShapeInput | None = None, + ): + r""" + Initialize a ``BindingsArray``. It can take parameter vectors and dictionaries. + + The ``shape`` argument does not need to be provided whenever it can unambiguously + be inferred from the provided arrays. Ambiguity arises because an array provided to the + constructor might represent values for either a single parameter, with an implicit missing + last dimension of size ``1``, or for many parameters, where the size of the last dimension + is the number of parameters it is providing values to. This ambiguity can be broken in the + following common ways: + + * Only a single array is provided to ``vals``, and no arrays to ``kwvals``, in which case + it is assumed that the last dimension is over many parameters. + * Multiple arrays are given whose shapes differ only in the last dimension size. + * Some array is given in ``kwvals`` where the key contains multiple + :class:`~.Parameter`\s, whose length the last dimension of the array must therefore match. + + Args: + vals: One or more arrays, where the last index of each corresponds to + distinct parameters. If their dtypes allow it, concatenating these + arrays over the last axis is equivalent to providing them separately. + kwvals: A mapping from one or more parameters to arrays of values to bind + them to, where the last axis is over parameters. + shape: The leading shape of every array in these bindings. + + Raises: + ValueError: If all inputs are ``None``. + ValueError: If the shape cannot be automatically inferred from the arrays, or if there + is some inconsistency in the shape of the given arrays. + """ + super().__init__() + + if vals is None: + vals = [] + if kwvals is None: + kwvals = {} + + vals = [vals] if isinstance(vals, np.ndarray) else [np.array(v, copy=False) for v in vals] + # TODO str will be used for internal data (_kwvals) instead of Parameter. + # This requires https://github.com/Qiskit/qiskit/issues/7107 + kwvals = { + (p,) if isinstance(p, Parameter) else tuple(p): np.array(val, copy=False) + for p, val in kwvals.items() + } + + if shape is None: + # jump through hoops to find out user's intended shape + shape = _infer_shape(vals, kwvals) + + # shape checking, and normalization so that each last index must be over parameters + self._shape = shape_tuple(shape) + for idx, val in enumerate(vals): + vals[idx] = _standardize_shape(val, self._shape) + + self._vals: list[np.ndarray] = vals + self._kwvals = kwvals + + self.validate() + + def __getitem__(self, args) -> BindingsArray: + # because the parameters live on the last axis, we don't need to do anything special to + # accomodate them because there will always be an implicit slice(None, None, None) + # on all unspecified trailing dimensions + # separately, we choose to not disallow args which touch the last dimension, even though it + # would not be a particularly friendly way to chop parameters + vals = [val[args] for val in self._vals] + kwvals = {params: val[args] for params, val in self._kwvals.items()} + try: + shape = next(chain(vals, kwvals.values())).shape[:-1] + except StopIteration: + shape = () + return BindingsArray(vals, kwvals, shape) + + @property + def kwvals(self) -> dict[tuple[str, ...], np.ndarray]: + """The keyword values of this array.""" + return {_format_key(k): v for k, v in self._kwvals.items()} + + @property + def num_parameters(self) -> int: + """The total number of parameters.""" + return sum(val.shape[-1] for val in chain(self.vals, self._kwvals.values())) + + @property + def vals(self) -> list[np.ndarray]: + """The non-keyword values of this array.""" + return self._vals + + def bind(self, circuit: QuantumCircuit, loc: tuple[int, ...]) -> QuantumCircuit: + """Return a new circuit bound to the values at the provided index. + + Args: + circuit: The circuit to bind. + loc: A tuple of indices, on for each dimension of this array. + + Returns: + The bound circuit. + + Raises: + ValueError: If the index doesn't have the right number of values. + """ + if len(loc) != self.ndim: + raise ValueError(f"Expected {loc} to index all dimensions of {self.shape}") + + flat_vals = (val for vals in self.vals for val in vals[loc]) + + if not self._kwvals: + # special case to avoid constructing a dictionary input + return circuit.assign_parameters(list(flat_vals)) + + parameters = dict(zip(circuit.parameters, flat_vals)) + parameters.update( + (param, val) + for params, vals in self._kwvals.items() + for param, val in zip(params, vals[loc]) + ) + return circuit.assign_parameters(parameters) + + def bind_all(self, circuit: QuantumCircuit) -> np.ndarray: + """Return an object array of bound circuits with the same shape. + + Args: + circuit: The circuit to bind. + + Returns: + An object array of the same shape containing all bound circuits. + """ + arr = np.empty(self.shape, dtype=object) + for idx in np.ndindex(self.shape): + arr[idx] = self.bind(circuit, idx) + return arr + + def ravel(self) -> BindingsArray: + """Return a new :class:`~BindingsArray` with one dimension. + + The returned bindings array has a :attr:`shape` given by ``(size, )``, where the size is the + :attr:`~size` of this bindings array. + + Returns: + A new bindings array. + """ + return self.reshape(self.size) + + def reshape(self, shape: int | Iterable[int]) -> BindingsArray: + """Return a new :class:`~BindingsArray` with a different shape. + + This results in a new view of the same arrays. + + Args: + shape: The shape of the returned bindings array. + + Returns: + A new bindings array. + + Raises: + ValueError: If the provided shape has a different product than the current size. + """ + shape = (shape, -1) if isinstance(shape, int) else (*shape, -1) + if np.prod(shape[:-1]).astype(int) != self.size: + raise ValueError("Reshaping cannot change the total number of elements.") + vals = [val.reshape(shape) for val in self._vals] + kwvals = {params: val.reshape(shape) for params, val in self._kwvals.items()} + return BindingsArray(vals, kwvals, shape[:-1]) + + @classmethod + def coerce(cls, bindings_array: BindingsArrayLike) -> BindingsArray: + """Coerce an input that is :class:`~BindingsArrayLike` into a new :class:`~BindingsArray`. + + Args: + bindings_array: An object to be bindings array. + + Returns: + A new bindings array. + """ + if isinstance(bindings_array, Sequence): + bindings_array = np.array(bindings_array) + if bindings_array is None: + bindings_array = cls() + elif isinstance(bindings_array, np.ndarray): + bindings_array = cls(bindings_array) + elif isinstance(bindings_array, Mapping): + bindings_array = cls(kwvals=bindings_array) + else: + raise TypeError(f"Unsupported type {type(bindings_array)} is given.") + return bindings_array + + def validate(self): + """Validate the consistency in bindings_array.""" + for parameters, val in self._kwvals.items(): + val = self._kwvals[parameters] = _standardize_shape(val, self._shape) + if len(parameters) != val.shape[-1]: + raise ValueError( + f"Length of {parameters} inconsistent with last dimension of {val}" + ) + + +def _standardize_shape(val: np.ndarray, shape: tuple[int, ...]) -> np.ndarray: + """Return ``val`` or ``val[..., None]``. + + Args: + val: The array whose shape to standardize. + shape: The shape to standardize to. + + Returns: + An array with one more dimension than ``len(shape)``, and whose leading dimensions match + ``shape``. + + Raises: + ValueError: If the leading shape of ``val`` does not match the ``shape``. + """ + if val.shape == shape: + val = val[..., None] + elif val.ndim - 1 != len(shape) or val.shape[:-1] != shape: + raise ValueError(f"Array with shape {val.shape} inconsistent with {shape}") + return val + + +def _infer_shape( + vals: list[np.ndarray], kwvals: dict[tuple[Parameter, ...], np.ndarray] +) -> tuple[int, ...]: + """Return a shape tuple that consistently defines the leading dimensions of all arrays. + + Args: + vals: A list of arrays. + kwvals: A mapping from tuples to arrays, where the length of each tuple should match the + last dimension of the corresponding array. + + Returns: + A shape tuple that matches the leading dimension of every array. + + Raises: + ValueError: If this cannot be done unambiguously. + """ + only_possible_shapes = None + + def examine_array(*possible_shapes): + nonlocal only_possible_shapes + if only_possible_shapes is None: + only_possible_shapes = set(possible_shapes) + else: + only_possible_shapes.intersection_update(possible_shapes) + + for parameters, val in kwvals.items(): + if len(parameters) > 1: + # here, the last dimension _has_ to be over parameters + examine_array(val.shape[:-1]) + elif val.shape == () or val.shape == (1,) or val.shape[-1] != 1: + # here, if the last dimension is not 1 or shape is () or (1,) then the shape is the shape + examine_array(val.shape) + else: + # here, the last dimension could be over parameters or not + examine_array(val.shape, val.shape[:-1]) + + if len(vals) == 1 and len(kwvals) == 0: + examine_array(vals[0].shape[:-1]) + elif len(vals) == 0 and len(kwvals) == 0: + examine_array(()) + else: + for val in vals: + # here, the last dimension could be over parameters or not + examine_array(val.shape, val.shape[:-1]) + + if len(only_possible_shapes) == 1: + return next(iter(only_possible_shapes)) + elif len(only_possible_shapes) == 0: + raise ValueError("Could not find any consistent shape.") + raise ValueError("Could not unambiguously determine the intended shape; specify shape manually") + + +def _format_key(key: tuple[Parameter | str, ...]): + return tuple(k.name if isinstance(k, Parameter) else k for k in key) + + +BindingsArrayLike = Union[ + BindingsArray, + ArrayLike, + "Mapping[Parameter, ArrayLike]", + "Sequence[ArrayLike]", + None, +] diff --git a/qiskit/primitives/containers/data_bin.py b/qiskit/primitives/containers/data_bin.py new file mode 100644 index 000000000000..b4e479266e72 --- /dev/null +++ b/qiskit/primitives/containers/data_bin.py @@ -0,0 +1,89 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 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. + +""" +Dataclass tools for data namespaces (bins) +""" +from __future__ import annotations + +from collections.abc import Iterable +from dataclasses import make_dataclass + + +class DataBinMeta(type): + """Metaclass for :class:`DataBin` that adds the shape to the type name. + + This is so that the class has a custom repr with DataBin<*shape> notation. + """ + + def __repr__(cls): + name = cls.__name__ + if cls._SHAPE is None: + return name + shape = ",".join(map(str, cls._SHAPE)) + return f"{name}<{shape}>" + + +class DataBin(metaclass=DataBinMeta): + """Base class for data bin containers. + + Subclasses are typically made via :class:`~make_data_bin`, which is a specialization of + :class:`make_dataclass`. + """ + + _RESTRICTED_NAMES = ("_RESTRICTED_NAMES", "_SHAPE", "_FIELDS", "_FIELD_TYPES") + _SHAPE: tuple[int, ...] | None = None + _FIELDS: tuple[str, ...] = () + """The fields allowed in this data bin.""" + _FIELD_TYPES: tuple[type, ...] = () + """The types of each field.""" + + def __repr__(self): + vals = (f"{name}={getattr(self, name)}" for name in self._FIELDS if hasattr(self, name)) + return f"{type(self)}({', '.join(vals)})" + + +def make_data_bin( + fields: Iterable[tuple[str, type]], shape: tuple[int, ...] | None = None +) -> DataBinMeta: + """Return a new subclass of :class:`~DataBin` with the provided fields and shape. + + .. code-block:: python + + my_bin = make_data_bin([("alpha", np.NDArray[np.float])], shape=(20, 30)) + + # behaves like a dataclass + my_bin(alpha=np.empty((20, 30))) + + Args: + fields: Tuples ``(name, type)`` specifying the attributes of the returned class. + shape: The intended shape of every attribute of this class. + + Returns: + A new class. + """ + field_names, field_types = zip(*fields) + for name in field_names: + if name in DataBin._RESTRICTED_NAMES: + raise ValueError(f"'{name}' is a restricted name for a DataBin.") + cls = make_dataclass( + "DataBin", + dict(zip(field_names, field_types)), + bases=(DataBin,), + frozen=True, + unsafe_hash=True, + repr=False, + ) + cls._SHAPE = shape + cls._FIELDS = field_names + cls._FIELD_TYPES = field_types + return cls diff --git a/qiskit/primitives/containers/object_array.py b/qiskit/primitives/containers/object_array.py new file mode 100644 index 000000000000..f93515daf898 --- /dev/null +++ b/qiskit/primitives/containers/object_array.py @@ -0,0 +1,94 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 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. + +""" +Object ND-array initialization function. +""" +from __future__ import annotations + +from collections.abc import Sequence + +import numpy as np +from numpy.typing import ArrayLike + + +def object_array( + arr: ArrayLike, + order: str | None = None, + copy: bool = True, + list_types: Sequence[type] | None = (), +) -> np.ndarray: + """Convert an array-like of objects into an object array. + + .. note:: + + If the objects in the array like input define ``__array__`` methods + this avoids calling them and will instead set the returned array values + to the Python objects themselves. + + Args: + arr: An array-like input. + order: Optional, the order of the returned array (C, F, A, K). If None + the default NumPy ordering of C is used. + copy: If True make a copy of the input if it is already an array. + list_types: Optional, a sequence of types to treat as lists of array + element objects when inferring the array shape from the input. + + Returns: + A NumPy ND-array with ``dtype=object``. + + Raises: + ValueError: If the input cannot be coerced into an object array. + """ + if isinstance(arr, np.ndarray): + if arr.dtype != object or order is not None or copy is True: + arr = arr.astype(object, order=order, copy=copy) + return arr + + shape = _infer_shape(arr, list_types=tuple(list_types)) + obj_arr = np.empty(shape, dtype=object, order=order) + if not shape: + # We call fill here instead of [()] to avoid invoking the + # objects `__array__` method if it has one (eg for Pauli's). + obj_arr.fill(arr) + else: + # For other arrays we need to do some tricks to avoid invoking the + # objects __array__ method by flattening the input and initializing + # using `np.fromiter` which does not invoke `__array__` for object + # dtypes. + def _flatten(nested, k): + if k == 1: + return nested + else: + return [item for sublist in nested for item in _flatten(sublist, k - 1)] + + flattened = _flatten(arr, len(shape)) + if len(flattened) != obj_arr.size: + raise ValueError( + "Input object size does not match the inferred array shape." + " This most likely occurs when the input is a ragged array." + ) + obj_arr.flat = np.fromiter(flattened, dtype=object, count=len(flattened)) + + return obj_arr + + +def _infer_shape(obj: ArrayLike, list_types: tuple[type, ...] = ()) -> tuple[int, ...]: + """Infer the shape of an array-like object without casting""" + if isinstance(obj, np.ndarray): + return obj.shape + if not isinstance(obj, (list, *list_types)): + return () + size = len(obj) + if size == 0: + return (size,) + return (size, *_infer_shape(obj[0], list_types=list_types)) diff --git a/qiskit/primitives/containers/observables_array.py b/qiskit/primitives/containers/observables_array.py new file mode 100644 index 000000000000..0d33111d5957 --- /dev/null +++ b/qiskit/primitives/containers/observables_array.py @@ -0,0 +1,265 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 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. + + +""" +ND-Array container class for Estimator observables. +""" +from __future__ import annotations + +import re +from collections import defaultdict +from collections.abc import Mapping as MappingType +from functools import lru_cache +from typing import Iterable, Mapping, Union, overload + +import numpy as np +from numpy.typing import ArrayLike + +from qiskit.quantum_info import Pauli, PauliList, SparsePauliOp + +from .object_array import object_array +from .shape import ShapedMixin + +BasisObservable = Mapping[str, complex] +"""Representation type of a single observable.""" + +BasisObservableLike = Union[ + str, + Pauli, + SparsePauliOp, + Mapping[Union[str, Pauli], complex], + Iterable[Union[str, Pauli, SparsePauliOp]], +] +"""Types that can be natively used to construct a :const:`BasisObservable`.""" + + +class ObservablesArray(ShapedMixin): + """An ND-array of :const:`.BasisObservable` for an :class:`.Estimator` primitive.""" + + __slots__ = ("_array", "_shape") + ALLOWED_BASIS: str = "IXYZ01+-lr" + """The allowed characters in :const:`BasisObservable` strings.""" + + def __init__( + self, + observables: BasisObservableLike | ArrayLike, + copy: bool = True, + validate: bool = True, + ): + """Initialize an observables array. + + Args: + observables: An array-like of basis observable compatible objects. + copy: Specify the ``copy`` kwarg of the :func:`.object_array` function + when initializing observables. + validate: If True, convert :const:`.BasisObservableLike` input objects + to :const:`.BasisObservable` objects and validate. If False the + input should already be an array-like of valid + :const:`.BasisObservble` objects. + + Raises: + ValueError: If ``validate=True`` and the input observables is not valid. + """ + super().__init__() + if isinstance(observables, ObservablesArray): + observables = observables._array + self._array = object_array(observables, copy=copy, list_types=(PauliList,)) + self._shape = self._array.shape + if validate: + num_qubits = None + for ndi, obs in np.ndenumerate(self._array): + basis_obs = self.format_observable(obs) + basis_num_qubits = len(next(iter(basis_obs))) + if num_qubits is None: + num_qubits = basis_num_qubits + elif basis_num_qubits != num_qubits: + raise ValueError( + "The number of qubits must be the same for all observables in the " + "observables array." + ) + self._array[ndi] = basis_obs + + def __repr__(self): + prefix = f"{type(self).__name__}(" + suffix = f", shape={self.shape})" + array = np.array2string(self._array, prefix=prefix, suffix=suffix, threshold=50) + return prefix + array + suffix + + def tolist(self) -> list: + """Convert to a nested list""" + return self._array.tolist() + + def __array__(self, dtype=None): + """Convert to an Numpy.ndarray""" + if dtype is None or dtype == object: + return self._array + raise ValueError("Type must be 'None' or 'object'") + + @overload + def __getitem__(self, args: int | tuple[int, ...]) -> BasisObservable: + ... + + @overload + def __getitem__(self, args: slice) -> ObservablesArray: + ... + + def __getitem__(self, args): + item = self._array[args] + if not isinstance(item, np.ndarray): + return item + return ObservablesArray(item, copy=False, validate=False) + + def reshape(self, shape: int | Iterable[int]) -> ObservablesArray: + """Return a new array with a different shape. + + This results in a new view of the same arrays. + + Args: + shape: The shape of the returned array. + + Returns: + A new array. + """ + return ObservablesArray(self._array.reshape(shape), copy=False, validate=False) + + def ravel(self) -> ObservablesArray: + """Return a new array with one dimension. + + The returned array has a :attr:`shape` given by ``(size, )``, where + the size is the :attr:`~size` of this array. + + Returns: + A new flattened array. + """ + return self.reshape(self.size) + + @classmethod + def format_observable(cls, observable: BasisObservableLike) -> BasisObservable: + """Format an observable-like object into a :const:`BasisObservable`. + + Args: + observable: The observable-like to format. + + Returns: + The given observable as a :const:`~BasisObservable`. + + Raises: + TypeError: If the input cannot be formatted because its type is not valid. + ValueError: If the input observable is invalid. + """ + + # Pauli-type conversions + if isinstance(observable, SparsePauliOp): + # Call simplify to combine duplicate keys before converting to a mapping + return cls.format_observable(dict(observable.simplify(atol=0).to_list())) + + if isinstance(observable, Pauli): + label, phase = observable[:].to_label(), observable.phase + return {label: 1} if phase == 0 else {label: (-1j) ** phase} + + # String conversion + if isinstance(observable, str): + cls._validate_basis(observable) + return {observable: 1} + + # Mapping conversion (with possible Pauli keys) + if isinstance(observable, MappingType): + num_qubits = len(next(iter(observable))) + unique = defaultdict(complex) + for basis, coeff in observable.items(): + if isinstance(basis, Pauli): + basis, phase = basis[:].to_label(), basis.phase + if phase != 0: + coeff = coeff * (-1j) ** phase + # Validate basis + cls._validate_basis(basis) + if len(basis) != num_qubits: + raise ValueError( + "Number of qubits must be the same for all observable basis elements." + ) + unique[basis] += coeff + return dict(unique) + + raise TypeError(f"Invalid observable type: {type(observable)}") + + @classmethod + def coerce(cls, observables: ObservablesArrayLike) -> ObservablesArray: + """Coerce ObservablesArrayLike into ObservableArray. + + Args: + observables: an object to be observables array. + + Returns: + A coerced observables array. + """ + if isinstance(observables, ObservablesArray): + return observables + if isinstance(observables, (str, SparsePauliOp, Pauli, Mapping)): + observables = [observables] + return cls(observables) + + def validate(self): + """Validate the consistency in observables array.""" + num_qubits = None + for obs in self._array: + basis_num_qubits = len(next(iter(obs))) + if num_qubits is None: + num_qubits = basis_num_qubits + elif basis_num_qubits != num_qubits: + raise ValueError( + "The number of qubits must be the same for all observables in the " + "observables array." + ) + + @classmethod + def _validate_basis(cls, basis: str) -> None: + """Validate a basis string. + + Args: + basis: a basis string to validate. + + Raises: + ValueError: If basis string contains invalid characters + """ + # NOTE: the allowed basis characters can be overridden by modifying the class + # attribute ALLOWED_BASIS + allowed_pattern = _regex_match(cls.ALLOWED_BASIS) + if not allowed_pattern.match(basis): + invalid_pattern = _regex_invalid(cls.ALLOWED_BASIS) + invalid_chars = list(set(invalid_pattern.findall(basis))) + raise ValueError( + f"Observable basis string '{basis}' contains invalid characters {invalid_chars}," + f" allowed characters are {list(cls.ALLOWED_BASIS)}.", + ) + + +ObservablesArrayLike = Union[ObservablesArray, ArrayLike, BasisObservableLike] +"""Types that can be natively converted to an ObservablesArray""" + + +class PauliArray(ObservablesArray): + """An ND-array of Pauli-basis observables for an :class:`.Estimator` primitive.""" + + ALLOWED_BASIS = "IXYZ" + + +@lru_cache(1) +def _regex_match(allowed_chars: str) -> re.Pattern: + """Return pattern for matching if a string contains only the allowed characters.""" + return re.compile(f"^[{re.escape(allowed_chars)}]*$") + + +@lru_cache(1) +def _regex_invalid(allowed_chars: str) -> re.Pattern: + """Return pattern for selecting invalid strings""" + return re.compile(f"[^{re.escape(allowed_chars)}]") diff --git a/qiskit/primitives/containers/primitive_result.py b/qiskit/primitives/containers/primitive_result.py new file mode 100644 index 000000000000..b383f4e119e1 --- /dev/null +++ b/qiskit/primitives/containers/primitive_result.py @@ -0,0 +1,52 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 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. + +"""PrimitiveResult""" + +from __future__ import annotations + +from collections.abc import Iterable +from typing import Any, Generic, TypeVar + +from .pub_result import PubResult + +T = TypeVar("T", bound=PubResult) + + +class PrimitiveResult(Generic[T]): + """A container for multiple pub results and global metadata.""" + + def __init__(self, pub_results: Iterable[T], metadata: dict[str, Any] | None = None): + """ + Args: + pub_results: Pub results. + metadata: Any metadata that doesn't make sense to put inside of pub results. + """ + self._pub_results = list(pub_results) + self._metadata = metadata or {} + + @property + def metadata(self) -> dict[str, Any]: + """The metadata of this primitive result.""" + return self._metadata + + def __getitem__(self, index) -> T: + return self._pub_results[index] + + def __len__(self) -> int: + return len(self._pub_results) + + def __repr__(self) -> str: + return f"PrimitiveResult({self._pub_results}, metadata={self.metadata})" + + def __iter__(self) -> Iterable[T]: + return iter(self._pub_results) diff --git a/qiskit/primitives/containers/pub_result.py b/qiskit/primitives/containers/pub_result.py new file mode 100644 index 000000000000..c7ac2b6160e7 --- /dev/null +++ b/qiskit/primitives/containers/pub_result.py @@ -0,0 +1,45 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Base Pub class +""" + +from __future__ import annotations + +from .data_bin import DataBin + + +class PubResult: + """Result of Primitive Unified Bloc.""" + + __slots__ = ("_data", "_metadata") + + def __init__(self, data: DataBin, metadata: dict | None = None): + """Initialize a pub result. + + Args: + data: result data bin. + metadata: metadata dictionary. + """ + self._data = data + self._metadata = metadata or {} + + @property + def data(self) -> DataBin: + """Result data for the pub.""" + return self._data + + @property + def metadata(self) -> dict: + """Metadata for the pub.""" + return self._metadata diff --git a/qiskit/primitives/containers/shape.py b/qiskit/primitives/containers/shape.py new file mode 100644 index 000000000000..952916cd67dc --- /dev/null +++ b/qiskit/primitives/containers/shape.py @@ -0,0 +1,129 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 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. + +""" +Array shape related classes and functions +""" +from __future__ import annotations + +from collections.abc import Iterable +from typing import Protocol, Union, runtime_checkable + +import numpy as np +from numpy.typing import ArrayLike, NDArray + +ShapeInput = Union[int, "Iterable[ShapeInput]"] +"""An input that is coercible into a shape tuple.""" + + +@runtime_checkable +class Shaped(Protocol): + """Protocol that defines what it means to be a shaped object. + + Note that static type checkers will classify ``numpy.ndarray`` as being :class:`Shaped`. + Moreover, since this protocol is runtime-checkable, we will even have + ``isinstance(, Shaped) == True``. + """ + + @property + def shape(self) -> tuple[int, ...]: + """The array shape of this object.""" + raise NotImplementedError("A `Shaped` protocol must implement the `shape` property") + + @property + def ndim(self) -> int: + """The number of array dimensions of this object.""" + raise NotImplementedError("A `Shaped` protocol must implement the `ndim` property") + + @property + def size(self) -> int: + """The total dimension of this object, i.e. the product of the entries of :attr:`~shape`.""" + raise NotImplementedError("A `Shaped` protocol must implement the `size` property") + + +class ShapedMixin(Shaped): + """Mixin class to create :class:`~Shaped` types by only providing :attr:`_shape` attribute.""" + + _shape: tuple[int, ...] + + def __repr__(self): + return f"{type(self).__name__}(<{self.shape}>)" + + @property + def shape(self): + return self._shape + + @property + def ndim(self): + return len(self._shape) + + @property + def size(self): + return int(np.prod(self._shape, dtype=int)) + + +def array_coerce(arr: ArrayLike | Shaped) -> NDArray | Shaped: + """Coerce the input into an object with a shape attribute. + + Copies are avoided. + + Args: + arr: The object to coerce. + + Returns: + Something that is :class:`~Shaped`, and always ``numpy.ndarray`` if the input is not + already :class:`~Shaped`. + """ + if isinstance(arr, Shaped): + return arr + return np.array(arr, copy=False) + + +def _flatten_to_ints(arg: ShapeInput) -> Iterable[int]: + """ + Yield one integer at a time. + + Args: + arg: Integers or iterables of integers, possibly nested, to be yielded. + + Yields: + The provided integers in depth-first recursive order. + + Raises: + ValueError: If an input is not an iterable or an integer. + """ + for item in arg: + try: + if isinstance(item, Iterable): + yield from _flatten_to_ints(item) + elif int(item) == item: + yield int(item) + else: + raise ValueError(f"Expected {item} to be iterable or an integer.") + except (TypeError, RecursionError) as ex: + raise ValueError(f"Expected {item} to be iterable or an integer.") from ex + + +def shape_tuple(*shapes: ShapeInput) -> tuple[int, ...]: + """ + Flatten the input into a single tuple of integers, preserving order. + + Args: + shapes: Integers or iterables of integers, possibly nested. + + Returns: + A tuple of integers. + + Raises: + ValueError: If some member of ``shapes`` is not an integer or iterable. + """ + return tuple(_flatten_to_ints(shapes)) diff --git a/qiskit/primitives/primitive_job.py b/qiskit/primitives/primitive_job.py index 7c87fd34994d..93924b2a4c23 100644 --- a/qiskit/primitives/primitive_job.py +++ b/qiskit/primitives/primitive_job.py @@ -15,13 +15,14 @@ import uuid from concurrent.futures import ThreadPoolExecutor -from typing import Generic, TypeVar +from typing import Generic, TypeVar, Union from qiskit.providers import JobError, JobStatus, JobV1 from .base.base_result import BasePrimitiveResult +from .containers import PrimitiveResult -T = TypeVar("T", bound=BasePrimitiveResult) +T = TypeVar("T", bound=Union[BasePrimitiveResult, PrimitiveResult]) class PrimitiveJob(JobV1, Generic[T]): diff --git a/test/python/primitives/containers/__init__.py b/test/python/primitives/containers/__init__.py new file mode 100644 index 000000000000..bdefdb61023f --- /dev/null +++ b/test/python/primitives/containers/__init__.py @@ -0,0 +1,13 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 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. + +"""Tests for the data containers of primitives.""" diff --git a/test/python/primitives/containers/test_bindings_array.py b/test/python/primitives/containers/test_bindings_array.py new file mode 100644 index 000000000000..9f714f38e83a --- /dev/null +++ b/test/python/primitives/containers/test_bindings_array.py @@ -0,0 +1,393 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 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. + +"""Test BindingsArray""" + + +import numpy as np + +from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit +from qiskit.primitives import BindingsArray +from qiskit.test import QiskitTestCase + + +class BindingsArrayTestCase(QiskitTestCase): + """Test the BindingsArray class""" + + def setUp(self): + self.circuit = QuantumCircuit(5) + self.params = ParameterVector("a", 50) + param_iter = iter(self.params) + for _ in range(10): + for qubit in range(5): + self.circuit.sx(qubit) + self.circuit.rz(next(param_iter), qubit) + self.circuit.cx(0, 1) + self.circuit.cx(2, 3) + return super().setUp() + + def test_construction_failures(self): + """Test all the possible construction failures""" + with self.assertRaisesRegex(ValueError, "inconsistent with last dimension of"): + BindingsArray(kwvals={Parameter("a"): [0, 1]}, shape=()) + + with self.assertRaisesRegex(ValueError, r"Array with shape \(\) inconsistent with \(1,\)"): + BindingsArray(kwvals={Parameter("a"): 0}, shape=(1,)) + + with self.assertRaisesRegex(ValueError, "ambiguous"): + # could have shape (1,) or (1, 1) + BindingsArray(kwvals={Parameter("a"): [[1]]}) + + with self.assertRaisesRegex(ValueError, r"\(3, 5\) inconsistent with \(2,\)"): + BindingsArray(np.empty((3, 5)), shape=2) + + with self.assertRaisesRegex(ValueError, "ambiguous"): + # could have shape (2,) or () + BindingsArray([np.empty(2), np.empty(2)]) + + with self.assertRaisesRegex(ValueError, "Could not find any consistent shape"): + BindingsArray([np.empty((5, 8, 3)), np.empty((4, 7, 2))]) + + with self.assertRaisesRegex(ValueError, "inconsistent with last dimension of"): + BindingsArray( + vals=np.empty((5, 10)), + kwvals={(Parameter("a"), Parameter("b")): np.empty((5, 10, 3))}, + ) + + def test_bind_at_idx(self): + """Test binding at a specified index""" + vals = np.linspace(0, 1, 1000).reshape((5, 4, 50)) + expected_circuit = self.circuit.assign_parameters(vals[2, 3]) + + ba = BindingsArray(vals) + self.assertEqual(ba.bind(self.circuit, (2, 3)), expected_circuit) + + ba = BindingsArray([vals[:, :, :20], vals[:, :, 20:27], vals[:, :, 27:]]) + self.assertEqual(ba.bind(self.circuit, (2, 3)), expected_circuit) + + ba = BindingsArray(vals[:, :, :20], {tuple(self.params[20:]): vals[:, :, 20:]}) + self.assertEqual(ba.bind(self.circuit, (2, 3)), expected_circuit) + + order = np.arange(30, 50, dtype=int) + np.random.default_rng().shuffle(order) + ba = BindingsArray( + [vals[:, :, :20], vals[:, :, 20:25]], + { + tuple(self.params[25:30]): vals[:, :, 25:30], + tuple(self.params[i] for i in order): vals[:, :, order], + }, + ) + self.assertEqual(ba.bind(self.circuit, (2, 3)), expected_circuit) + + def test_bind_all(self): + """Test binding all possible values""" + # this test assumes bind_all() is implemented via bind_at_idx(), which we have already + # tested. so here, we just test that it gets the order right + vals = np.linspace(0, 1, 300).reshape((2, 3, 50)) + bound_circuits = BindingsArray(vals).bind_all(self.circuit) + self.assertIsInstance(bound_circuits, np.ndarray) + self.assertEqual(bound_circuits.shape, (2, 3)) + for idx in np.ndindex((2, 3)): + self.assertEqual(bound_circuits[idx], self.circuit.assign_parameters(vals[idx])) + + def test_properties(self): + """Test properties""" + with self.subTest("binding a list"): + vals = np.linspace(0, 1, 50).tolist() + ba = BindingsArray(vals) + self.assertEqual(ba.num_parameters, 50) + self.assertEqual(ba.ndim, 0) + self.assertEqual(ba.shape, ()) + self.assertEqual(ba.size, 1) + self.assertEqual(ba.kwvals, {}) + np.testing.assert_allclose(ba.vals, np.array(vals)[:, np.newaxis]) + + with self.subTest("binding a single array"): + vals = np.linspace(0, 1, 300).reshape((2, 3, 50)) + ba = BindingsArray(vals) + self.assertEqual(ba.num_parameters, 50) + self.assertEqual(ba.ndim, 2) + self.assertEqual(ba.shape, (2, 3)) + self.assertEqual(ba.size, 6) + self.assertEqual(ba.kwvals, {}) + np.testing.assert_allclose(ba.vals, vals.reshape((1, 2, 3, 50))) + + with self.subTest("binding multiple arrays"): + vals = np.linspace(0, 1, 300).reshape((2, 3, 50)) + ba = BindingsArray([vals[:, :, :20], vals[:, :, 20:]]) + self.assertEqual(ba.num_parameters, 50) + self.assertEqual(ba.ndim, 2) + self.assertEqual(ba.shape, (2, 3)) + self.assertEqual(ba.size, 6) + self.assertEqual(ba.kwvals, {}) + self.assertEqual(len(ba.vals), 2) + np.testing.assert_allclose(ba.vals[0], vals[:, :, :20]) + np.testing.assert_allclose(ba.vals[1], vals[:, :, 20:]) + + def test_ravel(self): + """Test ravel""" + vals = np.linspace(0, 1, 300).reshape((2, 3, 50)) + + ba = BindingsArray(vals) + flat = ba.ravel() + self.assertEqual(flat.num_parameters, 50) + self.assertEqual(flat.ndim, 1) + self.assertEqual(flat.shape, (6,)) + self.assertEqual(flat.size, 6) + self.assertEqual(flat.kwvals, {}) + flat_vals = vals.reshape(-1, 50) + np.testing.assert_allclose(flat.vals, flat_vals.reshape((1, 6, 50))) + + bound_circuits = list(flat.bind_all(self.circuit).reshape(6)) + self.assertEqual(len(bound_circuits), 6) + for i in range(6): + self.assertEqual(bound_circuits[i], self.circuit.assign_parameters(flat_vals[i])) + + def test_reshape(self): + """Test reshape""" + vals = np.linspace(0, 1, 300).reshape((2, 3, 50)) + + with self.subTest("reshape"): + ba = BindingsArray(vals) + reshape_ba = ba.reshape((3, 2)) + self.assertEqual(reshape_ba.num_parameters, 50) + self.assertEqual(reshape_ba.ndim, 2) + self.assertEqual(reshape_ba.shape, (3, 2)) + self.assertEqual(reshape_ba.size, 6) + self.assertEqual(reshape_ba.kwvals, {}) + reshape_vals = vals.reshape((3, 2, 50)) + np.testing.assert_allclose(reshape_ba.vals, reshape_vals.reshape((1, 3, 2, 50))) + + circuit = self.circuit + bound_circuits = reshape_ba.bind_all(circuit) + self.assertEqual(bound_circuits.shape, (3, 2)) + self.assertEqual(bound_circuits[0, 0], circuit.assign_parameters(reshape_vals[0, 0])) + self.assertEqual(bound_circuits[0, 1], circuit.assign_parameters(reshape_vals[0, 1])) + self.assertEqual(bound_circuits[1, 0], circuit.assign_parameters(reshape_vals[1, 0])) + self.assertEqual(bound_circuits[1, 1], circuit.assign_parameters(reshape_vals[1, 1])) + self.assertEqual(bound_circuits[2, 0], circuit.assign_parameters(reshape_vals[2, 0])) + self.assertEqual(bound_circuits[2, 1], circuit.assign_parameters(reshape_vals[2, 1])) + + with self.subTest("flatten"): + ba = BindingsArray(vals) + reshape_ba = ba.reshape(6) + self.assertEqual(reshape_ba.num_parameters, 50) + self.assertEqual(reshape_ba.ndim, 1) + self.assertEqual(reshape_ba.shape, (6,)) + self.assertEqual(reshape_ba.size, 6) + self.assertEqual(reshape_ba.kwvals, {}) + reshape_vals = vals.reshape(-1, 50) + np.testing.assert_allclose(reshape_ba.vals, reshape_vals.reshape((1, 6, 50))) + + bound_circuits = list(reshape_ba.bind_all(self.circuit)) + self.assertEqual(len(bound_circuits), 6) + for i in range(6): + self.assertEqual(bound_circuits[i], self.circuit.assign_parameters(reshape_vals[i])) + + def test_kwvals(self): + """Test constructor with kwvals""" + with self.subTest("binding a single value"): + vals = np.linspace(0, 1, 50) + kwvals = {self.params: vals} + ba = BindingsArray(kwvals=kwvals) + self.assertEqual(ba.num_parameters, 50) + self.assertEqual(ba.ndim, 0) + self.assertEqual(ba.shape, ()) + self.assertEqual(ba.size, 1) + self.assertEqual(ba.vals, []) + self.assertEqual(ba.kwvals, {tuple(param.name for param in self.params): vals}) + + bound_circuit = ba.bind(self.circuit, ()) + self.assertEqual(bound_circuit, self.circuit.assign_parameters(vals)) + + with self.subTest("binding an array"): + vals = np.linspace(0, 1, 300).reshape((2, 3, 50)) + kwvals = {self.params: vals} + ba = BindingsArray(kwvals=kwvals) + self.assertEqual(ba.num_parameters, 50) + self.assertEqual(ba.ndim, 2) + self.assertEqual(ba.shape, (2, 3)) + self.assertEqual(ba.size, 6) + self.assertEqual(ba.vals, []) + self.assertEqual(ba.kwvals, {tuple(param.name for param in self.params): vals}) + + bound_circuits = ba.bind_all(self.circuit) + self.assertEqual(bound_circuits.shape, (2, 3)) + self.assertEqual(bound_circuits[0, 0], self.circuit.assign_parameters(vals[0, 0])) + self.assertEqual(bound_circuits[0, 1], self.circuit.assign_parameters(vals[0, 1])) + self.assertEqual(bound_circuits[0, 2], self.circuit.assign_parameters(vals[0, 2])) + self.assertEqual(bound_circuits[1, 0], self.circuit.assign_parameters(vals[1, 0])) + self.assertEqual(bound_circuits[1, 1], self.circuit.assign_parameters(vals[1, 1])) + self.assertEqual(bound_circuits[1, 2], self.circuit.assign_parameters(vals[1, 2])) + + with self.subTest("binding a single param"): + vals = np.linspace(0, 1, 50) + kwvals = {self.params[0]: vals} + ba = BindingsArray(kwvals=kwvals) + self.assertEqual(ba.num_parameters, 1) + self.assertEqual(ba.ndim, 1) + self.assertEqual(ba.shape, (50,)) + self.assertEqual(ba.size, 50) + self.assertEqual(ba.vals, []) + self.assertEqual(list(ba.kwvals.keys()), [(self.params[0].name,)]) + np.testing.assert_allclose(list(ba.kwvals.values()), [vals[..., np.newaxis]]) + + def test_vals_kwvals(self): + """Test constructor with vals and kwvals""" + with self.subTest("binding a single value"): + vals = np.linspace(0, 1, 50) + kwvals = {tuple(self.params[20:]): vals[20:]} + ba = BindingsArray(vals=vals[:20], kwvals=kwvals) + self.assertEqual(ba.num_parameters, 50) + self.assertEqual(ba.ndim, 0) + self.assertEqual(ba.shape, ()) + self.assertEqual(ba.size, 1) + np.testing.assert_allclose(ba.vals, vals[np.newaxis, :20]) + self.assertEqual(ba.kwvals, {tuple(p.name for p in k): v for k, v in kwvals.items()}) + + bound_circuit = ba.bind(self.circuit, ()) + self.assertEqual(bound_circuit, self.circuit.assign_parameters(vals)) + + with self.subTest("binding an array"): + vals = np.linspace(0, 1, 300).reshape((2, 3, 50)) + kwvals = {tuple(self.params[20:]): vals[:, :, 20:]} + ba = BindingsArray(vals=vals[:, :, :20], kwvals=kwvals) + self.assertEqual(ba.num_parameters, 50) + self.assertEqual(ba.ndim, 2) + self.assertEqual(ba.shape, (2, 3)) + self.assertEqual(ba.size, 6) + np.testing.assert_allclose(ba.vals, vals[np.newaxis, :, :, :20]) + self.assertEqual(ba.kwvals, {tuple(p.name for p in k): v for k, v in kwvals.items()}) + + bound_circuits = ba.bind_all(self.circuit) + self.assertEqual(bound_circuits.shape, (2, 3)) + self.assertEqual(bound_circuits[0, 0], self.circuit.assign_parameters(vals[0, 0])) + self.assertEqual(bound_circuits[0, 1], self.circuit.assign_parameters(vals[0, 1])) + self.assertEqual(bound_circuits[0, 2], self.circuit.assign_parameters(vals[0, 2])) + self.assertEqual(bound_circuits[1, 0], self.circuit.assign_parameters(vals[1, 0])) + self.assertEqual(bound_circuits[1, 1], self.circuit.assign_parameters(vals[1, 1])) + self.assertEqual(bound_circuits[1, 2], self.circuit.assign_parameters(vals[1, 2])) + + with self.subTest("len(val) == 1 and len(kwvals) > 0"): + ba = BindingsArray( + vals=np.empty((5, 10)), + kwvals={(Parameter("a"), Parameter("b")): np.empty((5, 10, 2))}, + ) + self.assertEqual(ba.num_parameters, 3) + self.assertEqual(ba.ndim, 2) + self.assertEqual(ba.shape, (5, 10)) + self.assertEqual(ba.size, 50) + + def test_simple_kwvals(self): + """Test simple constructions of BindingsArrays using kwvals.""" + with self.subTest("Single number kwval 1"): + ba = BindingsArray(kwvals={Parameter("a"): 1.0}) + self.assertEqual(ba.shape, ()) + + with self.subTest("Single number kwval 1 with shape"): + ba = BindingsArray(kwvals={Parameter("a"): 1.0}, shape=()) + self.assertEqual(ba.shape, ()) + + with self.subTest("Single number kwval 1 ndarray"): + ba = BindingsArray(kwvals={Parameter("a"): np.array(1.0)}) + self.assertEqual(ba.shape, ()) + + with self.subTest("Single number kwval 2"): + ba = BindingsArray(kwvals={Parameter("a"): 1.0, Parameter("b"): 0.0}) + self.assertEqual(ba.shape, ()) + + with self.subTest("Empty kwval"): + ba = BindingsArray(kwvals={Parameter("a"): []}) + self.assertEqual(ba.shape, (0,)) + + with self.subTest("Single kwval"): + ba = BindingsArray(kwvals={Parameter("a"): [0.0]}) + self.assertEqual(ba.shape, (1,)) + + with self.subTest("Single kwval ndarray"): + ba = BindingsArray(kwvals={Parameter("a"): np.array([0.0])}) + self.assertEqual(ba.shape, (1,)) + + with self.subTest("Multi kwval"): + ba = BindingsArray(kwvals={Parameter("a"): [0.0, 1.0]}) + self.assertEqual(ba.shape, (2,)) + + with self.subTest("Multiple kwvals empty"): + ba = BindingsArray(kwvals={Parameter("a"): [], Parameter("b"): []}) + self.assertEqual(ba.shape, (0,)) + + with self.subTest("Multiple kwvals single"): + ba = BindingsArray(kwvals={Parameter("a"): [0.0], Parameter("b"): [1.0]}) + self.assertEqual(ba.shape, (1,)) + + with self.subTest("Multiple kwvals multi"): + ba = BindingsArray(kwvals={Parameter("a"): [0.0, 1.0], Parameter("b"): [1.0, 0.0]}) + self.assertEqual(ba.shape, (2,)) + + def test_empty(self): + """Test simple constructions of BindingsArrays with empty cases""" + with self.subTest("Empty 1"): + ba = BindingsArray() + self.assertEqual(ba.shape, ()) + + with self.subTest("Empty 2"): + ba = BindingsArray([], shape=()) + self.assertEqual(ba.shape, ()) + + with self.subTest("Empty 3"): + ba = BindingsArray([], {}, shape=()) + self.assertEqual(ba.shape, ()) + + with self.subTest("Empty 4"): + ba = BindingsArray(shape=()) + self.assertEqual(ba.shape, ()) + + with self.subTest("Empty 5"): + ba = BindingsArray(kwvals={}, shape=()) + self.assertEqual(ba.shape, ()) + + def test_simple_vals(self): + """Test simple constructions of BindingsArrays using vals.""" + with self.subTest("0-d vals"): + ba = BindingsArray([1, 2, 3]) + self.assertEqual(ba.shape, ()) + # ba.vals => [array([1]), array([2]), array([3])] + self.assertEqual(len(ba.vals), 3) + self.assertEqual(ba.vals[0], 1) + self.assertEqual(ba.vals[1], 2) + self.assertEqual(ba.vals[2], 3) + + with self.subTest("1-d vals"): + ba = BindingsArray([[1, 2, 3]]) + self.assertEqual(ba.shape, ()) + # ba.vals => [array([1, 2, 3])] + self.assertEqual(len(ba.vals), 1) + np.testing.assert_allclose(ba.vals[0], [1, 2, 3]) + + with self.subTest("1-d vals ndarray"): + ba = BindingsArray(np.array([1, 2, 3])) + self.assertEqual(ba.shape, ()) + # ba.vals => [array([1, 2, 3])] + self.assertEqual(len(ba.vals), 1) + np.testing.assert_allclose(ba.vals[0], [1, 2, 3]) + + with self.subTest("2-d vals"): + ba = BindingsArray([[[1, 2, 3]]]) + self.assertEqual(ba.shape, (1,)) + self.assertEqual(len(ba.vals), 1) + np.testing.assert_allclose(ba.vals[0], [[1, 2, 3]]) + + with self.subTest("2-d vals ndarray"): + ba = BindingsArray(np.array([[1, 2, 3]])) + self.assertEqual(ba.shape, (1,)) + self.assertEqual(len(ba.vals), 1) + np.testing.assert_allclose(ba.vals[0], [[1, 2, 3]]) diff --git a/test/python/primitives/containers/test_data_bin.py b/test/python/primitives/containers/test_data_bin.py new file mode 100644 index 000000000000..003f0a997fc1 --- /dev/null +++ b/test/python/primitives/containers/test_data_bin.py @@ -0,0 +1,64 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 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. + + +"""Unit tests for DataBin.""" + + +import numpy as np +import numpy.typing as npt + +from qiskit.primitives.containers import make_data_bin +from qiskit.primitives.containers.data_bin import DataBin, DataBinMeta +from qiskit.test import QiskitTestCase + + +class DataBinTestCase(QiskitTestCase): + """Test the DataBin class.""" + + def test_make_databin(self): + """Test the make_databin() function.""" + data_bin_cls = make_data_bin( + [("alpha", npt.NDArray[np.uint16]), ("beta", np.ndarray)], shape=(10, 20) + ) + + self.assertTrue(issubclass(type(data_bin_cls), DataBinMeta)) + self.assertTrue(issubclass(data_bin_cls, DataBin)) + self.assertEqual(data_bin_cls._FIELDS, ("alpha", "beta")) + self.assertEqual(data_bin_cls._FIELD_TYPES, (npt.NDArray[np.uint16], np.ndarray)) + + alpha = np.empty((10, 20), dtype=np.uint16) + beta = np.empty((10, 20), dtype=int) + my_bin = data_bin_cls(alpha, beta) + self.assertTrue(np.all(my_bin.alpha == alpha)) + self.assertTrue(np.all(my_bin.beta == beta)) + self.assertTrue("alpha=" in str(my_bin)) + self.assertTrue(str(my_bin).startswith("DataBin<10,20>")) + + my_bin = data_bin_cls(beta=beta, alpha=alpha) + self.assertTrue(np.all(my_bin.alpha == alpha)) + self.assertTrue(np.all(my_bin.beta == beta)) + + def test_make_databin_no_shape(self): + """Test the make_databin() function with no shape.""" + data_bin_cls = make_data_bin([("alpha", dict), ("beta", int)]) + + self.assertTrue(issubclass(type(data_bin_cls), DataBinMeta)) + self.assertTrue(issubclass(data_bin_cls, DataBin)) + self.assertEqual(data_bin_cls._FIELDS, ("alpha", "beta")) + self.assertEqual(data_bin_cls._FIELD_TYPES, (dict, int)) + + my_bin = data_bin_cls({1: 2}, 5) + self.assertEqual(my_bin.alpha, {1: 2}) + self.assertEqual(my_bin.beta, 5) + self.assertTrue("alpha=" in str(my_bin)) + self.assertTrue(">" not in str(my_bin)) diff --git a/test/python/primitives/containers/test_observables_array.py b/test/python/primitives/containers/test_observables_array.py new file mode 100644 index 000000000000..f3b08cec9229 --- /dev/null +++ b/test/python/primitives/containers/test_observables_array.py @@ -0,0 +1,306 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 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. + +"""Test ObservablesArray""" + +import itertools as it + +import ddt +import numpy as np + +import qiskit.quantum_info as qi +from qiskit.primitives import ObservablesArray +from qiskit.test import QiskitTestCase + + +@ddt.ddt +class ObservablesArrayTestCase(QiskitTestCase): + """Test the ObservablesArray class""" + + @ddt.data(0, 1, 2) + def test_format_observable_str(self, num_qubits): + """Test format_observable for allowed basis str input""" + for chars in it.permutations(ObservablesArray.ALLOWED_BASIS, num_qubits): + label = "".join(chars) + obs = ObservablesArray.format_observable(label) + self.assertEqual(obs, {label: 1}) + + def test_format_observable_custom_basis(self): + """Test format_observable for custom allowed basis""" + + class PauliArray(ObservablesArray): + """Custom array allowing only Paulis, not projectors""" + + ALLOWED_BASIS = "IXYZ" + + with self.assertRaises(ValueError): + PauliArray.format_observable("0101") + for p in qi.pauli_basis(1): + obs = PauliArray.format_observable(p) + self.assertEqual(obs, {p.to_label(): 1}) + + @ddt.data("iXX", "012", "+/-") + def test_format_observable_invalid_str(self, basis): + """Test format_observable for Pauli input""" + with self.assertRaises(ValueError): + ObservablesArray.format_observable(basis) + + @ddt.data(1, 2, 3) + def test_format_observable_pauli(self, num_qubits): + """Test format_observable for Pauli input""" + for p in qi.pauli_basis(num_qubits): + obs = ObservablesArray.format_observable(p) + self.assertEqual(obs, {p.to_label(): 1}) + + @ddt.data(0, 1, 2, 3) + def test_format_observable_phased_pauli(self, phase): + """Test format_observable for phased Pauli input""" + pauli = qi.Pauli("IXYZ") + pauli.phase = phase + coeff = (-1j) ** phase + obs = ObservablesArray.format_observable(pauli) + self.assertIsInstance(obs, dict) + self.assertEqual(list(obs.keys()), ["IXYZ"]) + np.testing.assert_allclose( + list(obs.values()), [coeff], err_msg=f"Wrong value for Pauli {pauli}" + ) + + @ddt.data("+IXYZ", "-IXYZ", "iIXYZ", "+iIXYZ", "-IXYZ") + def test_format_observable_phased_pauli_str(self, pauli): + """Test format_observable for phased Pauli input""" + pauli = qi.Pauli(pauli) + coeff = (-1j) ** pauli.phase + obs = ObservablesArray.format_observable(pauli) + self.assertIsInstance(obs, dict) + self.assertEqual(list(obs.keys()), ["IXYZ"]) + np.testing.assert_allclose( + list(obs.values()), [coeff], err_msg=f"Wrong value for Pauli {pauli}" + ) + + def test_format_observable_phased_sparse_pauli_op(self): + """Test format_observable for SparsePauliOp input with phase paulis""" + op = qi.SparsePauliOp(["+I", "-X", "iY", "-iZ"], [1, 2, 3, 4]) + obs = ObservablesArray.format_observable(op) + self.assertIsInstance(obs, dict) + self.assertEqual(len(obs), 4) + self.assertEqual(sorted(obs.keys()), sorted(["I", "X", "Y", "Z"])) + np.testing.assert_allclose([obs[i] for i in ["I", "X", "Y", "Z"]], [1, -2, 3j, -4j]) + + def test_format_observable_zero_sparse_pauli_op(self): + """Test format_observable for SparsePauliOp input with zero val coeffs""" + op = qi.SparsePauliOp(["I", "X", "Y", "Z"], [0, 0, 0, 1]) + obs = ObservablesArray.format_observable(op) + self.assertIsInstance(obs, dict) + self.assertEqual(len(obs), 1) + self.assertEqual(sorted(obs.keys()), ["Z"]) + self.assertEqual(obs["Z"], 1) + + def test_format_observable_duplicate_sparse_pauli_op(self): + """Test format_observable for SparsePauliOp wiht duplicate paulis""" + op = qi.SparsePauliOp(["XX", "-XX", "iXX", "-iXX"], [2, 1, 3, 2]) + obs = ObservablesArray.format_observable(op) + self.assertIsInstance(obs, dict) + self.assertEqual(len(obs), 1) + self.assertEqual(list(obs.keys()), ["XX"]) + self.assertEqual(obs["XX"], 1 + 1j) + + def test_format_observable_pauli_mapping(self): + """Test format_observable for pauli-keyed Mapping input""" + mapping = dict(zip(qi.pauli_basis(1), range(1, 5))) + obs = ObservablesArray.format_observable(mapping) + target = {key.to_label(): val for key, val in mapping.items()} + self.assertEqual(obs, target) + + def test_format_invalid_mapping_qubits(self): + """Test an error is raised when different qubits in mapping keys""" + mapping = {"IX": 1, "XXX": 2} + with self.assertRaises(ValueError): + ObservablesArray.format_observable(mapping) + + def test_format_invalid_mapping_basis(self): + """Test an error is raised when keys contain invalid characters""" + mapping = {"XX": 1, "0Z": 2, "02": 3} + with self.assertRaises(ValueError): + ObservablesArray.format_observable(mapping) + + def test_init_nested_list_str(self): + """Test init with nested lists of str""" + obj = [["X", "Y", "Z"], ["0", "1", "+"]] + obs = ObservablesArray(obj) + self.assertEqual(obs.size, 6) + self.assertEqual(obs.shape, (2, 3)) + + def test_init_nested_list_sparse_pauli_op(self): + """Test init with nested lists of SparsePauliOp""" + obj = [[qi.SparsePauliOp(qi.random_pauli_list(2, 3)) for _ in range(3)] for _ in range(5)] + obs = ObservablesArray(obj) + self.assertEqual(obs.size, 15) + self.assertEqual(obs.shape, (5, 3)) + + def test_init_single_sparse_pauli_op(self): + """Test init with single SparsePauliOps""" + obj = qi.SparsePauliOp(qi.random_pauli_list(2, 3)) + obs = ObservablesArray(obj) + self.assertEqual(obs.size, 1) + self.assertEqual(obs.shape, ()) + + def test_init_pauli_list(self): + """Test init with PauliList""" + obs = ObservablesArray(qi.pauli_basis(2)) + self.assertEqual(obs.size, 16) + self.assertEqual(obs.shape, (16,)) + + def test_init_nested_pauli_list(self): + """Test init with nested PauliList""" + obj = [qi.random_pauli_list(2, 3) for _ in range(5)] + obs = ObservablesArray(obj) + self.assertEqual(obs.size, 15) + self.assertEqual(obs.shape, (5, 3)) + + def test_init_ragged_array(self): + """Test init with ragged input""" + obj = [["X", "Y"], ["X", "Y", "Z"]] + with self.assertRaises(ValueError): + ObservablesArray(obj) + + def test_init_validate_false(self): + """Test init validate kwarg""" + obj = [["A", "B", "C"], ["D", "E", "F"]] + obs = ObservablesArray(obj, validate=False) + self.assertEqual(obs.shape, (2, 3)) + self.assertEqual(obs.size, 6) + for i in range(2): + for j in range(3): + self.assertEqual(obs[i, j], obj[i][j]) + + def test_init_validate_true(self): + """Test init validate kwarg""" + obj = [["A", "B", "C"], ["D", "E", "F"]] + with self.assertRaises(ValueError): + ObservablesArray(obj, validate=True) + + @ddt.data(0, 1, 2, 3) + def test_size_and_shape_single(self, ndim): + """Test size and shape method for size=1 array""" + obs = {"XX": 1} + for _ in range(ndim): + obs = [obs] + arr = ObservablesArray(obs, validate=False) + self.assertEqual(arr.size, 1, msg="Incorrect ObservablesArray.size") + self.assertEqual(arr.shape, (1,) * ndim, msg="Incorrect ObservablesArray.shape") + + @ddt.data(0, 1, 2, 3) + def test_tolist_single(self, ndim): + """Test tolist method for size=1 array""" + obs = {"XX": 1} + for _ in range(ndim): + obs = [obs] + arr = ObservablesArray(obs, validate=False) + ls = arr.tolist() + self.assertEqual(ls, obs) + + @ddt.data(0, 1, 2, 3) + def test_array_single(self, ndim): + """Test __array__ method for size=1 array""" + obs = {"XX": 1} + for _ in range(ndim): + obs = [obs] + arr = ObservablesArray(obs, validate=False) + nparr = np.array(arr) + self.assertEqual(nparr.dtype, object) + self.assertEqual(nparr.shape, arr.shape) + self.assertEqual(nparr.size, arr.size) + self.assertTrue(np.all(nparr == np.array(obs))) + + @ddt.data(0, 1, 2, 3) + def test_getitem_single(self, ndim): + """Test __getitem__ method for size=1 array""" + base_obs = {"XX": 1} + obs = base_obs + for _ in range(ndim): + obs = [obs] + arr = ObservablesArray(obs, validate=False) + idx = ndim * (0,) + item = arr[idx] + self.assertEqual(item, base_obs) + + def test_tolist_1d(self): + """Test tolist method""" + obj = ["A", "B", "C", "D"] + obs = ObservablesArray(obj, validate=False) + self.assertEqual(obs.tolist(), obj) + + def test_tolist_2d(self): + """Test tolist method""" + obj = [["A", "B", "C"], ["D", "E", "F"]] + obs = ObservablesArray(obj, validate=False) + self.assertEqual(obs.tolist(), obj) + + def test_array_1d(self): + """Test __array__ dunder method""" + obj = np.array(["A", "B", "C", "D"], dtype=object) + obs = ObservablesArray(obj, validate=False) + self.assertTrue(np.all(np.array(obs) == obj)) + + def test_array_2d(self): + """Test __array__ dunder method""" + obj = np.array([["A", "B", "C"], ["D", "E", "F"]], dtype=object) + obs = ObservablesArray(obj, validate=False) + self.assertTrue(np.all(np.array(obs) == obj)) + + def test_getitem_1d(self): + """Test __getitem__ for 1D array""" + obj = np.array(["A", "B", "C", "D"], dtype=object) + obs = ObservablesArray(obj, validate=False) + for i in range(obj.size): + self.assertEqual(obs[i], obj[i]) + + def test_getitem_2d(self): + """Test __getitem__ for 2D array""" + obj = np.array([["A", "B", "C"], ["D", "E", "F"]], dtype=object) + obs = ObservablesArray(obj, validate=False) + for i in range(obj.shape[0]): + row = obs[i] + self.assertIsInstance(row, ObservablesArray) + self.assertEqual(row.shape, (3,)) + self.assertTrue(np.all(np.array(row) == obj[i])) + + def test_ravel(self): + """Test ravel method""" + bases_flat = qi.pauli_basis(2).to_labels() + bases = [bases_flat[4 * i : 4 * (i + 1)] for i in range(4)] + obs = ObservablesArray(bases) + flat = obs.ravel() + self.assertEqual(flat.ndim, 1) + self.assertEqual(flat.shape, (16,)) + self.assertEqual(flat.size, 16) + for ( + i, + label, + ) in enumerate(bases_flat): + self.assertEqual(flat[i], {label: 1}) + + def test_reshape(self): + """Test reshape method""" + bases = qi.pauli_basis(2) + labels = np.array(bases.to_labels(), dtype=object) + obs = ObservablesArray(qi.pauli_basis(2)) + + for shape in [(16,), (4, 4), (2, 4, 2), (2, 2, 2, 2), (1, 8, 1, 2)]: + with self.subTest(shape): + obs_rs = obs.reshape(shape) + self.assertEqual(obs_rs.shape, shape) + labels_rs = labels.reshape(shape) + for idx in np.ndindex(shape): + self.assertEqual( + obs_rs[idx], {labels_rs[idx]: 1}, msg=f"failed for shape {shape}" + ) diff --git a/test/python/primitives/containers/test_primitive_result.py b/test/python/primitives/containers/test_primitive_result.py new file mode 100644 index 000000000000..25b2f505b21f --- /dev/null +++ b/test/python/primitives/containers/test_primitive_result.py @@ -0,0 +1,44 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 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. + + +"""Unit tests for PrimitiveResult.""" + +import numpy as np +import numpy.typing as npt + +from qiskit.primitives.containers import PrimitiveResult, PubResult, make_data_bin +from qiskit.test import QiskitTestCase + + +class PrimitiveResultCase(QiskitTestCase): + """Test the PrimitiveResult class.""" + + def test_primitive_result(self): + """Test the PrimitiveResult class.""" + data_bin_cls = make_data_bin( + [("alpha", npt.NDArray[np.uint16]), ("beta", np.ndarray)], shape=(10, 20) + ) + + alpha = np.empty((10, 20), dtype=np.uint16) + beta = np.empty((10, 20), dtype=int) + + pub_results = [ + PubResult(data_bin_cls(alpha, beta)), + PubResult(data_bin_cls(alpha, beta)), + ] + result = PrimitiveResult(pub_results, {1: 2}) + + self.assertTrue(result[0] is pub_results[0]) + self.assertTrue(result[1] is pub_results[1]) + self.assertTrue(list(result)[0] is pub_results[0]) + self.assertEqual(len(result), 2) diff --git a/test/python/primitives/containers/test_shape.py b/test/python/primitives/containers/test_shape.py new file mode 100644 index 000000000000..c9c315613e24 --- /dev/null +++ b/test/python/primitives/containers/test_shape.py @@ -0,0 +1,105 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 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. + +"""Test shape.py module""" + + +import numpy as np + +from qiskit.primitives.containers.shape import Shaped, ShapedMixin, array_coerce, shape_tuple +from qiskit.test import QiskitTestCase + + +class DummyShaped(ShapedMixin): + """Dummy ShapedMixin child for testing.""" + + def __init__(self, arr): + super().__init__() + self._shape = arr.shape + self._arr = arr + + def __getitem__(self, arg): + return self._arr[arg] + + +class ShapedTestCase(QiskitTestCase): + """Test the Shaped protocol class""" + + def test_ndarray_is_shaped(self): + """Test that ndarrays are shaped""" + self.assertTrue(isinstance(np.empty((1, 2, 3)), Shaped)) + + def test_mixin_is_shaped(self): + """Test that ShapedMixin is shaped""" + self.assertTrue(isinstance(DummyShaped(np.empty((1, 2, 3))), Shaped)) + + +class ShapedMixinTestCase(QiskitTestCase): + """Test the ShapedMixin class""" + + def test_shape(self): + """Test the shape attribute.""" + self.assertEqual(DummyShaped(np.empty((1, 2, 3))).shape, (1, 2, 3)) + self.assertEqual(DummyShaped(np.empty(())).shape, ()) + + def test_ndim(self): + """Test the ndim attribute.""" + self.assertEqual(DummyShaped(np.empty(())).ndim, 0) + self.assertEqual(DummyShaped(np.empty((1, 2, 3))).ndim, 3) + + def test_size(self): + """Test the size attribute.""" + self.assertEqual(DummyShaped(np.empty(())).size, 1) + self.assertEqual(DummyShaped(np.empty((0, 1))).size, 0) + self.assertEqual(DummyShaped(np.empty((1, 2, 3))).size, 6) + + def test_getitem(self): + """Missing docstring.""" + arr = np.arange(100).reshape(2, 5, 10) + np.testing.assert_allclose(DummyShaped(arr)[:, 0, :2], arr[:, 0, :2]) + + +class ArrayCoerceTestCase(QiskitTestCase): + """Test array_coerce() function.""" + + def test_shaped(self): + """Test that array_coerce() works with ShapedMixin objects.""" + sh = DummyShaped(np.empty((1, 2, 3))) + self.assertIs(sh, array_coerce(sh)) + + def test_ndarray(self): + """Test that array_coerce() works with ndarray objects.""" + sh = np.arange(100).reshape(5, 2, 2, 5) + np.testing.assert_allclose(sh, array_coerce(sh)) + + +class ShapeTupleTestCase(QiskitTestCase): + """Test shape_tuple() function.""" + + def test_int(self): + """Test shape_tuple() with int inputs.""" + self.assertEqual(shape_tuple(), ()) + self.assertEqual(shape_tuple(5), (5,)) + self.assertEqual(shape_tuple(5, 10), (5, 10)) + self.assertEqual(shape_tuple(1e2), (100,)) + + def test_nested(self): + """Test shape_tuple() with nested inputs.""" + self.assertEqual(shape_tuple(0, (), (1, (2, (3,)), (4, 5))), (0, 1, 2, 3, 4, 5)) + + def test_exceptions(self): + """Test shape_tuple() raises correctly.""" + with self.assertRaisesRegex(ValueError, "iterable or an integer"): + shape_tuple(None) + + with self.assertRaisesRegex(ValueError, "iterable or an integer"): + shape_tuple(1.5) From cb4762f77b55ea33437354034ed698e69358d710 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Wed, 10 Jan 2024 02:28:06 +0000 Subject: [PATCH 5/9] Fix quadratic construction time of `QuantumCircuit` (#11517) * Fix quadratic construction time of `QuantumCircuit` The current implementation of `CircuitData.add_qubit` (ditto `clbit`) has linear time complexity because it reconstructs the list on each addition, while the `CircuitData.qubits` getter silently clones the list on return. This was intended to avoid inadvertant Python-space modifications from getting the Rust components out of sync, but in practice, this cost being hidden in a standard attribute access makes it very easy to introduce accidental quadratic dependencies. In this case, `QuantumCircuit(num_qubits)` and subsequent `qc.append(..., qc.qubits)` calls were accidentally quadratic in `num_qubits` due to repeated accesses of `QuantumCircuit.qubits`. * Restore explicit Python token * Strengthen warnings --- .../src/quantum_circuit/circuit_data.rs | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/crates/accelerate/src/quantum_circuit/circuit_data.rs b/crates/accelerate/src/quantum_circuit/circuit_data.rs index 76c3c78d57ed..033bd2ea7005 100644 --- a/crates/accelerate/src/quantum_circuit/circuit_data.rs +++ b/crates/accelerate/src/quantum_circuit/circuit_data.rs @@ -213,32 +213,33 @@ impl CircuitData { Ok((ty, args, None::<()>, self_.iter()?).into_py(py)) } - /// Returns the current sequence of registered :class:`.Qubit` - /// instances as a list. + /// Returns the current sequence of registered :class:`.Qubit` instances as a list. /// - /// .. note:: + /// .. warning:: /// - /// This list is not kept in sync with the container. + /// Do not modify this list yourself. It will invalidate the :class:`CircuitData` data + /// structures. /// /// Returns: /// list(:class:`.Qubit`): The current sequence of registered qubits. #[getter] - pub fn qubits(&self, py: Python<'_>) -> PyObject { - PyList::new(py, self.qubits.as_ref(py)).into_py(py) + pub fn qubits(&self, py: Python<'_>) -> Py { + self.qubits.clone_ref(py) } /// Returns the current sequence of registered :class:`.Clbit` /// instances as a list. /// - /// .. note:: + /// .. warning:: /// - /// This list is not kept in sync with the container. + /// Do not modify this list yourself. It will invalidate the :class:`CircuitData` data + /// structures. /// /// Returns: /// list(:class:`.Clbit`): The current sequence of registered clbits. #[getter] - pub fn clbits(&self, py: Python<'_>) -> PyObject { - PyList::new(py, self.clbits.as_ref(py)).into_py(py) + pub fn clbits(&self, py: Python<'_>) -> Py { + self.clbits.clone_ref(py) } /// Registers a :class:`.Qubit` instance. @@ -251,7 +252,13 @@ impl CircuitData { /// ValueError: The specified ``bit`` is already present and flag ``strict`` /// was provided. #[pyo3(signature = (bit, *, strict=true))] - pub fn add_qubit(&mut self, py: Python<'_>, bit: &PyAny, strict: bool) -> PyResult<()> { + pub fn add_qubit(&mut self, py: Python, bit: &PyAny, strict: bool) -> PyResult<()> { + if self.qubits_native.len() != self.qubits.as_ref(bit.py()).len() { + return Err(PyRuntimeError::new_err(concat!( + "This circuit's 'qubits' list has become out of sync with the circuit data.", + " Did something modify it?" + ))); + } let idx: BitType = self.qubits_native.len().try_into().map_err(|_| { PyRuntimeError::new_err( "The number of qubits in the circuit has exceeded the maximum capacity", @@ -263,7 +270,7 @@ impl CircuitData { .is_ok() { self.qubits_native.push(bit.into_py(py)); - self.qubits = PyList::new(py, &self.qubits_native).into_py(py); + self.qubits.as_ref(py).append(bit)?; } else if strict { return Err(PyValueError::new_err(format!( "Existing bit {:?} cannot be re-added in strict mode.", @@ -283,7 +290,13 @@ impl CircuitData { /// ValueError: The specified ``bit`` is already present and flag ``strict`` /// was provided. #[pyo3(signature = (bit, *, strict=true))] - pub fn add_clbit(&mut self, py: Python<'_>, bit: &PyAny, strict: bool) -> PyResult<()> { + pub fn add_clbit(&mut self, py: Python, bit: &PyAny, strict: bool) -> PyResult<()> { + if self.clbits_native.len() != self.clbits.as_ref(bit.py()).len() { + return Err(PyRuntimeError::new_err(concat!( + "This circuit's 'clbits' list has become out of sync with the circuit data.", + " Did something modify it?" + ))); + } let idx: BitType = self.clbits_native.len().try_into().map_err(|_| { PyRuntimeError::new_err( "The number of clbits in the circuit has exceeded the maximum capacity", @@ -295,7 +308,7 @@ impl CircuitData { .is_ok() { self.clbits_native.push(bit.into_py(py)); - self.clbits = PyList::new(py, &self.clbits_native).into_py(py); + self.clbits.as_ref(py).append(bit)?; } else if strict { return Err(PyValueError::new_err(format!( "Existing bit {:?} cannot be re-added in strict mode.", From b8d21aee18972fdaeb8e043614c509d89ee00d58 Mon Sep 17 00:00:00 2001 From: Naoki Kanazawa Date: Wed, 10 Jan 2024 22:57:32 +0900 Subject: [PATCH 6/9] Legacy code removal in pass manager (#11448) * Remove flow controller subclass .append * Update builtin pass managers to avoid append pattern * Remove running pass manager * Remove PropertySet and Fenced objects from transpile * Remove _pass_sets and let PassManager generate pass list on the fly This is necessary because kwargs is going to be removed from append method. Current pass list is generated based on the kwargs. * Remove kwargs from PassManager.append and .replace This also removes FlowController factory class * Remove max_iteration keyword from append and replace * Reno * minor: fix test and docs * Remove qiskit.passmanager.flow_controllers.FlowController * Remove .passes method from PassManager and StagedPassManager * Update reference image of pass manager drawer test * minor: fix drawer label generation * Revert "Update builtin pass managers to avoid append pattern" This reverts commit 4bcfcd68ae9fc5b8a9bfbdfad22aaa663ac972f3. * Minimum update of builtin pass managers --- qiskit/passmanager/__init__.py | 2 - qiskit/passmanager/flow_controllers.py | 222 +------------ qiskit/transpiler/__init__.py | 13 +- qiskit/transpiler/fencedobjs.py | 78 ----- qiskit/transpiler/passmanager.py | 196 +---------- .../preset_passmanagers/builtin_plugins.py | 118 ++++--- .../transpiler/preset_passmanagers/common.py | 78 +++-- qiskit/transpiler/propertyset.py | 19 -- qiskit/transpiler/runningpassmanager.py | 174 ---------- .../pass_manager_visualization.py | 72 ++++- ...upgrade-pass-manager-98aa64edde67b5bb.yaml | 79 +++++ .../test_commutative_cancellation.py | 45 ++- .../transpiler/test_gates_in_basis_pass.py | 13 +- .../test_optimize_swap_before_measure.py | 13 +- test/python/transpiler/test_pass_scheduler.py | 306 ++++++++---------- test/python/transpiler/test_passmanager.py | 16 +- .../transpiler/test_preset_passmanagers.py | 26 +- ...st_remove_diagonal_gates_before_measure.py | 7 +- .../test_remove_reset_in_zero_state.py | 7 +- .../transpiler/test_staged_passmanager.py | 29 +- .../references/pass_manager_standard.dot | 4 +- .../references/pass_manager_style.dot | 4 +- .../visualization/test_pass_manager_drawer.py | 9 +- 23 files changed, 497 insertions(+), 1033 deletions(-) delete mode 100644 qiskit/transpiler/fencedobjs.py delete mode 100644 qiskit/transpiler/propertyset.py delete mode 100644 qiskit/transpiler/runningpassmanager.py create mode 100644 releasenotes/notes/upgrade-pass-manager-98aa64edde67b5bb.yaml diff --git a/qiskit/passmanager/__init__.py b/qiskit/passmanager/__init__.py index 579a8569b750..a84f37abbf03 100644 --- a/qiskit/passmanager/__init__.py +++ b/qiskit/passmanager/__init__.py @@ -209,7 +209,6 @@ def digit_condition(property_set): .. autosummary:: :toctree: ../stubs/ - FlowController FlowControllerLinear ConditionalController DoWhileController @@ -232,7 +231,6 @@ def digit_condition(property_set): from .passmanager import BasePassManager from .flow_controllers import ( - FlowController, FlowControllerLinear, ConditionalController, DoWhileController, diff --git a/qiskit/passmanager/flow_controllers.py b/qiskit/passmanager/flow_controllers.py index eb06202e8673..c7d952d048dd 100644 --- a/qiskit/passmanager/flow_controllers.py +++ b/qiskit/passmanager/flow_controllers.py @@ -15,9 +15,8 @@ import logging from collections.abc import Callable, Iterable, Generator -from typing import Type, Any +from typing import Any -from qiskit.utils.deprecation import deprecate_func from .base_tasks import BaseController, Task from .compilation_status import PassManagerState, PropertySet from .exceptions import PassManagerError @@ -45,31 +44,6 @@ def passes(self) -> list[Task]: """Alias of tasks for backward compatibility.""" return list(self.tasks) - @deprecate_func( - since="0.45.0", - additional_msg="All tasks must be provided at construction time of the controller object.", - ) - def append( - self, - passes: Task | list[Task], - ): - """Add new task to pipeline. - - Args: - passes: A new task or list of tasks to add. - """ - if not isinstance(passes, Iterable): - passes = [passes] - - tasks = list(self.tasks) - for task in passes: - if not isinstance(task, Task): - raise TypeError( - f"New task {task} is not a valid pass manager pass or flow controller." - ) - tasks.append(task) - self.tasks = tuple(tasks) - def iter_tasks(self, state: PassManagerState) -> Generator[Task, PassManagerState, None]: for task in self.tasks: state = yield task @@ -101,31 +75,6 @@ def passes(self) -> list[Task]: """Alias of tasks for backward compatibility.""" return list(self.tasks) - @deprecate_func( - since="0.45.0", - additional_msg="All tasks must be provided at construction time of the controller object.", - ) - def append( - self, - passes: Task | list[Task], - ): - """Add new task to pipeline. - - Args: - passes: A new task or list of tasks to add. - """ - if not isinstance(passes, Iterable): - passes = [passes] - - tasks = list(self.tasks) - for task in passes: - if not isinstance(task, Task): - raise TypeError( - f"New task {task} is not a valid pass manager pass or flow controller." - ) - tasks.append(task) - self.tasks = tuple(tasks) - def iter_tasks(self, state: PassManagerState) -> Generator[Task, PassManagerState, None]: max_iteration = self._options.get("max_iteration", 1000) for _ in range(max_iteration): @@ -161,176 +110,7 @@ def passes(self) -> list[Task]: """Alias of tasks for backward compatibility.""" return list(self.tasks) - @deprecate_func( - since="0.45.0", - additional_msg="All tasks must be provided at construction time of the controller object.", - ) - def append( - self, - passes: Task | list[Task], - ): - """Add new task to pipeline. - - Args: - passes: A new task or list of tasks to add. - """ - if not isinstance(passes, Iterable): - passes = [passes] - - tasks = list(self.tasks) - for task in passes: - if not isinstance(task, Task): - raise TypeError( - f"New task {task} is not a valid pass manager pass or flow controller." - ) - tasks.append(task) - self.tasks = tuple(tasks) - def iter_tasks(self, state: PassManagerState) -> Generator[Task, PassManagerState, None]: if self.condition(state.property_set): for task in self.tasks: state = yield task - - -class FlowController(BaseController): - """A legacy factory for other flow controllers. - - .. warning:: - - This class is primarily for compatibility with legacy versions of Qiskit, and in general, - you should prefer simply instantiating the controller you want, and adding it to the - relevant :class:`.PassManager` or other controller. Its use is deprecated. - - This allows syntactic sugar for writing pipelines. For example:: - - FlowController.add_flow_controller("my_condition", CustomController) - - controller = FlowController.controller_factory( - [PassA(), PassB()], - {"max_iteration": 1000}, - condition=lambda prop_set: prop_set["x"] == 0, - do_while=lambda prop_set: prop_set["x"] < 100, - my_condition=lambda prop_set: prop_set["y"] = "abc", - ) - - This creates a nested flow controller that runs when the value :code:`x` in the - :class:`.PropertySet` is zero and repeats the pipeline until the value becomes 100. - In each innermost loop, the custom iteration condition provided by - the ``CustomController`` is also evaluated. - - .. warning:: - - :class:`.BaseController` must be directly subclassed to define a custom flow controller. - This class provides a controller factory method, which consumes a class variable - :attr:`.registered_controllers`. Subclassing FlowController may cause - unexpected behavior in the factory method. - Note that factory method implicitly determines the priority of the builtin controllers - when multiple controllers are called together, - and the behavior of generated controller is hardly debugged. - """ - - registered_controllers = { - "condition": ConditionalController, - "do_while": DoWhileController, - } - hierarchy = [ - "condition", - "do_while", - ] - - @classmethod - @deprecate_func( - since="0.45.0", - additional_msg=( - "Controller object must be explicitly instantiated. " - "Building controller with keyword arguments may yield race condition when " - "multiple keyword arguments are provided together, which is likely unsafe." - ), - ) - def controller_factory( - cls, - passes: Task | list[Task], - options: dict, - **controllers, - ): - """Create a new flow controller with normalization. - - Args: - passes: A list of optimization tasks. - options: Option for this flow controller. - controllers: Dictionary of controller callables keyed on flow controller alias. - - Returns: - An instance of normalized flow controller. - """ - if None in controllers.values(): - raise PassManagerError("The controller needs a callable. Value cannot be None.") - - if isinstance(passes, BaseController): - instance = passes - else: - instance = FlowControllerLinear(passes, options=options) - - if controllers: - # Alias in higher hierarchy becomes outer controller. - for alias in cls.hierarchy[::-1]: - if alias not in controllers: - continue - class_type = cls.registered_controllers[alias] - init_kwargs = { - "options": options, - alias: controllers.pop(alias), - } - instance = class_type([instance], **init_kwargs) - - return instance - - @classmethod - @deprecate_func( - since="0.45.0", - additional_msg=( - "Controller factory method is deprecated and managing the custom flow controllers " - "with alias no longer helps building the task pipeline. " - "Controllers must be explicitly instantiated and appended to the pipeline." - ), - ) - def add_flow_controller( - cls, - name: str, - controller: Type[BaseController], - ): - """Adds a flow controller. - - Args: - name: Alias of controller class in the namespace. - controller: Flow controller class. - """ - cls.registered_controllers[name] = controller - if name not in cls.hierarchy: - cls.hierarchy.append(name) - - @classmethod - @deprecate_func( - since="0.45.0", - additional_msg=( - "Controller factory method is deprecated and managing the custom flow controllers " - "with alias no longer helps building the task pipeline. " - "Controllers must be explicitly instantiated and appended to the pipeline." - ), - ) - def remove_flow_controller( - cls, - name: str, - ): - """Removes a flow controller. - - Args: - name: Alias of the controller to remove. - - Raises: - KeyError: If the controller to remove was not registered. - """ - if name not in cls.hierarchy: - raise KeyError("Flow controller not found: %s" % name) - del cls.registered_controllers[name] - cls.hierarchy.remove(name) diff --git a/qiskit/transpiler/__init__.py b/qiskit/transpiler/__init__.py index 71f596c95dad..bfc0e622d5d2 100644 --- a/qiskit/transpiler/__init__.py +++ b/qiskit/transpiler/__init__.py @@ -1231,15 +1231,6 @@ InstructionDurations -Fenced Objects --------------- - -.. autosummary:: - :toctree: ../stubs/ - - FencedPropertySet - FencedDAGCircuit - Abstract Passes --------------- @@ -1262,14 +1253,13 @@ # For backward compatibility from qiskit.passmanager import ( - FlowController, ConditionalController, DoWhileController, ) +from qiskit.passmanager.compilation_status import PropertySet from .passmanager import PassManager, StagedPassManager from .passmanager_config import PassManagerConfig -from .propertyset import PropertySet # pylint: disable=no-name-in-module from .exceptions import ( TranspilerError, TranspilerAccessError, @@ -1277,7 +1267,6 @@ LayoutError, CircuitTooWideForTarget, ) -from .fencedobjs import FencedDAGCircuit, FencedPropertySet from .basepasses import AnalysisPass, TransformationPass from .coupling import CouplingMap from .layout import Layout, TranspileLayout diff --git a/qiskit/transpiler/fencedobjs.py b/qiskit/transpiler/fencedobjs.py deleted file mode 100644 index 8113e58d76d9..000000000000 --- a/qiskit/transpiler/fencedobjs.py +++ /dev/null @@ -1,78 +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. - -"""Fenced objects are wraps for raising TranspilerError when they are modified.""" - -from qiskit.utils.deprecation import deprecate_func -from .exceptions import TranspilerError - - -class FencedObject: - """Given an instance and a list of attributes to fence, raises a TranspilerError when one - of these attributes is accessed.""" - - @deprecate_func( - since="0.45.0", - additional_msg=( - "Internal use of FencedObject is already removed from pass manager. " - "Implementation of a task subclass with protection for input object modification " - "is now responsibility of the developer." - ), - pending=True, - ) - def __init__(self, instance, attributes_to_fence): - self._wrapped = instance - self._attributes_to_fence = attributes_to_fence - - def __getattribute__(self, name): - object.__getattribute__(self, "_check_if_fenced")(name) - return getattr(object.__getattribute__(self, "_wrapped"), name) - - def __getitem__(self, key): - object.__getattribute__(self, "_check_if_fenced")("__getitem__") - return object.__getattribute__(self, "_wrapped")[key] - - def __setitem__(self, key, value): - object.__getattribute__(self, "_check_if_fenced")("__setitem__") - object.__getattribute__(self, "_wrapped")[key] = value - - def _check_if_fenced(self, name): - """ - Checks if the attribute name is in the list of attributes to protect. If so, raises - TranspilerError. - - Args: - name (string): the attribute name to check - - Raises: - TranspilerError: when name is the list of attributes to protect. - """ - if name in object.__getattribute__(self, "_attributes_to_fence"): - raise TranspilerError( - "The fenced %s has the property %s protected" - % (type(object.__getattribute__(self, "_wrapped")), name) - ) - - -class FencedPropertySet(FencedObject): - """A property set that cannot be written (via __setitem__)""" - - def __init__(self, property_set_instance): - super().__init__(property_set_instance, ["__setitem__"]) - - -class FencedDAGCircuit(FencedObject): - """A dag circuit that cannot be modified (via remove_op_node)""" - - # FIXME: add more fenced methods of the dag after dagcircuit rewrite - def __init__(self, dag_circuit_instance): - super().__init__(dag_circuit_instance, ["remove_op_node"]) diff --git a/qiskit/transpiler/passmanager.py b/qiskit/transpiler/passmanager.py index cf7c44d69a7d..7757282bc10d 100644 --- a/qiskit/transpiler/passmanager.py +++ b/qiskit/transpiler/passmanager.py @@ -16,7 +16,6 @@ import inspect import io import re -import warnings from collections.abc import Iterator, Iterable, Callable from functools import wraps from typing import Union, List, Any @@ -25,14 +24,12 @@ from qiskit.converters import circuit_to_dag, dag_to_circuit from qiskit.dagcircuit import DAGCircuit from qiskit.passmanager.passmanager import BasePassManager -from qiskit.passmanager.base_tasks import Task, BaseController -from qiskit.passmanager.flow_controllers import FlowController +from qiskit.passmanager.base_tasks import Task +from qiskit.passmanager.flow_controllers import FlowControllerLinear from qiskit.passmanager.exceptions import PassManagerError -from qiskit.utils.deprecation import deprecate_arg from .basepasses import BasePass from .exceptions import TranspilerError from .layout import TranspileLayout -from .runningpassmanager import RunningPassManager _CircuitsT = Union[List[QuantumCircuit], QuantumCircuit] @@ -52,9 +49,6 @@ def __init__( max_iteration: The maximum number of iterations the schedule will be looped if the condition is not met. """ - # For backward compatibility. - self._pass_sets = [] - super().__init__( tasks=passes, max_iteration=max_iteration, @@ -102,140 +96,32 @@ def _passmanager_backend( return out_program - @deprecate_arg( - name="max_iteration", - since="0.25", - additional_msg="'max_iteration' can be set in the constructor.", - pending=True, - package_name="qiskit-terra", - ) def append( self, passes: Task | list[Task], - max_iteration: int = None, - **flow_controller_conditions: Any, ) -> None: """Append a Pass Set to the schedule of passes. Args: - passes: A set of passes (a pass set) to be added to schedule. A pass set is a list of - passes that are controlled by the same flow controller. If a single pass is - provided, the pass set will only have that pass a single element. - It is also possible to append a :class:`.BaseFlowController` instance and - the rest of the parameter will be ignored. - max_iteration: max number of iterations of passes. - flow_controller_conditions: Dictionary of control flow plugins. - Following built-in controllers are available by default: - - * do_while: The passes repeat until the callable returns False. Corresponds to - :class:`.DoWhileController`. - * condition: The passes run only if the callable returns True. Corresponds to - :class:`.ConditionalController`. - - In general, you have more control simply by creating the controller you want and - passing it to :meth:`append`. + passes: A set of transpiler passes to be added to schedule. Raises: TranspilerError: if a pass in passes is not a proper pass. """ - if max_iteration: - self.max_iteration = max_iteration - - # Backward compatibility as of Terra 0.25 - if isinstance(passes, Task): - passes = [passes] - self._pass_sets.append( - { - "passes": passes, - "flow_controllers": flow_controller_conditions, - } - ) - if flow_controller_conditions: - passes = _legacy_build_flow_controller( - passes, - options={"max_iteration": self.max_iteration}, - **flow_controller_conditions, - ) - - super().append(passes) + super().append(tasks=passes) - @deprecate_arg( - name="max_iteration", - since="0.25", - additional_msg="'max_iteration' can be set in the constructor.", - pending=True, - package_name="qiskit-terra", - ) def replace( self, index: int, passes: Task | list[Task], - max_iteration: int = None, - **flow_controller_conditions: Any, ) -> None: """Replace a particular pass in the scheduler. Args: index: Pass index to replace, based on the position in passes(). passes: A pass set to be added to the pass manager schedule. - max_iteration: max number of iterations of passes. - flow_controller_conditions: Dictionary of control flow plugins. - See :meth:`qiskit.transpiler.PassManager.append` for details. """ - if max_iteration: - self.max_iteration = max_iteration - - # Backward compatibility as of Terra 0.25 - if isinstance(passes, Task): - passes = [passes] - try: - self._pass_sets[index] = { - "passes": passes, - "flow_controllers": flow_controller_conditions, - } - except IndexError as ex: - raise PassManagerError(f"Index to replace {index} does not exists") from ex - if flow_controller_conditions: - passes = _legacy_build_flow_controller( - passes, - options={"max_iteration": self.max_iteration}, - **flow_controller_conditions, - ) - - super().replace(index, passes) - - def remove(self, index: int) -> None: - super().remove(index) - - # Backward compatibility as of Terra 0.25 - del self._pass_sets[index] - - def __getitem__(self, index): - new_passmanager = super().__getitem__(index) - - # Backward compatibility as of Terra 0.25 - _pass_sets = self._pass_sets[index] - if isinstance(_pass_sets, dict): - _pass_sets = [_pass_sets] - new_passmanager._pass_sets = _pass_sets - return new_passmanager - - def __add__(self, other): - new_passmanager = super().__add__(other) - - # Backward compatibility as of Terra 0.25 - if isinstance(other, self.__class__): - new_passmanager._pass_sets = self._pass_sets - new_passmanager._pass_sets += other._pass_sets - - # When other is not identical type, _pass_sets is also evaluated by self.append. - return new_passmanager - - def to_flow_controller(self) -> RunningPassManager: - # For backward compatibility. - # This method will be resolved to the base class and return FlowControllerLinear - flatten_tasks = list(self._flatten_tasks(self._tasks)) - return RunningPassManager(flatten_tasks) + super().replace(index, tasks=passes) # pylint: disable=arguments-differ def run( @@ -320,22 +206,6 @@ def draw(self, filename=None, style=None, raw=False): return pass_manager_drawer(self, filename=filename, style=style, raw=raw) - def passes(self) -> list[dict[str, BasePass]]: - """Return a list structure of the appended passes and its options. - - Returns: - A list of pass sets, as defined in ``append()``. - """ - ret = [] - for pass_set in self._pass_sets: - item = {"passes": pass_set["passes"]} - if pass_set["flow_controllers"]: - item["flow_controllers"] = set(pass_set["flow_controllers"].keys()) - else: - item["flow_controllers"] = {} - ret.append(item) - return ret - class StagedPassManager(PassManager): """A pass manager pipeline built from individual stages. @@ -462,12 +332,10 @@ def _generate_expanded_stages(self) -> Iterator[str]: def _update_passmanager(self) -> None: self._tasks = [] - self._pass_sets = [] for stage in self.expanded_stages: pm = getattr(self, stage, None) if pm is not None: self._tasks += pm._tasks - self._pass_sets.extend(pm._pass_sets) def __setattr__(self, attr, value): if value == self and attr in self.expanded_stages: @@ -479,8 +347,6 @@ def __setattr__(self, attr, value): def append( self, passes: Task | list[Task], - max_iteration: int = None, - **flow_controller_conditions: Any, ) -> None: raise NotImplementedError @@ -488,8 +354,6 @@ def replace( self, index: int, passes: BasePass | list[BasePass], - max_iteration: int = None, - **flow_controller_conditions: Any, ) -> None: raise NotImplementedError @@ -504,10 +368,6 @@ def __getitem__(self, index): # It returns instance of self.__class__ which is StagedPassManager. new_passmanager = PassManager(max_iteration=self.max_iteration) new_passmanager._tasks = self._tasks[index] - _pass_sets = self._pass_sets[index] - if isinstance(_pass_sets, dict): - _pass_sets = [_pass_sets] - new_passmanager._pass_sets = _pass_sets return new_passmanager def __len__(self): @@ -520,10 +380,6 @@ def __setitem__(self, index, item): def __add__(self, other): raise NotImplementedError - def passes(self) -> list[dict[str, BasePass]]: - self._update_passmanager() - return super().passes() - def run( self, circuits: _CircuitsT, @@ -533,6 +389,10 @@ def run( self._update_passmanager() return super().run(circuits, output_name, callback) + def to_flow_controller(self) -> FlowControllerLinear: + self._update_passmanager() + return super().to_flow_controller() + def draw(self, filename=None, style=None, raw=False): """Draw the staged pass manager.""" from qiskit.visualization import staged_pass_manager_drawer @@ -577,41 +437,3 @@ def _wrapped_callable(task, passmanager_ir, property_set, running_time, count): ) return _wrapped_callable - - -def _legacy_build_flow_controller( - tasks: list[Task], - options: dict[str, Any], - **flow_controller_conditions, -) -> BaseController: - """A legacy method to build flow controller with keyword arguments. - - Args: - tasks: A list of tasks fed into custom flow controllers. - options: Option for flow controllers. - flow_controller_conditions: Callables keyed on the alias of the flow controller. - - Returns: - A built controller. - """ - warnings.warn( - "Building a flow controller with keyword arguments is going to be deprecated. " - "Custom controllers must be explicitly instantiated and appended to the task list.", - PendingDeprecationWarning, - stacklevel=3, - ) - if isinstance(tasks, Task): - tasks = [tasks] - if any(not isinstance(t, Task) for t in tasks): - raise TypeError("Added tasks are not all valid pass manager task types.") - # Alias in higher hierarchy becomes outer controller. - for alias in FlowController.hierarchy[::-1]: - if alias not in flow_controller_conditions: - continue - class_type = FlowController.registered_controllers[alias] - init_kwargs = { - "options": options, - alias: flow_controller_conditions.pop(alias), - } - tasks = class_type(tasks, **init_kwargs) - return tasks diff --git a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py index b21c7ac130f5..d24fdca64885 100644 --- a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py +++ b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py @@ -43,7 +43,7 @@ from qiskit.transpiler.passes import Depth, Size, FixedPoint, MinimumPoint from qiskit.transpiler.passes.utils.gates_basis import GatesInBasis from qiskit.transpiler.passes.synthesis.unitary_synthesis import UnitarySynthesis -from qiskit.passmanager.flow_controllers import ConditionalController +from qiskit.passmanager.flow_controllers import ConditionalController, DoWhileController from qiskit.transpiler.timing_constraints import TimingConstraints from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason from qiskit.circuit.library.standard_gates import ( @@ -598,7 +598,7 @@ def _opt_control(property_set): else: raise TranspilerError(f"Invalid optimization_level: {optimization_level}") - unroll = [pass_ for x in translation.passes() for pass_ in x["passes"]] + unroll = translation.to_flow_controller() # Build nested Flow controllers def _unroll_condition(property_set): return not property_set["all_gates_in_basis"] @@ -618,7 +618,7 @@ def _unroll_condition(property_set): if optimization_level == 3 else _opt + _unroll_if_out_of_basis + _depth_check + _size_check ) - optimization.append(opt_loop, do_while=_opt_control) + optimization.append(DoWhileController(opt_loop, do_while=_opt_control)) return optimization else: return None @@ -712,13 +712,19 @@ def _swap_mapped(property_set): layout = PassManager() layout.append(_given_layout) if optimization_level == 0: - layout.append(TrivialLayout(coupling_map), condition=_choose_layout_condition) + layout.append( + ConditionalController( + TrivialLayout(coupling_map), condition=_choose_layout_condition + ) + ) layout += common.generate_embed_passmanager(coupling_map) return layout elif optimization_level == 1: layout.append( - [TrivialLayout(coupling_map), CheckMap(coupling_map)], - condition=_choose_layout_condition, + ConditionalController( + [TrivialLayout(coupling_map), CheckMap(coupling_map)], + condition=_choose_layout_condition, + ) ) choose_layout_1 = VF2Layout( coupling_map=pass_manager_config.coupling_map, @@ -728,7 +734,7 @@ def _swap_mapped(property_set): target=pass_manager_config.target, max_trials=2500, # Limits layout scoring to < 600ms on ~400 qubit devices ) - layout.append(choose_layout_1, condition=_layout_not_perfect) + layout.append(ConditionalController(choose_layout_1, condition=_layout_not_perfect)) choose_layout_2 = SabreLayout( coupling_map, max_iterations=2, @@ -739,13 +745,15 @@ def _swap_mapped(property_set): and pass_manager_config.routing_method != "sabre", ) layout.append( - [ - BarrierBeforeFinalMeasurements( - "qiskit.transpiler.internal.routing.protection.barrier" - ), - choose_layout_2, - ], - condition=_vf2_match_not_found, + ConditionalController( + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + choose_layout_2, + ], + condition=_vf2_match_not_found, + ) ) elif optimization_level == 2: choose_layout_0 = VF2Layout( @@ -756,7 +764,9 @@ def _swap_mapped(property_set): target=pass_manager_config.target, max_trials=25000, # Limits layout scoring to < 10s on ~400 qubit devices ) - layout.append(choose_layout_0, condition=_choose_layout_condition) + layout.append( + ConditionalController(choose_layout_0, condition=_choose_layout_condition) + ) choose_layout_1 = SabreLayout( coupling_map, max_iterations=2, @@ -767,13 +777,15 @@ def _swap_mapped(property_set): and pass_manager_config.routing_method != "sabre", ) layout.append( - [ - BarrierBeforeFinalMeasurements( - "qiskit.transpiler.internal.routing.protection.barrier" - ), - choose_layout_1, - ], - condition=_vf2_match_not_found, + ConditionalController( + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + choose_layout_1, + ], + condition=_vf2_match_not_found, + ) ) elif optimization_level == 3: choose_layout_0 = VF2Layout( @@ -784,7 +796,9 @@ def _swap_mapped(property_set): target=pass_manager_config.target, max_trials=250000, # Limits layout scoring to < 60s on ~400 qubit devices ) - layout.append(choose_layout_0, condition=_choose_layout_condition) + layout.append( + ConditionalController(choose_layout_0, condition=_choose_layout_condition) + ) choose_layout_1 = SabreLayout( coupling_map, max_iterations=4, @@ -795,21 +809,21 @@ def _swap_mapped(property_set): and pass_manager_config.routing_method != "sabre", ) layout.append( - [ - BarrierBeforeFinalMeasurements( - "qiskit.transpiler.internal.routing.protection.barrier" - ), - choose_layout_1, - ], - condition=_vf2_match_not_found, + ConditionalController( + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + choose_layout_1, + ], + condition=_vf2_match_not_found, + ) ) else: raise TranspilerError(f"Invalid optimization level: {optimization_level}") embed = common.generate_embed_passmanager(coupling_map) - layout.append( - [pass_ for x in embed.passes() for pass_ in x["passes"]], condition=_swap_mapped - ) + layout.append(ConditionalController(embed.to_flow_controller(), condition=_swap_mapped)) return layout @@ -829,7 +843,9 @@ def _choose_layout_condition(property_set): layout = PassManager() layout.append(_given_layout) - layout.append(TrivialLayout(coupling_map), condition=_choose_layout_condition) + layout.append( + ConditionalController(TrivialLayout(coupling_map), condition=_choose_layout_condition) + ) layout += common.generate_embed_passmanager(coupling_map) return layout @@ -851,12 +867,14 @@ def _choose_layout_condition(property_set): layout = PassManager() layout.append(_given_layout) layout.append( - DenseLayout( - coupling_map=pass_manager_config.coupling_map, - backend_prop=pass_manager_config.backend_properties, - target=pass_manager_config.target, - ), - condition=_choose_layout_condition, + ConditionalController( + DenseLayout( + coupling_map=pass_manager_config.coupling_map, + backend_prop=pass_manager_config.backend_properties, + target=pass_manager_config.target, + ), + condition=_choose_layout_condition, + ) ) layout += common.generate_embed_passmanager(coupling_map) return layout @@ -924,16 +942,16 @@ def _swap_mapped(property_set): else: raise TranspilerError(f"Invalid optimization level: {optimization_level}") layout.append( - [ - BarrierBeforeFinalMeasurements( - "qiskit.transpiler.internal.routing.protection.barrier" - ), - layout_pass, - ], - condition=_choose_layout_condition, + ConditionalController( + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + layout_pass, + ], + condition=_choose_layout_condition, + ) ) embed = common.generate_embed_passmanager(coupling_map) - layout.append( - [pass_ for x in embed.passes() for pass_ in x["passes"]], condition=_swap_mapped - ) + layout.append(ConditionalController(embed.to_flow_controller(), condition=_swap_mapped)) return layout diff --git a/qiskit/transpiler/preset_passmanagers/common.py b/qiskit/transpiler/preset_passmanagers/common.py index 9219854a16a3..c00824a70374 100644 --- a/qiskit/transpiler/preset_passmanagers/common.py +++ b/qiskit/transpiler/preset_passmanagers/common.py @@ -20,6 +20,7 @@ from qiskit.circuit.controlflow import CONTROL_FLOW_OP_NAMES from qiskit.utils.deprecation import deprecate_func +from qiskit.passmanager.flow_controllers import ConditionalController from qiskit.transpiler.passmanager import PassManager from qiskit.transpiler.passes import Error from qiskit.transpiler.passes import Unroller @@ -147,9 +148,11 @@ def generate_control_flow_options_check( out = PassManager() out.append(ContainsInstruction(CONTROL_FLOW_OP_NAMES, recurse=False)) if bad_options: - out.append(Error(message), condition=_has_control_flow) + out.append(ConditionalController(Error(message), condition=_has_control_flow)) backend_control = _InvalidControlFlowForBackend(basis_gates, target) - out.append(Error(backend_control.message), condition=backend_control.condition) + out.append( + ConditionalController(Error(backend_control.message), condition=backend_control.condition) + ) return out @@ -158,7 +161,7 @@ def generate_error_on_control_flow(message): circuit.""" out = PassManager() out.append(ContainsInstruction(CONTROL_FLOW_OP_NAMES, recurse=False)) - out.append(Error(message), condition=_has_control_flow) + out.append(ConditionalController(Error(message), condition=_has_control_flow)) return out @@ -171,8 +174,8 @@ def if_has_control_flow_else(if_present, if_absent): if_absent = if_absent.to_flow_controller() out = PassManager() out.append(ContainsInstruction(CONTROL_FLOW_OP_NAMES, recurse=False)) - out.append(if_present, condition=_has_control_flow) - out.append(if_absent, condition=_without_control_flow) + out.append(ConditionalController(if_present, condition=_has_control_flow)) + out.append(ConditionalController(if_absent, condition=_without_control_flow)) return out @@ -328,32 +331,36 @@ def _swap_condition(property_set): if use_barrier_before_measurement: routing.append( - [ - BarrierBeforeFinalMeasurements( - label="qiskit.transpiler.internal.routing.protection.barrier" - ), - routing_pass, - ], - condition=_swap_condition, + ConditionalController( + [ + BarrierBeforeFinalMeasurements( + label="qiskit.transpiler.internal.routing.protection.barrier" + ), + routing_pass, + ], + condition=_swap_condition, + ) ) else: - routing.append([routing_pass], condition=_swap_condition) + routing.append(ConditionalController(routing_pass, condition=_swap_condition)) is_vf2_fully_bounded = vf2_call_limit and vf2_max_trials if (target is not None or backend_properties is not None) and is_vf2_fully_bounded: routing.append( - VF2PostLayout( - target, - coupling_map, - backend_properties, - seed_transpiler, - call_limit=vf2_call_limit, - max_trials=vf2_max_trials, - strict_direction=False, - ), - condition=_run_post_layout_condition, + ConditionalController( + VF2PostLayout( + target, + coupling_map, + backend_properties, + seed_transpiler, + call_limit=vf2_call_limit, + max_trials=vf2_max_trials, + strict_direction=False, + ), + condition=_run_post_layout_condition, + ) ) - routing.append(ApplyLayout(), condition=_apply_post_layout_condition) + routing.append(ConditionalController(ApplyLayout(), condition=_apply_post_layout_condition)) def filter_fn(node): return ( @@ -388,7 +395,12 @@ def generate_pre_op_passmanager(target=None, coupling_map=None, remove_reset_in_ def _direction_condition(property_set): return not property_set["is_direction_mapped"] - pre_opt.append([GateDirection(coupling_map, target=target)], condition=_direction_condition) + pre_opt.append( + ConditionalController( + [GateDirection(coupling_map, target=target)], + condition=_direction_condition, + ) + ) if remove_reset_in_zero: pre_opt.append(RemoveResetInZeroState()) return pre_opt @@ -554,7 +566,9 @@ def _contains_delay(property_set): scheduling.append(ContainsInstruction("delay")) scheduling.append( - TimeUnitConversion(instruction_durations, target=target), condition=_contains_delay + ConditionalController( + TimeUnitConversion(instruction_durations, target=target), condition=_contains_delay + ) ) if ( timing_constraints.granularity != 1 @@ -574,11 +588,13 @@ def _require_alignment(property_set): ) ) scheduling.append( - ConstrainedReschedule( - acquire_alignment=timing_constraints.acquire_alignment, - pulse_alignment=timing_constraints.pulse_alignment, - ), - condition=_require_alignment, + ConditionalController( + ConstrainedReschedule( + acquire_alignment=timing_constraints.acquire_alignment, + pulse_alignment=timing_constraints.pulse_alignment, + ), + condition=_require_alignment, + ) ) scheduling.append( ValidatePulseGates( diff --git a/qiskit/transpiler/propertyset.py b/qiskit/transpiler/propertyset.py deleted file mode 100644 index 6ff160b14a43..000000000000 --- a/qiskit/transpiler/propertyset.py +++ /dev/null @@ -1,19 +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. - -"""A property set is maintained by the PassManager to keep information -about the current state of the circuit """ - - -# For backward compatibility -# pylint: disable=unused-import -from qiskit.passmanager import PropertySet diff --git a/qiskit/transpiler/runningpassmanager.py b/qiskit/transpiler/runningpassmanager.py deleted file mode 100644 index 548b49833265..000000000000 --- a/qiskit/transpiler/runningpassmanager.py +++ /dev/null @@ -1,174 +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. - -"""RunningPassManager class for the transpiler. -This object holds the state of a pass manager during running-time.""" -from __future__ import annotations - -import inspect -import logging -from functools import wraps -from typing import Callable - -from qiskit.circuit import QuantumCircuit -from qiskit.converters import circuit_to_dag, dag_to_circuit -from qiskit.passmanager.compilation_status import PropertySet, WorkflowStatus, PassManagerState -from qiskit.passmanager.base_tasks import Task -from qiskit.passmanager.exceptions import PassManagerError -from qiskit.utils.deprecation import deprecate_func - -# pylint: disable=unused-import -from qiskit.passmanager.flow_controllers import ( - BaseController, - FlowController, - FlowControllerLinear, - # for backward compatibility - ConditionalController, - DoWhileController, -) - -from .exceptions import TranspilerError -from .layout import TranspileLayout - -logger = logging.getLogger(__name__) - - -class RunningPassManager(FlowControllerLinear): - """A RunningPassManager is a running pass manager. - - .. warning:: - - :class:`.RunningPassManager` will be deprecated in the future release. - As of Qiskit Terra 0.25 this class becomes a subclass of the flow controller - with extra methods for backward compatibility. - Relying on a subclass of the running pass manager might break your code stack. - """ - - @deprecate_func( - since="0.45.0", - additional_msg=( - "Building the pipline of the tasks is responsibility of PassManager. " - "RunningPassManager should not modify prepared pipeline at running time." - ), - ) - def append( - self, - passes: Task | list[Task], - **flow_controller_conditions, - ): - """Append a passes to the schedule of passes. - - Args: - passes: A set of passes (a pass set) to be added to schedule. A pass set is a list of - passes that are controlled by the same flow controller. If a single pass is - provided, the pass set will only have that pass a single element. - It is also possible to append a :class:`.BaseFlowController` instance and - the rest of the parameter will be ignored. - flow_controller_conditions: Dictionary of control flow plugins. - Following built-in controllers are available by default: - - * do_while: The passes repeat until the callable returns False. - * condition: The passes run only if the callable returns True. - """ - if not isinstance(passes, BaseController): - normalized_controller = passes - else: - # Backward compatibility. Will be deprecated. - normalized_controller = FlowController.controller_factory( - passes=passes, - options=self._options, - **flow_controller_conditions, - ) - super().append(normalized_controller) - - # pylint: disable=arguments-differ - @deprecate_func( - since="0.45.0", - additional_msg="Now RunningPassManager is a subclass of flow controller.", - pending=True, - ) - def run( - self, - circuit: QuantumCircuit, - output_name: str = None, - callback: Callable = None, - ) -> QuantumCircuit: - """Run all the passes on a QuantumCircuit - - Args: - circuit: Circuit to transform via all the registered passes. - output_name: The output circuit name. If not given, the same as the input circuit. - callback: A callback function that will be called after each pass execution. - - Returns: - QuantumCircuit: Transformed circuit. - """ - initial_status = WorkflowStatus() - property_set = PropertySet() - state = PassManagerState(workflow_status=initial_status, property_set=property_set) - - passmanager_ir = circuit_to_dag(circuit) - passmanager_ir, state = super().execute( - passmanager_ir=passmanager_ir, - state=state, - callback=callback, - ) - - out_circuit = dag_to_circuit(passmanager_ir, copy_operations=False) - out_circuit.name = output_name - - if state.property_set["layout"] is not None: - circuit._layout = TranspileLayout( - initial_layout=state.property_set["layout"], - input_qubit_mapping=state.property_set["original_qubit_indices"], - final_layout=state.property_set["final_layout"], - ) - circuit._clbit_write_latency = state.property_set["clbit_write_latency"] - circuit._conditional_latency = state.property_set["conditional_latency"] - - if state.property_set["node_start_time"]: - # This is dictionary keyed on the DAGOpNode, which is invalidated once - # dag is converted into circuit. So this schedule information is - # also converted into list with the same ordering with circuit.data. - topological_start_times = [] - start_times = state.property_set["node_start_time"] - for dag_node in passmanager_ir.topological_op_nodes(): - topological_start_times.append(start_times[dag_node]) - circuit._op_start_times = topological_start_times - - return circuit - - -# A temporary error handling with slight overhead at class loading. -# This method wraps all class methods to replace PassManagerError with TranspilerError. -# The pass flow controller mechanics raises PassManagerError, as it has been moved to base class. -# PassManagerError is not caught by TranspilerError due to the hierarchy. - - -def _replace_error(meth): - @wraps(meth) - def wrapper(*meth_args, **meth_kwargs): - try: - return meth(*meth_args, **meth_kwargs) - except PassManagerError as ex: - raise TranspilerError(ex.message) from ex - - return wrapper - - -for _name, _method in inspect.getmembers(RunningPassManager, predicate=inspect.isfunction): - if _name.startswith("_"): - # Ignore protected and private. - # User usually doesn't directly execute and catch error from these methods. - continue - _wrapped = _replace_error(_method) - setattr(RunningPassManager, _name, _wrapped) diff --git a/qiskit/visualization/pass_manager_visualization.py b/qiskit/visualization/pass_manager_visualization.py index 6b166ac85ffc..6c91ce9c0099 100644 --- a/qiskit/visualization/pass_manager_visualization.py +++ b/qiskit/visualization/pass_manager_visualization.py @@ -14,11 +14,15 @@ Visualization function for a pass manager. Passes are grouped based on their flow controller, and coloured based on the type of pass. """ +from __future__ import annotations + import os import inspect import tempfile from qiskit.utils import optionals as _optionals +from qiskit.passmanager.base_tasks import BaseController, GenericPass +from qiskit.passmanager.flow_controllers import FlowControllerLinear from qiskit.transpiler.basepasses import AnalysisPass, TransformationPass from .exceptions import VisualizationError @@ -74,8 +78,6 @@ def pass_manager_drawer(pass_manager, filename=None, style=None, raw=False): """ import pydot - passes = pass_manager.passes() - if not style: style = DEFAULT_STYLE @@ -89,7 +91,7 @@ def pass_manager_drawer(pass_manager, filename=None, style=None, raw=False): prev_node = None - for index, controller_group in enumerate(passes): + for index, controller_group in enumerate(pass_manager.to_flow_controller().tasks): subgraph, component_id, prev_node = draw_subgraph( controller_group, component_id, style, prev_node, index ) @@ -174,9 +176,8 @@ def staged_pass_manager_drawer(pass_manager, filename=None, style=None, raw=Fals stage = getattr(pass_manager, st) if stage is not None: - passes = stage.passes() stagegraph = pydot.Cluster(str(st), label=str(st), fontname="helvetica", labeljust="l") - for controller_group in passes: + for controller_group in stage.to_flow_controller().tasks: subgraph, component_id, prev_node = draw_subgraph( controller_group, component_id, style, prev_node, idx ) @@ -193,28 +194,65 @@ def draw_subgraph(controller_group, component_id, style, prev_node, idx): import pydot # label is the name of the flow controller parameter - label = "[{}] {}".format(idx, ", ".join(controller_group["flow_controllers"])) + label = f"[{idx}] " + if isinstance(controller_group, BaseController) and not isinstance( + controller_group, FlowControllerLinear + ): + label += f"{controller_group.__class__.__name__}" # create the subgraph for this controller subgraph = pydot.Cluster(str(component_id), label=label, fontname="helvetica", labeljust="l") component_id += 1 - for pass_ in controller_group["passes"]: - - # label is the name of the pass - node = pydot.Node( - str(component_id), - label=str(type(pass_).__name__), - color=_get_node_color(pass_, style), - shape="rectangle", - fontname="helvetica", - ) + if isinstance(controller_group, BaseController): + # Assume linear pipeline + # TODO: support pipeline branching when such controller is introduced + tasks = getattr(controller_group, "tasks", []) + elif isinstance(controller_group, GenericPass): + tasks = [controller_group] + elif isinstance(controller_group, (list, tuple)): + tasks = controller_group + else: + # Invalid data + return subgraph, component_id, prev_node + + flatten_tasks = [] + for task in tasks: + # Flatten nested linear flow controller. + # This situation often occurs in the builtin pass managers because it constructs + # some stages by appending other pass manager instance converted into a linear controller. + # Flattening inner linear controller tasks doesn't change the execution. + if isinstance(task, FlowControllerLinear): + flatten_tasks.extend(task.tasks) + else: + flatten_tasks.append(task) + + for task in flatten_tasks: + if isinstance(task, BaseController): + # Partly nested flow controller + # TODO recursively inject subgraph into subgraph + node = pydot.Node( + str(component_id), + label="Nested flow controller", + color="k", + shape="rectangle", + fontname="helvetica", + ) + else: + # label is the name of the pass + node = pydot.Node( + str(component_id), + label=str(type(task).__name__), + color=_get_node_color(task, style), + shape="rectangle", + fontname="helvetica", + ) subgraph.add_node(node) component_id += 1 # the arguments that were provided to the pass when it was created - arg_spec = inspect.getfullargspec(pass_.__init__) + arg_spec = inspect.getfullargspec(task.__init__) # 0 is the args, 1: to remove the self arg args = arg_spec[0][1:] diff --git a/releasenotes/notes/upgrade-pass-manager-98aa64edde67b5bb.yaml b/releasenotes/notes/upgrade-pass-manager-98aa64edde67b5bb.yaml new file mode 100644 index 000000000000..be8b0ce26206 --- /dev/null +++ b/releasenotes/notes/upgrade-pass-manager-98aa64edde67b5bb.yaml @@ -0,0 +1,79 @@ +--- +upgrade: + - | + A pattern for the pass piepline construction was upgraded. + The syntactic suger shown below for instantiation of flow controller was removed. + + .. code-block:: python + + from qiskit.transpiler import PassManager + + pm = PassManager() + pm.append(my_pass, condition=condition_callable, do_while=do_while_callable) + + Instead of using this keyword argument pattern, you should explicitly instantiate the + flow controller. + + .. code-block:: python + + from qiskit.passmanager.flow_controllers import ConditionalController, DoWhileController + + pm = PassManager() + pm.append( + ConditionalController( + DoWhileController(my_pass, do_while=do_while_callable), + condition=condition_callable, + ) + ) + + Note that you can manage the pecking order of controllers when you want to nest them, + which was not possible with keyword arguments. + You can also build the pipeline with the constructor of the pass manager like below + because there is no reason to call the append method now. + + .. code-block:: python + + pm = PassManager( + ConditionalController( + DoWhileController(my_pass, do_while=do_while_callable), + condition=condition_callable, + ) + ) + + - | + The append method of builtin flow controllers was removed. This includes + + * :meth:`.ConditionalController.append` + * :meth:`.DoWhileController.append` + * :meth:`.FlowControllerLinear.append` + + The task pipeline in a flow controller is frozen, and it must be passed + when the controller instance is created. + + - | + Removed methods :meth:`qiskit.transpiler.PassManager.passes` and + :meth:`qiskit.transpiler.StagedPassManager.passes` that generates + a representation of inset passes in the form of list of dictionary, + however, this format doesn't efficiently represent more complicated pass pipeline, + which may include conditional branching and nested conditions. + Instead of using this representation, please use following pattern + + .. code-block:: python + + pm = PassManager(...) + pm.to_flow_controller().tasks + + This directly returns a linearized base task instances in tuple format. + + - | + The max_iteration argument was removed from :meth:`qiskit.transpiler.PassManager.append` + and :meth:`qiskit.transpiler.PassManager.replace`. + + - | + The following legacy classes were removed from the pass manager and transpiler module. + + * :class:`qiskit.passmanager.flow_controllers.FlowController` + * :class:`qiskit.transpiler.fencedobjs.FencedObject` + * :class:`qiskit.transpiler.fencedobjs.FencedPropertySet` + * :class:`qiskit.transpiler.fencedobjs.FencedDAGCircuit` + * :class:`qiskit.transpiler.runningpassmanager.RunningPassManager` diff --git a/test/python/transpiler/test_commutative_cancellation.py b/test/python/transpiler/test_commutative_cancellation.py index 577ae886afc7..31931a7fdb4c 100644 --- a/test/python/transpiler/test_commutative_cancellation.py +++ b/test/python/transpiler/test_commutative_cancellation.py @@ -19,6 +19,7 @@ from qiskit import QuantumRegister, QuantumCircuit from qiskit.circuit.library import U1Gate, RZGate, PhaseGate, CXGate, SXGate from qiskit.circuit.parameter import Parameter +from qiskit.passmanager.flow_controllers import DoWhileController from qiskit.transpiler.target import Target from qiskit.transpiler import PassManager, PropertySet from qiskit.transpiler.passes import CommutationAnalysis, CommutativeCancellation, FixedPoint, Size @@ -136,8 +137,15 @@ def test_consecutive_cnots2(self): passmanager = PassManager() passmanager.append( - [CommutationAnalysis(), CommutativeCancellation(), Size(), FixedPoint("size")], - do_while=lambda property_set: not property_set["size_fixed_point"], + DoWhileController( + [ + CommutationAnalysis(), + CommutativeCancellation(), + Size(), + FixedPoint("size"), + ], + do_while=lambda property_set: not property_set["size_fixed_point"], + ) ) new_circuit = passmanager.run(circuit) expected = QuantumCircuit(qr) @@ -410,8 +418,15 @@ def test_commutative_circuit3(self): passmanager = PassManager() passmanager.append( - [CommutationAnalysis(), CommutativeCancellation(), Size(), FixedPoint("size")], - do_while=lambda property_set: not property_set["size_fixed_point"], + DoWhileController( + [ + CommutationAnalysis(), + CommutativeCancellation(), + Size(), + FixedPoint("size"), + ], + do_while=lambda property_set: not property_set["size_fixed_point"], + ) ) new_circuit = passmanager.run(circuit) expected = QuantumCircuit(qr) @@ -453,8 +468,15 @@ def test_cnot_cascade(self): passmanager = PassManager() # passmanager.append(CommutativeCancellation()) passmanager.append( - [CommutationAnalysis(), CommutativeCancellation(), Size(), FixedPoint("size")], - do_while=lambda property_set: not property_set["size_fixed_point"], + DoWhileController( + [ + CommutationAnalysis(), + CommutativeCancellation(), + Size(), + FixedPoint("size"), + ], + do_while=lambda property_set: not property_set["size_fixed_point"], + ) ) new_circuit = passmanager.run(circuit) expected = QuantumCircuit(qr) @@ -509,8 +531,15 @@ def test_cnot_cascade1(self): passmanager = PassManager() # passmanager.append(CommutativeCancellation()) passmanager.append( - [CommutationAnalysis(), CommutativeCancellation(), Size(), FixedPoint("size")], - do_while=lambda property_set: not property_set["size_fixed_point"], + DoWhileController( + [ + CommutationAnalysis(), + CommutativeCancellation(), + Size(), + FixedPoint("size"), + ], + do_while=lambda property_set: not property_set["size_fixed_point"], + ) ) new_circuit = passmanager.run(circuit) expected = QuantumCircuit(qr) diff --git a/test/python/transpiler/test_gates_in_basis_pass.py b/test/python/transpiler/test_gates_in_basis_pass.py index e181423a00ae..4092b312b337 100644 --- a/test/python/transpiler/test_gates_in_basis_pass.py +++ b/test/python/transpiler/test_gates_in_basis_pass.py @@ -16,6 +16,7 @@ from qiskit.circuit.library import HGate, CXGate, UGate, XGate, ZGate from qiskit.circuit.measure import Measure from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary +from qiskit.passmanager.flow_controllers import ConditionalController from qiskit.transpiler import PassManager from qiskit.transpiler.passes import BasisTranslator from qiskit.transpiler.passes import GatesInBasis @@ -86,8 +87,10 @@ def test_all_gates_in_basis_after_translation(self): pm = PassManager() pm.append(analysis_pass) pm.append( - BasisTranslator(SessionEquivalenceLibrary, basis_gates), - condition=lambda property_set: not property_set["all_gates_in_basis"], + ConditionalController( + BasisTranslator(SessionEquivalenceLibrary, basis_gates), + condition=lambda property_set: not property_set["all_gates_in_basis"], + ) ) pm.append(analysis_pass) pm.run(circuit) @@ -200,8 +203,10 @@ def test_all_gates_in_basis_after_translation_with_target(self): pm = PassManager() pm.append(analysis_pass) pm.append( - BasisTranslator(SessionEquivalenceLibrary, basis_gates, target=target), - condition=lambda property_set: not property_set["all_gates_in_basis"], + ConditionalController( + BasisTranslator(SessionEquivalenceLibrary, basis_gates, target=target), + condition=lambda property_set: not property_set["all_gates_in_basis"], + ) ) pm.append(analysis_pass) pm.run(circuit) diff --git a/test/python/transpiler/test_optimize_swap_before_measure.py b/test/python/transpiler/test_optimize_swap_before_measure.py index 75b0079c6650..258796e56929 100644 --- a/test/python/transpiler/test_optimize_swap_before_measure.py +++ b/test/python/transpiler/test_optimize_swap_before_measure.py @@ -15,6 +15,7 @@ import unittest from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister +from qiskit.passmanager.flow_controllers import DoWhileController from qiskit.transpiler import PassManager from qiskit.transpiler.passes import OptimizeSwapBeforeMeasure, DAGFixedPoint from qiskit.converters import circuit_to_dag @@ -311,8 +312,10 @@ def test_optimize_undone_swap(self): pass_manager = PassManager() pass_manager.append( - [OptimizeSwapBeforeMeasure(), DAGFixedPoint()], - do_while=lambda property_set: not property_set["dag_fixed_point"], + DoWhileController( + [OptimizeSwapBeforeMeasure(), DAGFixedPoint()], + do_while=lambda property_set: not property_set["dag_fixed_point"], + ) ) after = pass_manager.run(circuit) @@ -340,8 +343,10 @@ def test_optimize_overlap_swap(self): pass_manager = PassManager() pass_manager.append( - [OptimizeSwapBeforeMeasure(), DAGFixedPoint()], - do_while=lambda property_set: not property_set["dag_fixed_point"], + DoWhileController( + [OptimizeSwapBeforeMeasure(), DAGFixedPoint()], + do_while=lambda property_set: not property_set["dag_fixed_point"], + ) ) after = pass_manager.run(circuit) diff --git a/test/python/transpiler/test_pass_scheduler.py b/test/python/transpiler/test_pass_scheduler.py index a52ad8b1ece2..263db4f64914 100644 --- a/test/python/transpiler/test_pass_scheduler.py +++ b/test/python/transpiler/test_pass_scheduler.py @@ -19,11 +19,7 @@ from qiskit import QuantumRegister, QuantumCircuit from qiskit.transpiler import PassManager, TranspilerError -from qiskit.transpiler.runningpassmanager import ( - DoWhileController, - ConditionalController, - FlowController, -) +from qiskit.passmanager.flow_controllers import DoWhileController, ConditionalController from qiskit.test import QiskitTestCase from ._dummy_passes import ( PassA_TP_NR_NP, @@ -136,7 +132,9 @@ def test_conditional_passes_true(self): """A pass set with a conditional parameter. The callable is True.""" self.passmanager.append(PassE_AP_NR_NP(True)) self.passmanager.append( - PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ConditionalController( + PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ) ) self.assertScheduler( self.circuit, @@ -171,7 +169,9 @@ def test_conditional_passes_false(self): """A pass set with a conditional parameter. The callable is False.""" self.passmanager.append(PassE_AP_NR_NP(False)) self.passmanager.append( - PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ConditionalController( + PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ) ) self.assertScheduler( self.circuit, @@ -183,9 +183,17 @@ def test_conditional_and_loop(self): """Run a conditional first, then a loop.""" self.passmanager.append(PassE_AP_NR_NP(True)) self.passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], - condition=lambda property_set: property_set["property"], + ConditionalController( + DoWhileController( + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ), + condition=lambda property_set: property_set["property"], + ) ) self.assertScheduler( self.circuit, @@ -240,15 +248,19 @@ def test_conditional_and_loop(self): def test_loop_and_conditional(self): """Run a loop first, then a conditional.""" - with self.assertWarns(DeprecationWarning): - FlowController.remove_flow_controller("condition") - FlowController.add_flow_controller("condition", ConditionalController) - self.passmanager.append(PassK_check_fixed_point_property()) self.passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], - condition=lambda property_set: not property_set["property_fixed_point"], + ConditionalController( + DoWhileController( + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ), + condition=lambda property_set: not property_set["property_fixed_point"], + ) ) self.assertScheduler( self.circuit, @@ -380,8 +392,14 @@ def test_pass_no_return(self): def test_fixed_point_pass(self): """A pass set with a do_while parameter that checks for a fixed point.""" self.passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], + DoWhileController( + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ) ) self.assertScheduler( self.circuit, @@ -436,7 +454,11 @@ def test_fixed_point_fc(self): """A fixed point scheduler with flow control.""" self.passmanager.append( DoWhileController( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], do_while=lambda property_set: not property_set["property_fixed_point"], ) ) @@ -492,9 +514,11 @@ def test_fixed_point_pass_max_iteration(self): """A pass set with a do_while parameter that checks that the max_iteration is raised.""" self.passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], - max_iteration=2, + DoWhileController( + [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], + do_while=lambda property_set: not property_set["property_fixed_point"], + options={"max_iteration": 2}, + ), ) self.assertSchedulerRaises( self.circuit, @@ -535,16 +559,18 @@ def test_fresh_initial_state(self): def test_nested_conditional_in_loop(self): """Run a loop with a nested conditional.""" - nested_conditional = [ - ConditionalController( - [PassA_TP_NR_NP()], condition=lambda property_set: property_set["property"] >= 5 - ) - ] + nested_conditional = ConditionalController( + PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] >= 5 + ) self.passmanager.append( - [PassK_check_fixed_point_property()] - + nested_conditional - + [PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], + DoWhileController( + [ + PassK_check_fixed_point_property(), + nested_conditional, + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ) ) expected = [ "run analysis pass PassG_calculates_dag_property", @@ -589,21 +615,6 @@ def test_nested_conditional_in_loop(self): self.assertScheduler(self.circuit, self.passmanager, expected) -class DoXTimesController(FlowController): - """A control-flow plugin for running a set of passes an X amount of times.""" - - def __init__(self, passes, options, do_x_times, **_): - super().__init__(options) - self.passes = passes - self.do_x_times = do_x_times - - # pylint: disable=missing-function-docstring - def iter_tasks(self, metadata): - for _ in range(self.do_x_times(metadata.property_set)): - for pass_ in self.passes: - metadata = yield pass_ - - class TestControlFlowPlugin(SchedulerTestCase): """Testing the control flow plugin system.""" @@ -612,58 +623,6 @@ def setUp(self): self.passmanager = PassManager() self.circuit = QuantumCircuit(QuantumRegister(1)) - def test_control_flow_plugin(self): - """Adds a control flow plugin with a single parameter and runs it.""" - with self.assertWarns(DeprecationWarning): - FlowController.add_flow_controller("do_x_times", DoXTimesController) - self.passmanager.append([PassB_TP_RA_PA(), PassC_TP_RA_PA()], do_x_times=lambda x: 3) - self.assertScheduler( - self.circuit, - self.passmanager, - [ - "run transformation pass PassA_TP_NR_NP", - "run transformation pass PassB_TP_RA_PA", - "run transformation pass PassC_TP_RA_PA", - "run transformation pass PassB_TP_RA_PA", - "run transformation pass PassC_TP_RA_PA", - "run transformation pass PassB_TP_RA_PA", - "run transformation pass PassC_TP_RA_PA", - ], - ) - - def test_callable_control_flow_plugin(self): - """Removes do_while, then adds it back. Checks max_iteration still working.""" - controllers_length = len(FlowController.registered_controllers) - with self.assertWarns(DeprecationWarning): - FlowController.remove_flow_controller("do_while") - self.assertEqual(controllers_length - 1, len(FlowController.registered_controllers)) - with self.assertWarns(DeprecationWarning): - FlowController.add_flow_controller("do_while", DoWhileController) - self.assertEqual(controllers_length, len(FlowController.registered_controllers)) - self.passmanager.append( - [PassB_TP_RA_PA(), PassC_TP_RA_PA()], - do_while=lambda property_set: True, - max_iteration=2, - ) - self.assertSchedulerRaises( - self.circuit, - self.passmanager, - [ - "run transformation pass PassA_TP_NR_NP", - "run transformation pass PassB_TP_RA_PA", - "run transformation pass PassC_TP_RA_PA", - "run transformation pass PassB_TP_RA_PA", - "run transformation pass PassC_TP_RA_PA", - ], - TranspilerError, - ) - - def test_remove_nonexistent_plugin(self): - """Tries to remove a plugin that does not exist.""" - with self.assertRaises(KeyError): - with self.assertWarns(DeprecationWarning): - FlowController.remove_flow_controller("foo") - class TestDumpPasses(SchedulerTestCase): """Testing the passes method.""" @@ -673,12 +632,8 @@ def test_passes(self): passmanager = PassManager() passmanager.append(PassC_TP_RA_PA()) passmanager.append(PassB_TP_RA_PA()) - - expected = [ - {"flow_controllers": {}, "passes": [PassC_TP_RA_PA()]}, - {"flow_controllers": {}, "passes": [PassB_TP_RA_PA()]}, - ] - self.assertEqual(expected, passmanager.passes()) + expected = PassC_TP_RA_PA(), PassB_TP_RA_PA() + self.assertEqual(expected, passmanager.to_flow_controller().tasks) def test_passes_in_linear(self): """Dump passes in the same FlowControllerLinear""" @@ -690,54 +645,34 @@ def test_passes_in_linear(self): PassB_TP_RA_PA(), ] ) - - expected = [ - { - "flow_controllers": {}, - "passes": [ - PassC_TP_RA_PA(), - PassB_TP_RA_PA(), - PassD_TP_NR_NP(argument1=[1, 2]), - PassB_TP_RA_PA(), - ], - } - ] - self.assertEqual(expected, passmanager.passes()) - - def test_control_flow_plugin(self): - """Dump passes in a custom flow controller.""" - passmanager = PassManager() - with self.assertWarns(DeprecationWarning): - FlowController.add_flow_controller("do_x_times", DoXTimesController) - passmanager.append([PassB_TP_RA_PA(), PassC_TP_RA_PA()], do_x_times=lambda x: 3) - - expected = [ - {"passes": [PassB_TP_RA_PA(), PassC_TP_RA_PA()], "flow_controllers": {"do_x_times"}} - ] - self.assertEqual(expected, passmanager.passes()) + expected = ( + PassC_TP_RA_PA(), + PassB_TP_RA_PA(), + PassD_TP_NR_NP(argument1=[1, 2]), + PassB_TP_RA_PA(), + ) + self.assertEqual(expected, passmanager.to_flow_controller().tasks) def test_conditional_and_loop(self): """Dump passes with a conditional and a loop.""" - passmanager = PassManager() - passmanager.append(PassE_AP_NR_NP(True)) - passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], - condition=lambda property_set: property_set["property_fixed_point"], - ) - - expected = [ - {"passes": [PassE_AP_NR_NP(True)], "flow_controllers": {}}, - { - "passes": [ + nested_controller = ConditionalController( + DoWhileController( + [ PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property(), ], - "flow_controllers": {"condition", "do_while"}, - }, - ] - self.assertEqual(expected, passmanager.passes()) + do_while=lambda property_set: not property_set["property_fixed_point"], + ), + condition=lambda property_set: property_set["property_fixed_point"], + ) + + passmanager = PassManager() + passmanager.append(PassE_AP_NR_NP(True)) + passmanager.append(nested_controller) + + expected = (PassE_AP_NR_NP(True), nested_controller) + self.assertEqual(expected, passmanager.to_flow_controller().tasks) class StreamHandlerRaiseException(StreamHandler): @@ -801,33 +736,22 @@ def test_passes_in_linear(self): ], ) - def test_control_flow_plugin(self): - """Dump passes in a custom flow controller.""" - passmanager = PassManager() - with self.assertWarns(DeprecationWarning): - FlowController.add_flow_controller("do_x_times", DoXTimesController) - passmanager.append([PassB_TP_RA_PA(), PassC_TP_RA_PA()], do_x_times=lambda x: 3) - self.assertPassLog( - passmanager, - [ - "PassA_TP_NR_NP", - "PassB_TP_RA_PA", - "PassC_TP_RA_PA", - "PassB_TP_RA_PA", - "PassC_TP_RA_PA", - "PassB_TP_RA_PA", - "PassC_TP_RA_PA", - ], - ) - def test_conditional_and_loop(self): """Dump passes with a conditional and a loop""" passmanager = PassManager() passmanager.append(PassE_AP_NR_NP(True)) passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], - condition=lambda property_set: property_set["property_fixed_point"], + ConditionalController( + DoWhileController( + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ), + condition=lambda property_set: property_set["property_fixed_point"], + ) ) self.assertPassLog(passmanager, ["PassE_AP_NR_NP"]) @@ -858,7 +782,9 @@ def test_conditional_twice(self): """Run a conditional twice.""" self.passmanager.append(PassE_AP_NR_NP(True)) self.passmanager.append( - PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ConditionalController( + PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ) ) expected = [ @@ -873,8 +799,14 @@ def test_conditional_twice(self): def test_fixed_point_twice(self): """A fixed point scheduler, twice.""" self.passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], + DoWhileController( + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ) ) expected = [ @@ -1015,7 +947,10 @@ def test_replace_with_conditional(self): self.passmanager.append(PassB_TP_RA_PA()) self.passmanager.replace( - 1, PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + 1, + ConditionalController( + PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ), ) expected = ["run analysis pass PassE_AP_NR_NP", "set property as False"] @@ -1071,9 +1006,17 @@ def test_accessing_passmanager_by_index_with_condition(self): """test accessing PassManager's conditioned passes by index""" self.passmanager.append(PassF_reduce_dag_property()) self.passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - condition=lambda property_set: True, - do_while=lambda property_set: not property_set["property_fixed_point"], + ConditionalController( + DoWhileController( + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ), + condition=lambda property_set: True, + ) ) new_passmanager = self.passmanager[1] @@ -1145,7 +1088,10 @@ def test_accessing_passmanager_by_range_with_condition(self): self.passmanager.append(PassB_TP_RA_PA()) self.passmanager.append(PassE_AP_NR_NP(True)) self.passmanager.append( - PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ConditionalController( + PassA_TP_NR_NP(), + condition=lambda property_set: property_set["property"], + ) ) self.passmanager.append(PassB_TP_RA_PA()) @@ -1194,7 +1140,9 @@ def test_concatenating_passmanagers_with_condition(self): self.passmanager1.append(PassE_AP_NR_NP(True)) self.passmanager1.append(PassB_TP_RA_PA()) self.passmanager2.append( - PassC_TP_RA_PA(), condition=lambda property_set: property_set["property"] + ConditionalController( + PassC_TP_RA_PA(), condition=lambda property_set: property_set["property"] + ) ) self.passmanager2.append(PassB_TP_RA_PA()) @@ -1247,7 +1195,9 @@ def test_adding_list_of_passes_to_passmanager_with_condition(self): """test adding a list of passes to a PassManager that have conditions""" self.passmanager1.append(PassE_AP_NR_NP(False)) self.passmanager1.append( - PassB_TP_RA_PA(), condition=lambda property_set: property_set["property"] + ConditionalController( + PassB_TP_RA_PA(), condition=lambda property_set: property_set["property"] + ) ) self.passmanager1 += PassC_TP_RA_PA() diff --git a/test/python/transpiler/test_passmanager.py b/test/python/transpiler/test_passmanager.py index 3ed34083ee31..181dace29acf 100644 --- a/test/python/transpiler/test_passmanager.py +++ b/test/python/transpiler/test_passmanager.py @@ -21,7 +21,11 @@ from qiskit import QuantumRegister, QuantumCircuit from qiskit.circuit.library import U2Gate from qiskit.converters import circuit_to_dag -from qiskit.passmanager.flow_controllers import FlowControllerLinear +from qiskit.passmanager.flow_controllers import ( + FlowControllerLinear, + ConditionalController, + DoWhileController, +) from qiskit.transpiler import PassManager, PropertySet, TransformationPass from qiskit.transpiler.passes import CommutativeCancellation from qiskit.transpiler.passes import Optimize1qGates, Unroller @@ -144,20 +148,20 @@ def condition(_): def make_inner(prefix): inner = PassManager() inner.append(DummyPass(f"{prefix} 1")) - inner.append(DummyPass(f"{prefix} 2"), condition=lambda _: False) - inner.append(DummyPass(f"{prefix} 3"), condition=lambda _: True) - inner.append(DummyPass(f"{prefix} 4"), do_while=repeat(1)) + inner.append(ConditionalController(DummyPass(f"{prefix} 2"), condition=lambda _: False)) + inner.append(ConditionalController(DummyPass(f"{prefix} 3"), condition=lambda _: True)) + inner.append(DoWhileController(DummyPass(f"{prefix} 4"), do_while=repeat(1))) return inner.to_flow_controller() self.assertIsInstance(make_inner("test"), FlowControllerLinear) outer = PassManager() outer.append(make_inner("first")) - outer.append(make_inner("second"), condition=lambda _: False) + outer.append(ConditionalController(make_inner("second"), condition=lambda _: False)) # The intent of this `condition=repeat(1)` is to ensure that the outer condition is only # checked once and not flattened into the inner controllers; an inner pass invalidating the # condition should not affect subsequent passes once the initial condition was met. - outer.append(make_inner("third"), condition=repeat(1)) + outer.append(ConditionalController(make_inner("third"), condition=repeat(1))) calls = [] diff --git a/test/python/transpiler/test_preset_passmanagers.py b/test/python/transpiler/test_preset_passmanagers.py index 1e737abdcfc2..190227f99b11 100644 --- a/test/python/transpiler/test_preset_passmanagers.py +++ b/test/python/transpiler/test_preset_passmanagers.py @@ -1296,13 +1296,11 @@ def get_translation_stage_plugin(self): pm = generate_preset_pass_manager(optimization_level, backend=target) self.assertIsInstance(pm, PassManager) - pass_list = [y.__class__.__name__ for x in pm.passes() for y in x["passes"]] + pass_list = [x.__class__.__name__ for x in pm.to_flow_controller().tasks] self.assertIn("PadDynamicalDecoupling", pass_list) self.assertIn("ALAPScheduleAnalysis", pass_list) post_translation_pass_list = [ - y.__class__.__name__ - for x in pm.translation.passes() # pylint: disable=no-member - for y in x["passes"] + x.__class__.__name__ for x in pm.translation.to_flow_controller().tasks ] self.assertIn("RemoveResetInZeroState", post_translation_pass_list) @@ -1330,13 +1328,11 @@ def get_translation_stage_plugin(self): pm = generate_preset_pass_manager(optimization_level, backend=target) self.assertIsInstance(pm, PassManager) - pass_list = [y.__class__.__name__ for x in pm.passes() for y in x["passes"]] + pass_list = [x.__class__.__name__ for x in pm.to_flow_controller().tasks] self.assertIn("PadDynamicalDecoupling", pass_list) self.assertIn("ALAPScheduleAnalysis", pass_list) post_translation_pass_list = [ - y.__class__.__name__ - for x in pm.translation.passes() # pylint: disable=no-member - for y in x["passes"] + x.__class__.__name__ for x in pm.translation.to_flow_controller().tasks ] self.assertIn("RemoveResetInZeroState", post_translation_pass_list) @@ -1364,13 +1360,11 @@ def get_translation_stage_plugin(self): pm = generate_preset_pass_manager(optimization_level, backend=target) self.assertIsInstance(pm, PassManager) - pass_list = [y.__class__.__name__ for x in pm.passes() for y in x["passes"]] + pass_list = [x.__class__.__name__ for x in pm.to_flow_controller().tasks] self.assertIn("PadDynamicalDecoupling", pass_list) self.assertIn("ALAPScheduleAnalysis", pass_list) post_translation_pass_list = [ - y.__class__.__name__ - for x in pm.translation.passes() # pylint: disable=no-member - for y in x["passes"] + x.__class__.__name__ for x in pm.translation.to_flow_controller().tasks ] self.assertIn("RemoveResetInZeroState", post_translation_pass_list) @@ -1398,14 +1392,10 @@ def get_translation_stage_plugin(self): pm = generate_preset_pass_manager(optimization_level, backend=target) self.assertIsInstance(pm, PassManager) - pass_list = [y.__class__.__name__ for x in pm.passes() for y in x["passes"]] + pass_list = [x.__class__.__name__ for x in pm.to_flow_controller().tasks] self.assertIn("PadDynamicalDecoupling", pass_list) self.assertIn("ALAPScheduleAnalysis", pass_list) - post_translation_pass_list = [ - y.__class__.__name__ - for x in pm.translation.passes() # pylint: disable=no-member - for y in x["passes"] - ] + post_translation_pass_list = [x.__class__.__name__ for x in pm.to_flow_controller().tasks] self.assertIn("RemoveResetInZeroState", post_translation_pass_list) def test_generate_preset_pass_manager_with_list_coupling_map(self): diff --git a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py index d58bc47bf57d..b68eca204bba 100644 --- a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py +++ b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py @@ -17,6 +17,7 @@ from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister from qiskit.circuit.library import U1Gate, CU1Gate +from qiskit.passmanager.flow_controllers import DoWhileController from qiskit.transpiler import PassManager from qiskit.transpiler.passes import RemoveDiagonalGatesBeforeMeasure, DAGFixedPoint from qiskit.converters import circuit_to_dag @@ -450,8 +451,10 @@ def test_optimize_rz_z(self): pass_manager = PassManager() pass_manager.append( - [RemoveDiagonalGatesBeforeMeasure(), DAGFixedPoint()], - do_while=lambda property_set: not property_set["dag_fixed_point"], + DoWhileController( + [RemoveDiagonalGatesBeforeMeasure(), DAGFixedPoint()], + do_while=lambda property_set: not property_set["dag_fixed_point"], + ) ) after = pass_manager.run(circuit) diff --git a/test/python/transpiler/test_remove_reset_in_zero_state.py b/test/python/transpiler/test_remove_reset_in_zero_state.py index 64767d1e07ec..ebcd2087c0ec 100644 --- a/test/python/transpiler/test_remove_reset_in_zero_state.py +++ b/test/python/transpiler/test_remove_reset_in_zero_state.py @@ -15,6 +15,7 @@ import unittest from qiskit import QuantumRegister, QuantumCircuit +from qiskit.passmanager.flow_controllers import DoWhileController from qiskit.transpiler import PassManager from qiskit.transpiler.passes import RemoveResetInZeroState, DAGFixedPoint from qiskit.converters import circuit_to_dag @@ -94,8 +95,10 @@ def test_two_resets(self): pass_manager = PassManager() pass_manager.append( - [RemoveResetInZeroState(), DAGFixedPoint()], - do_while=lambda property_set: not property_set["dag_fixed_point"], + DoWhileController( + [RemoveResetInZeroState(), DAGFixedPoint()], + do_while=lambda property_set: not property_set["dag_fixed_point"], + ) ) after = pass_manager.run(circuit) diff --git a/test/python/transpiler/test_staged_passmanager.py b/test/python/transpiler/test_staged_passmanager.py index 677426bad3d6..33bec0f4f913 100644 --- a/test/python/transpiler/test_staged_passmanager.py +++ b/test/python/transpiler/test_staged_passmanager.py @@ -37,7 +37,7 @@ def test_default_stages(self): scheduling=PassManager([Depth()]), ) self.assertEqual( - [x.__class__.__name__ for passes in spm.passes() for x in passes["passes"]], + [x.__class__.__name__ for x in spm.to_flow_controller().tasks], ["Optimize1qGates", "Unroller", "Depth"], ) @@ -45,14 +45,14 @@ def test_inplace_edit(self): spm = StagedPassManager(stages=["single_stage"]) spm.single_stage = PassManager([Optimize1qGates(), Depth()]) self.assertEqual( - [x.__class__.__name__ for passes in spm.passes() for x in passes["passes"]], + [x.__class__.__name__ for x in spm.to_flow_controller().tasks], ["Optimize1qGates", "Depth"], ) with self.assertWarns(DeprecationWarning): spm.single_stage.append(Unroller(["u"])) spm.single_stage.append(Depth()) self.assertEqual( - [x.__class__.__name__ for passes in spm.passes() for x in passes["passes"]], + [x.__class__.__name__ for x in spm.to_flow_controller().tasks], ["Optimize1qGates", "Depth", "Unroller", "Depth"], ) @@ -62,8 +62,9 @@ def test_invalid_stage(self): def test_pre_phase_is_valid_stage(self): spm = StagedPassManager(stages=["init"], pre_init=PassManager([Depth()])) + self.assertEqual( - [x.__class__.__name__ for passes in spm.passes() for x in passes["passes"]], + [x.__class__.__name__ for x in spm.to_flow_controller().tasks], ["Depth"], ) @@ -110,16 +111,16 @@ def test_repeated_stages(self): spm = StagedPassManager( stages, pre_alpha=pre_alpha, alpha=alpha, post_alpha=post_alpha, omega=omega ) - passes = [ - *pre_alpha.passes(), - *alpha.passes(), - *post_alpha.passes(), - *omega.passes(), - *pre_alpha.passes(), - *alpha.passes(), - *post_alpha.passes(), - ] - self.assertEqual(spm.passes(), passes) + passes = ( + *pre_alpha.to_flow_controller().tasks, + *alpha.to_flow_controller().tasks, + *post_alpha.to_flow_controller().tasks, + *omega.to_flow_controller().tasks, + *pre_alpha.to_flow_controller().tasks, + *alpha.to_flow_controller().tasks, + *post_alpha.to_flow_controller().tasks, + ) + self.assertEqual(spm.to_flow_controller().tasks, passes) def test_edit_stages(self): spm = StagedPassManager() diff --git a/test/python/visualization/references/pass_manager_standard.dot b/test/python/visualization/references/pass_manager_standard.dot index e1c5d3979ca2..bdb2137f33a3 100644 --- a/test/python/visualization/references/pass_manager_standard.dot +++ b/test/python/visualization/references/pass_manager_standard.dot @@ -10,7 +10,7 @@ labeljust=l; subgraph cluster_3 { fontname=helvetica; -label="[1] condition"; +label="[1] ConditionalController"; labeljust=l; 4 [color=red, fontname=helvetica, label=TrivialLayout, shape=rectangle]; 5 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; @@ -58,7 +58,7 @@ labeljust=l; subgraph cluster_17 { fontname=helvetica; -label="[6] do_while"; +label="[6] DoWhileController"; labeljust=l; 18 [color=blue, fontname=helvetica, label=BarrierBeforeFinalMeasurements, shape=rectangle]; 19 [color=black, fontname=helvetica, fontsize=10, label=label, shape=ellipse, style=dashed]; diff --git a/test/python/visualization/references/pass_manager_style.dot b/test/python/visualization/references/pass_manager_style.dot index 0c80853d00be..3de498c4ada7 100644 --- a/test/python/visualization/references/pass_manager_style.dot +++ b/test/python/visualization/references/pass_manager_style.dot @@ -10,7 +10,7 @@ labeljust=l; subgraph cluster_3 { fontname=helvetica; -label="[1] condition"; +label="[1] ConditionalController"; labeljust=l; 4 [color=red, fontname=helvetica, label=TrivialLayout, shape=rectangle]; 5 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; @@ -58,7 +58,7 @@ labeljust=l; subgraph cluster_17 { fontname=helvetica; -label="[6] do_while"; +label="[6] DoWhileController"; labeljust=l; 18 [color=blue, fontname=helvetica, label=BarrierBeforeFinalMeasurements, shape=rectangle]; 19 [color=black, fontname=helvetica, fontsize=10, label=label, shape=ellipse, style=dashed]; diff --git a/test/python/visualization/test_pass_manager_drawer.py b/test/python/visualization/test_pass_manager_drawer.py index b3456b141406..6e53a895f67d 100644 --- a/test/python/visualization/test_pass_manager_drawer.py +++ b/test/python/visualization/test_pass_manager_drawer.py @@ -18,6 +18,7 @@ from qiskit.transpiler import CouplingMap, Layout from qiskit.transpiler.passmanager import PassManager from qiskit import QuantumRegister +from qiskit.passmanager.flow_controllers import ConditionalController, DoWhileController from qiskit.transpiler.passes import GateDirection, Unroller from qiskit.transpiler.passes import CheckMap from qiskit.transpiler.passes import SetLayout @@ -48,13 +49,17 @@ def setUp(self): # Create a pass manager with a variety of passes and flow control structures self.pass_manager = PassManager() self.pass_manager.append(SetLayout(layout)) - self.pass_manager.append(TrivialLayout(coupling_map), condition=lambda x: True) + self.pass_manager.append( + ConditionalController(TrivialLayout(coupling_map), condition=lambda x: True) + ) self.pass_manager.append(FullAncillaAllocation(coupling_map)) self.pass_manager.append(EnlargeWithAncilla()) with self.assertWarns(DeprecationWarning): self.pass_manager.append(Unroller(basis_gates)) self.pass_manager.append(CheckMap(coupling_map)) - self.pass_manager.append(BarrierBeforeFinalMeasurements(), do_while=lambda x: False) + self.pass_manager.append( + DoWhileController(BarrierBeforeFinalMeasurements(), do_while=lambda x: False) + ) self.pass_manager.append(GateDirection(coupling_map)) self.pass_manager.append(RemoveResetInZeroState()) From 3c538f356c0066c2a26aacee5bce884cf83d6171 Mon Sep 17 00:00:00 2001 From: Ian Hincks Date: Thu, 11 Jan 2024 06:05:57 -0500 Subject: [PATCH 7/9] Provide return annotations to all `providers.JobV1.result()` methods (#11531) * Make providers.JobV1 generic on result() return Makes `JobV1` a `Generic[T]` class where `T` is the return type of the `run()` method. The intended value is to allow tools such as the primitives to be more explicit about what the jobs they return will contain. * Remove future import * alphebetize imports * switch to covariant TypeVar * remove generic and just use Result * avoid circular import * fix circular imports attempt 2 --- qiskit/providers/job.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/qiskit/providers/job.py b/qiskit/providers/job.py index fc797ba691b0..55ee2423e4e4 100644 --- a/qiskit/providers/job.py +++ b/qiskit/providers/job.py @@ -12,15 +12,20 @@ """Job abstract interface.""" +from __future__ import annotations + import time from abc import ABC, abstractmethod -from typing import Callable, Optional +from typing import Callable, Optional, TYPE_CHECKING from qiskit.exceptions import QiskitError from qiskit.providers.backend import Backend from qiskit.providers.exceptions import JobTimeoutError from qiskit.providers.jobstatus import JOB_FINAL_STATES, JobStatus +if TYPE_CHECKING: + from qiskit.result import Result + class Job: """Base common type for all versioned Job abstract classes. @@ -128,7 +133,7 @@ def submit(self): pass @abstractmethod - def result(self): + def result(self) -> Result: """Return the results of the job.""" pass @@ -137,6 +142,6 @@ def cancel(self): raise NotImplementedError @abstractmethod - def status(self): + def status(self) -> JobStatus: """Return the status of the job, among the values of ``JobStatus``.""" pass From 4ce0049af70c14eefca4a5d6d3860032b07a9b3a Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Thu, 11 Jan 2024 11:55:14 +0000 Subject: [PATCH 8/9] Warn on import failures within optional dependencies (#11522) * Warn on import failures within optional dependencies This distinguishes the case of "failed to find an optional dependency" from "found the optional dependency, but it failed to import" within the lazy testers. Now, a warning containing the import failures will be shown to users, rather than silently treating the dependency as missing. This occurred recently when a PR caused an import failure in Aer, which the CI suite failed to detect because the optional-dependency checkers allowed the test suite to pass regardless. Note that this commit alone won't reliably cause the test suite to fail on a failed import because: 1. this only emits a warning, not an exception (by design). 2. the `unittest` decorators are evaluated during test discovery, which happens before we activate our increased warning filters. This commit also unifies the now two Qiskit-specific warnings (the other being `QPYLoadingDeprecatedFeatureWarning`) with a common `QiskitWarning` subclass, much as we do for `QiskitError`. This gives us a convenient way to globally deny these errors during CI runs, without turning _all_ `UserWarning`s into errors, which we're not ready to do yet. * Suppress unnecessary lint warnings * Fix warning when failed import is parent package --- qiskit/exceptions.py | 31 +++++- qiskit/qpy/exceptions.py | 4 +- qiskit/test/base.py | 3 + qiskit/utils/lazy_tester.py | 45 +++++++- ...arn-on-import-errors-95a9bdaacc9c3d2b.yaml | 14 +++ test/python/utils/test_lazy_loaders.py | 103 +++++++++++++++++- 6 files changed, 189 insertions(+), 11 deletions(-) create mode 100644 releasenotes/notes/lazy-testers-warn-on-import-errors-95a9bdaacc9c3d2b.yaml diff --git a/qiskit/exceptions.py b/qiskit/exceptions.py index 048f79dfbc62..e26d4097fa90 100644 --- a/qiskit/exceptions.py +++ b/qiskit/exceptions.py @@ -15,7 +15,10 @@ Top-level exceptions (:mod:`qiskit.exceptions`) =============================================== -All Qiskit-related errors raised by Qiskit are subclasses of the base: +Exceptions +========== + +All Qiskit-related exceptions raised by Qiskit are subclasses of the base: .. autoexception:: QiskitError @@ -42,6 +45,22 @@ .. autoexception:: QiskitUserConfigError .. autoexception:: InvalidFileError + + +Warnings +======== + +Some particular features of Qiskit may raise custom warnings. In general, Qiskit will use built-in +Python warnings (such as :exc:`DeprecationWarning`) when appropriate, but warnings related to +Qiskit-specific functionality will be subtypes of :exc:`QiskitWarning`. + +.. autoexception:: QiskitWarning + +Related to :exc:`MissingOptionalLibraryError`, in some cases an optional dependency might be found, +but fail to import for some other reason. In this case, Qiskit will continue as if the dependency +is not present, but will raise :exc:`OptionalDependencyImportWarning` to let you know about it. + +.. autoexception:: OptionalDependencyImportWarning """ from typing import Optional @@ -95,3 +114,13 @@ def __str__(self) -> str: class InvalidFileError(QiskitError): """Raised when the file provided is not valid for the specific task.""" + + +class QiskitWarning(UserWarning): + """Common subclass of warnings for Qiskit-specific warnings being raised.""" + + +class OptionalDependencyImportWarning(QiskitWarning): + """Raised when an optional library raises errors during its import.""" + + # Not a subclass of `ImportWarning` because those are hidden by default. diff --git a/qiskit/qpy/exceptions.py b/qiskit/qpy/exceptions.py index 476e39eaa337..c6cdb4303a62 100644 --- a/qiskit/qpy/exceptions.py +++ b/qiskit/qpy/exceptions.py @@ -12,7 +12,7 @@ """Exception for errors raised by the QPY module.""" -from qiskit.exceptions import QiskitError +from qiskit.exceptions import QiskitError, QiskitWarning class QpyError(QiskitError): @@ -28,6 +28,6 @@ def __str__(self): return repr(self.message) -class QPYLoadingDeprecatedFeatureWarning(UserWarning): +class QPYLoadingDeprecatedFeatureWarning(QiskitWarning): """Visible deprecation warning for QPY loading functions without a stable point in the call stack.""" diff --git a/qiskit/test/base.py b/qiskit/test/base.py index e9b9e8e2f710..19103fec59f2 100644 --- a/qiskit/test/base.py +++ b/qiskit/test/base.py @@ -28,6 +28,7 @@ import unittest from unittest.util import safe_repr +from qiskit.exceptions import QiskitWarning from qiskit.tools.parallel import get_platform_parallel_default from qiskit.utils import optionals as _optionals from qiskit.circuit import QuantumCircuit @@ -201,6 +202,8 @@ def setUpClass(cls): setup_test_logging(cls.log, os.getenv("LOG_LEVEL"), filename) warnings.filterwarnings("error", category=DeprecationWarning) + warnings.filterwarnings("error", category=QiskitWarning) + allow_DeprecationWarning_modules = [ "test.python.pulse.test_builder", "test.python.pulse.test_block", diff --git a/qiskit/utils/lazy_tester.py b/qiskit/utils/lazy_tester.py index 4f0d8e5a37aa..d3e5cd971185 100644 --- a/qiskit/utils/lazy_tester.py +++ b/qiskit/utils/lazy_tester.py @@ -13,14 +13,16 @@ """Lazy testers for optional features.""" import abc +import collections import contextlib import functools import importlib import subprocess import typing +import warnings from typing import Union, Iterable, Dict, Optional, Callable, Type -from qiskit.exceptions import MissingOptionalLibraryError +from qiskit.exceptions import MissingOptionalLibraryError, OptionalDependencyImportWarning from .classtools import wrap_method @@ -284,12 +286,43 @@ def __init__( super().__init__(name=name, callback=callback, install=install, msg=msg) def _is_available(self): - try: - for module, names in self._modules.items(): + failed_modules = {} + failed_names = collections.defaultdict(list) + for module, names in self._modules.items(): + try: imported = importlib.import_module(module) - for name in names: - getattr(imported, name) - except (ImportError, AttributeError): + except ModuleNotFoundError as exc: + failed_parts = exc.name.split(".") + target_parts = module.split(".") + if failed_parts == target_parts[: len(failed_parts)]: + # If the module that wasn't found is the one we were explicitly searching for + # (or one of its parents), then it's just not installed. + return False + # Otherwise, we _did_ find the module, it just didn't import, which is a problem. + failed_modules[module] = exc + continue + except ImportError as exc: + failed_modules[module] = exc + continue + for name in names: + try: + _ = getattr(imported, name) + except AttributeError: + failed_names[module].append(name) + if failed_modules or failed_names: + package_description = f"'{self._name}'" if self._name else "optional packages" + message = ( + f"While trying to import {package_description}," + " some components were located but raised other errors during import." + " You might have an incompatible version installed." + " Qiskit will continue as if the optional is not available." + ) + for module, exc in failed_modules.items(): + message += "".join(f"\n - module '{module}' failed to import with: {exc!r}") + for module, names in failed_names.items(): + attributes = f"attribute '{names[0]}'" if len(names) == 1 else f"attributes {names}" + message += "".join(f"\n - '{module}' imported, but {attributes} couldn't be found") + warnings.warn(message, category=OptionalDependencyImportWarning) return False return True diff --git a/releasenotes/notes/lazy-testers-warn-on-import-errors-95a9bdaacc9c3d2b.yaml b/releasenotes/notes/lazy-testers-warn-on-import-errors-95a9bdaacc9c3d2b.yaml new file mode 100644 index 000000000000..0a3167cc1f66 --- /dev/null +++ b/releasenotes/notes/lazy-testers-warn-on-import-errors-95a9bdaacc9c3d2b.yaml @@ -0,0 +1,14 @@ +--- +features: + - | + A new warning base class, :exc:`.QiskitWarning`, was added. While Qiskit will continue to use + built-in Python warnings (such as :exc:`DeprecationWarning`) when those are most appropriate, + for cases that are more specific to Qiskit, the warnings will be subclasses of :exc:`.QiskitWarning`. + - | + :exc:`.QPYLoadingDeprecatedFeatureWarning` is now a subclass of :exc:`.QiskitWarning`. + - | + The optional-functionality testers (:mod:`qiskit.utils.optionals`) will now distinguish an + optional dependency that was completely not found (a normal situation) with one that was found, + but triggered errors during its import. In the latter case, they will now issue an + :exc:`.OptionalDependencyImportWarning` telling you what happened, since it might indicate a + failed installation or an incompatible version. diff --git a/test/python/utils/test_lazy_loaders.py b/test/python/utils/test_lazy_loaders.py index 614bea15f04d..7a8c16eda2fe 100644 --- a/test/python/utils/test_lazy_loaders.py +++ b/test/python/utils/test_lazy_loaders.py @@ -12,12 +12,17 @@ """Tests for the lazy loaders.""" +from __future__ import annotations + +import importlib.abc +import importlib.util import sys +import warnings from unittest import mock import ddt -from qiskit.exceptions import MissingOptionalLibraryError +from qiskit.exceptions import MissingOptionalLibraryError, OptionalDependencyImportWarning from qiskit.test import QiskitTestCase from qiskit.utils import LazyImportTester, LazySubprocessTester @@ -49,6 +54,29 @@ def mock_availability_test(feature): return mock.patch.object(type(feature), "_is_available", wraps=feature._is_available) +def patch_imports(mapping: dict[str, importlib.abc.Loader]): + """Patch the import system so that the given named modules will skip the regular search system + and instead be loaded by the given loaders. + + Already imported modules will not be affected; this should use uniquely named modules.""" + + class OverrideLoaders(importlib.abc.MetaPathFinder): + """A metapath finder that will simply return an explicit loader for specific modules.""" + + def __init__(self, mapping: dict[str, importlib.abc.Loader]): + self.mapping = mapping + + def find_spec(self, fullname, path, target=None): + """Implementation of the abstract (but undefined) method.""" + del path, target # ABC parameters we don't need. + if (loader := self.mapping.get(fullname)) is not None: + return importlib.util.spec_from_loader(fullname, loader) + return None + + new_path = [OverrideLoaders(mapping)] + sys.meta_path + return mock.patch.object(sys, "meta_path", new_path) + + @ddt.ddt class TestLazyDependencyTester(QiskitTestCase): """Tests for the lazy loaders. Within this class, we parameterise the test cases with @@ -71,6 +99,23 @@ def test_evaluates_correctly_false(self, test_generator): if test_generator(): self.fail("did not evaluate false") + def test_submodule_import_detects_false_correctly(self): + """Test that a lazy import of a submodule where the parent is not available still generates + a silent failure.""" + + # The idea here is that the base package is what will fail the import, and the corresponding + # `ImportError.name` won't be the same as the full path we were trying to import. We want + # to make sure that the "was it found and failed to import?" handling is correct in this + # case. + def checker(): + return LazyImportTester("_qiskit_module_does_not_exist_.submodule") + + # Just in case something else is allowing the warnings, but they should be forbidden by + # default. + with warnings.catch_warnings(record=True) as log: + self.assertFalse(checker()) + self.assertEqual(log, []) + @ddt.data(available_importer, available_process, unavailable_importer, unavailable_process) def test_check_occurs_once(self, test_generator): """Check that the test of availability is only performed once.""" @@ -324,6 +369,59 @@ class Module: vars(type(mock_module))[attribute].assert_called() vars(type(mock_module))["unaccessed_attribute"].assert_not_called() + def test_warns_on_import_error(self): + """Check that the module raising an `ImportError` other than being not found is warned + against.""" + + # pylint: disable=missing-class-docstring,missing-function-docstring,abstract-method + + class RaisesImportErrorOnLoad(importlib.abc.Loader): + def __init__(self, name): + self.name = name + + def create_module(self, spec): + raise ImportError("sentinel import failure", name=self.name) + + def exec_module(self, module): + pass + + dummy = f"{__name__}_{type(self).__name__}_test_warns_on_import_error".replace(".", "_") + tester = LazyImportTester(dummy) + with patch_imports({dummy: RaisesImportErrorOnLoad(dummy)}): + with self.assertWarnsRegex( + OptionalDependencyImportWarning, + rf"module '{dummy}' failed to import with: .*sentinel import failure.*", + ): + self.assertFalse(tester) + + def test_warns_on_internal_not_found_error(self): + """Check that the module raising an `ModuleNotFoundError` for some module other than itself + (such as a module trying to import parts of Terra that don't exist any more) is caught and + warned against, rather than silently caught as an expected `ModuleNotFoundError`.""" + + # pylint: disable=missing-class-docstring,missing-function-docstring,abstract-method + + class ImportsBadModule(importlib.abc.Loader): + def create_module(self, spec): + # Doesn't matter what, we just want to return any module object; we're going to + # raise an error during "execution" of the module. + return sys + + def exec_module(self, module): + del module # ABC parameter we don't care about. + import __qiskit__some_module_that_does_not_exist + + dummy = f"{__name__}_{type(self).__name__}_test_warns_on_internal_not_found_error".replace( + ".", "_" + ) + tester = LazyImportTester(dummy) + with patch_imports({dummy: ImportsBadModule()}): + with self.assertWarnsRegex( + OptionalDependencyImportWarning, + rf"module '{dummy}' failed to import with: ModuleNotFoundError.*__qiskit__", + ): + self.assertFalse(tester) + def test_import_allows_attributes_failure(self): """Check that the import tester can accept a dictionary mapping module names to attributes, and that these are recognised when they are missing.""" @@ -334,7 +432,8 @@ def test_import_allows_attributes_failure(self): } feature = LazyImportTester(name_map) - self.assertFalse(feature) + with self.assertWarnsRegex(UserWarning, r"'builtins' imported, but attribute"): + self.assertFalse(feature) def test_import_fails_with_no_modules(self): """Catch programmer errors with no modules to test.""" From 6bcb17b2f82d411c57c877584a52ce2c7825b6d7 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 11 Jan 2024 07:39:27 -0500 Subject: [PATCH 9/9] Remove deprecated IBMQ dependent functionality (#11513) * Remove deprecate IBMQ dependent functionality This commit removes all the functionality in Qiskit that depended on the legacy qiskit-ibmq-provider package. This process was started in #10525 which removed the qiskit.IBMQ alias object but as was discovered in that PR there were many other places in qiskit that were relying on the qiskit-ibmq-provider package. This commit removes all of these places, which primarily include qiskit.test.decorators's online_test decorator, qiskit.tools.jupyter, qiskit.tools.monitor, qiskit.test.ibmq_mock, and qiskit.test.mock (which just referenced IBMQ and didn't actually use it). With these removals we're also no longer optionally dependent on ipywidgets, jupyter, or ipython and those are removed from the optional and development requirements lists. * Remove stale api docs * Remove ibmq usage in fake backend update script * Preserve circuit library visualizations * Remove apidocs from index * Remove unused import * Update docs --- docs/apidoc/index.rst | 1 - docs/apidoc/tools_jupyter.rst | 6 - pyproject.toml | 1 - qiskit/__init__.py | 53 -- qiskit/circuit/library/basis_change/qft.py | 6 +- .../library/boolean_logic/inner_product.py | 2 +- .../library/boolean_logic/quantum_and.py | 4 +- .../library/boolean_logic/quantum_or.py | 4 +- .../library/boolean_logic/quantum_xor.py | 2 +- qiskit/circuit/library/fourier_checking.py | 2 +- .../circuit/library/generalized_gates/gms.py | 2 +- .../circuit/library/generalized_gates/gr.py | 8 +- .../circuit/library/generalized_gates/mcmt.py | 2 +- .../library/generalized_gates/permutation.py | 4 +- qiskit/circuit/library/graph_state.py | 2 +- .../circuit/library/hidden_linear_function.py | 2 +- qiskit/circuit/library/iqp.py | 2 +- qiskit/circuit/library/phase_estimation.py | 2 +- qiskit/circuit/library/quantum_volume.py | 2 +- qiskit/providers/__init__.py | 11 +- qiskit/providers/fake_provider/__init__.py | 17 +- qiskit/test/__init__.py | 2 +- qiskit/test/base.py | 2 - qiskit/test/decorators.py | 86 --- qiskit/test/ibmq_mock.py | 45 -- qiskit/test/mock/__init__.py | 40 -- qiskit/test/mock/backends/__init__.py | 32 - qiskit/test/mock/backends/almaden/__init__.py | 32 - qiskit/test/mock/backends/armonk/__init__.py | 32 - qiskit/test/mock/backends/athens/__init__.py | 32 - qiskit/test/mock/backends/belem/__init__.py | 32 - .../test/mock/backends/boeblingen/__init__.py | 32 - qiskit/test/mock/backends/bogota/__init__.py | 32 - .../test/mock/backends/brooklyn/__init__.py | 32 - .../test/mock/backends/burlington/__init__.py | 32 - qiskit/test/mock/backends/cairo/__init__.py | 32 - .../test/mock/backends/cambridge/__init__.py | 32 - .../test/mock/backends/casablanca/__init__.py | 32 - qiskit/test/mock/backends/essex/__init__.py | 32 - .../test/mock/backends/guadalupe/__init__.py | 32 - qiskit/test/mock/backends/hanoi/__init__.py | 32 - qiskit/test/mock/backends/jakarta/__init__.py | 32 - .../mock/backends/johannesburg/__init__.py | 32 - qiskit/test/mock/backends/kolkata/__init__.py | 32 - qiskit/test/mock/backends/lagos/__init__.py | 32 - qiskit/test/mock/backends/lima/__init__.py | 32 - qiskit/test/mock/backends/london/__init__.py | 32 - .../test/mock/backends/manhattan/__init__.py | 32 - qiskit/test/mock/backends/manila/__init__.py | 32 - .../test/mock/backends/melbourne/__init__.py | 32 - .../test/mock/backends/montreal/__init__.py | 32 - qiskit/test/mock/backends/mumbai/__init__.py | 32 - qiskit/test/mock/backends/nairobi/__init__.py | 32 - qiskit/test/mock/backends/ourense/__init__.py | 32 - qiskit/test/mock/backends/paris/__init__.py | 32 - .../mock/backends/poughkeepsie/__init__.py | 32 - qiskit/test/mock/backends/quito/__init__.py | 32 - .../test/mock/backends/rochester/__init__.py | 32 - qiskit/test/mock/backends/rome/__init__.py | 32 - .../mock/backends/rueschlikon/__init__.py | 32 - .../test/mock/backends/santiago/__init__.py | 32 - .../test/mock/backends/singapore/__init__.py | 32 - qiskit/test/mock/backends/sydney/__init__.py | 32 - .../test/mock/backends/tenerife/__init__.py | 32 - qiskit/test/mock/backends/tokyo/__init__.py | 32 - qiskit/test/mock/backends/toronto/__init__.py | 32 - .../test/mock/backends/valencia/__init__.py | 32 - qiskit/test/mock/backends/vigo/__init__.py | 32 - .../test/mock/backends/washington/__init__.py | 32 - .../test/mock/backends/yorktown/__init__.py | 32 - qiskit/tools/__init__.py | 7 - qiskit/tools/jupyter/__init__.py | 138 ---- qiskit/tools/jupyter/backend_monitor.py | 588 ------------------ qiskit/tools/jupyter/backend_overview.py | 322 ---------- qiskit/tools/jupyter/copyright.py | 42 -- qiskit/tools/jupyter/job_watcher.py | 167 ----- qiskit/tools/jupyter/job_widgets.py | 160 ----- qiskit/tools/jupyter/jupyter_magics.py | 190 ------ qiskit/tools/jupyter/library.py | 189 ------ qiskit/tools/jupyter/monospace.py | 29 - qiskit/tools/jupyter/progressbar.py | 122 ---- qiskit/tools/jupyter/version_table.py | 67 -- qiskit/tools/jupyter/watcher_monitor.py | 74 --- qiskit/tools/monitor/__init__.py | 16 - qiskit/tools/monitor/job_monitor.py | 107 ---- qiskit/tools/monitor/overview.py | 247 -------- qiskit/visualization/library.py | 37 ++ .../notes/remove-ibmq-4bb57a04991da9af.yaml | 34 + requirements-optional.txt | 3 - .../mock/test_mock_module_deprecation.py | 28 - .../python/notebooks/test_backend_tools.ipynb | 156 ----- test/python/notebooks/test_pbar_status.ipynb | 270 -------- test/python/test_examples.py | 35 +- test/python/tools/jupyter/__init__.py | 13 - test/python/tools/jupyter/test_notebooks.py | 98 --- test/python/tools/monitor/__init__.py | 13 - .../tools/monitor/test_backend_monitor.py | 103 --- test/python/tools/monitor/test_job_monitor.py | 43 -- tools/update_fake_backends.py | 6 +- 99 files changed, 105 insertions(+), 4917 deletions(-) delete mode 100644 docs/apidoc/tools_jupyter.rst delete mode 100644 qiskit/test/ibmq_mock.py delete mode 100644 qiskit/test/mock/__init__.py delete mode 100644 qiskit/test/mock/backends/__init__.py delete mode 100644 qiskit/test/mock/backends/almaden/__init__.py delete mode 100644 qiskit/test/mock/backends/armonk/__init__.py delete mode 100644 qiskit/test/mock/backends/athens/__init__.py delete mode 100644 qiskit/test/mock/backends/belem/__init__.py delete mode 100644 qiskit/test/mock/backends/boeblingen/__init__.py delete mode 100644 qiskit/test/mock/backends/bogota/__init__.py delete mode 100644 qiskit/test/mock/backends/brooklyn/__init__.py delete mode 100644 qiskit/test/mock/backends/burlington/__init__.py delete mode 100644 qiskit/test/mock/backends/cairo/__init__.py delete mode 100644 qiskit/test/mock/backends/cambridge/__init__.py delete mode 100644 qiskit/test/mock/backends/casablanca/__init__.py delete mode 100644 qiskit/test/mock/backends/essex/__init__.py delete mode 100644 qiskit/test/mock/backends/guadalupe/__init__.py delete mode 100644 qiskit/test/mock/backends/hanoi/__init__.py delete mode 100644 qiskit/test/mock/backends/jakarta/__init__.py delete mode 100644 qiskit/test/mock/backends/johannesburg/__init__.py delete mode 100644 qiskit/test/mock/backends/kolkata/__init__.py delete mode 100644 qiskit/test/mock/backends/lagos/__init__.py delete mode 100644 qiskit/test/mock/backends/lima/__init__.py delete mode 100644 qiskit/test/mock/backends/london/__init__.py delete mode 100644 qiskit/test/mock/backends/manhattan/__init__.py delete mode 100644 qiskit/test/mock/backends/manila/__init__.py delete mode 100644 qiskit/test/mock/backends/melbourne/__init__.py delete mode 100644 qiskit/test/mock/backends/montreal/__init__.py delete mode 100644 qiskit/test/mock/backends/mumbai/__init__.py delete mode 100644 qiskit/test/mock/backends/nairobi/__init__.py delete mode 100644 qiskit/test/mock/backends/ourense/__init__.py delete mode 100644 qiskit/test/mock/backends/paris/__init__.py delete mode 100644 qiskit/test/mock/backends/poughkeepsie/__init__.py delete mode 100644 qiskit/test/mock/backends/quito/__init__.py delete mode 100644 qiskit/test/mock/backends/rochester/__init__.py delete mode 100644 qiskit/test/mock/backends/rome/__init__.py delete mode 100644 qiskit/test/mock/backends/rueschlikon/__init__.py delete mode 100644 qiskit/test/mock/backends/santiago/__init__.py delete mode 100644 qiskit/test/mock/backends/singapore/__init__.py delete mode 100644 qiskit/test/mock/backends/sydney/__init__.py delete mode 100644 qiskit/test/mock/backends/tenerife/__init__.py delete mode 100644 qiskit/test/mock/backends/tokyo/__init__.py delete mode 100644 qiskit/test/mock/backends/toronto/__init__.py delete mode 100644 qiskit/test/mock/backends/valencia/__init__.py delete mode 100644 qiskit/test/mock/backends/vigo/__init__.py delete mode 100644 qiskit/test/mock/backends/washington/__init__.py delete mode 100644 qiskit/test/mock/backends/yorktown/__init__.py delete mode 100644 qiskit/tools/jupyter/__init__.py delete mode 100644 qiskit/tools/jupyter/backend_monitor.py delete mode 100644 qiskit/tools/jupyter/backend_overview.py delete mode 100644 qiskit/tools/jupyter/copyright.py delete mode 100644 qiskit/tools/jupyter/job_watcher.py delete mode 100644 qiskit/tools/jupyter/job_widgets.py delete mode 100644 qiskit/tools/jupyter/jupyter_magics.py delete mode 100644 qiskit/tools/jupyter/library.py delete mode 100644 qiskit/tools/jupyter/monospace.py delete mode 100644 qiskit/tools/jupyter/progressbar.py delete mode 100644 qiskit/tools/jupyter/version_table.py delete mode 100644 qiskit/tools/jupyter/watcher_monitor.py delete mode 100644 qiskit/tools/monitor/__init__.py delete mode 100644 qiskit/tools/monitor/job_monitor.py delete mode 100644 qiskit/tools/monitor/overview.py create mode 100644 qiskit/visualization/library.py create mode 100644 releasenotes/notes/remove-ibmq-4bb57a04991da9af.yaml delete mode 100644 test/python/mock/test_mock_module_deprecation.py delete mode 100644 test/python/notebooks/test_backend_tools.ipynb delete mode 100644 test/python/notebooks/test_pbar_status.ipynb delete mode 100644 test/python/tools/jupyter/__init__.py delete mode 100644 test/python/tools/jupyter/test_notebooks.py delete mode 100644 test/python/tools/monitor/__init__.py delete mode 100644 test/python/tools/monitor/test_backend_monitor.py delete mode 100644 test/python/tools/monitor/test_job_monitor.py diff --git a/docs/apidoc/index.rst b/docs/apidoc/index.rst index 9d1eb2f64960..3fc3e25e457d 100644 --- a/docs/apidoc/index.rst +++ b/docs/apidoc/index.rst @@ -35,7 +35,6 @@ API Reference quantum_info result tools - tools_jupyter transpiler transpiler_passes transpiler_preset diff --git a/docs/apidoc/tools_jupyter.rst b/docs/apidoc/tools_jupyter.rst deleted file mode 100644 index 92691f6ba9a8..000000000000 --- a/docs/apidoc/tools_jupyter.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _qiskit-tools-jupyter: - -.. automodule:: qiskit.tools.jupyter - :no-members: - :no-inherited-members: - :no-special-members: diff --git a/pyproject.toml b/pyproject.toml index 04bafbf3f995..d42b249a1058 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,7 +45,6 @@ qasm3-import = [ ] visualization = [ "matplotlib >= 3.3", - "ipywidgets >= 7.3.0", "pydot", "Pillow >= 4.2.1", "pylatexenc >= 1.4", diff --git a/qiskit/__init__.py b/qiskit/__init__.py index 664cd8c57c5c..444aea12e024 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -74,8 +74,6 @@ _config = _user_config.get_config() -# Moved to after IBMQ and Aer imports due to import issues -# with other modules that check for IBMQ (tools) from qiskit.execute_function import execute from qiskit.compiler import transpile, assemble, schedule, sequence @@ -125,64 +123,13 @@ def __getattr__(self, attr): return getattr(self.aer, attr) -class IBMQWrapper: - """Lazy loading wrapper for IBMQ provider.""" - - def __init__(self): - self.ibmq = None - - def __bool__(self): - if self.ibmq is None: - try: - from qiskit.providers import ibmq - - self.ibmq = ibmq.IBMQ - warnings.warn( - "The qiskit.IBMQ entrypoint and the qiskit-ibmq-provider package (" - "accessible from 'qiskit.providers.ibmq`) are deprecated and will be removed " - "in a future release. Instead you should use the qiskit-ibm-provider package " - "which is accessible from 'qiskit_ibm_provider'. You can install it with " - "'pip install qiskit_ibm_provider'", - DeprecationWarning, - stacklevel=2, - ) - - except ImportError: - return False - return True - - def __getattr__(self, attr): - if not self.ibmq: - try: - from qiskit.providers import ibmq - - self.ibmq = ibmq.IBMQ - warnings.warn( - "The qiskit.IBMQ entrypoint and the qiskit-ibmq-provider package (" - "accessible from 'qiskit.providers.ibmq`) are deprecated and will be removed " - "in a future release. Instead you should use the qiskit-ibm-provider package " - "which is accessible from 'qiskit_ibm_provider'. You can install it with " - "'pip install qiskit_ibm_provider'. Just replace 'qiskit.IBMQ' with " - "'qiskit_ibm_provider.IBMProvider'", - DeprecationWarning, - stacklevel=2, - ) - except ImportError as ex: - raise MissingOptionalLibraryError( - "qiskit-ibmq-provider", "IBMQ provider", "pip install qiskit-ibmq-provider" - ) from ex - return getattr(self.ibmq, attr) - - Aer = AerWrapper() -IBMQ = IBMQWrapper() __all__ = [ "Aer", "AncillaRegister", "BasicAer", "ClassicalRegister", - "IBMQ", "MissingOptionalLibraryError", "QiskitError", "QuantumCircuit", diff --git a/qiskit/circuit/library/basis_change/qft.py b/qiskit/circuit/library/basis_change/qft.py index 7e9262d81943..9dcb1251453f 100644 --- a/qiskit/circuit/library/basis_change/qft.py +++ b/qiskit/circuit/library/basis_change/qft.py @@ -41,7 +41,7 @@ class QFT(BlueprintCircuit): .. plot:: from qiskit.circuit.library import QFT - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = QFT(4) _generate_circuit_library_visualization(circuit) @@ -51,7 +51,7 @@ class QFT(BlueprintCircuit): .. plot:: from qiskit.circuit.library import QFT - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = QFT(4).inverse() _generate_circuit_library_visualization(circuit) @@ -67,7 +67,7 @@ class QFT(BlueprintCircuit): .. plot:: from qiskit.circuit.library import QFT - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = QFT(5, approximation_degree=2) _generate_circuit_library_visualization(circuit) diff --git a/qiskit/circuit/library/boolean_logic/inner_product.py b/qiskit/circuit/library/boolean_logic/inner_product.py index 08524f51048a..6efbc8a02913 100644 --- a/qiskit/circuit/library/boolean_logic/inner_product.py +++ b/qiskit/circuit/library/boolean_logic/inner_product.py @@ -56,7 +56,7 @@ class InnerProduct(QuantumCircuit): .. plot:: from qiskit.circuit.library import InnerProduct - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = InnerProduct(4) _generate_circuit_library_visualization(circuit) """ diff --git a/qiskit/circuit/library/boolean_logic/quantum_and.py b/qiskit/circuit/library/boolean_logic/quantum_and.py index 97c2a0e143b9..ce2d253ca85a 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_and.py +++ b/qiskit/circuit/library/boolean_logic/quantum_and.py @@ -32,7 +32,7 @@ class AND(QuantumCircuit): .. plot:: from qiskit.circuit.library import AND - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = AND(5) _generate_circuit_library_visualization(circuit) @@ -43,7 +43,7 @@ class AND(QuantumCircuit): .. plot:: from qiskit.circuit.library import AND - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = AND(5, flags=[-1, 0, 0, 1, 1]) _generate_circuit_library_visualization(circuit) diff --git a/qiskit/circuit/library/boolean_logic/quantum_or.py b/qiskit/circuit/library/boolean_logic/quantum_or.py index 2ac114ffb1b1..91d6c4fbdd52 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_or.py +++ b/qiskit/circuit/library/boolean_logic/quantum_or.py @@ -33,7 +33,7 @@ class OR(QuantumCircuit): .. plot:: from qiskit.circuit.library import OR - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = OR(5) _generate_circuit_library_visualization(circuit) @@ -44,7 +44,7 @@ class OR(QuantumCircuit): .. plot:: from qiskit.circuit.library import OR - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = OR(5, flags=[-1, 0, 0, 1, 1]) _generate_circuit_library_visualization(circuit) diff --git a/qiskit/circuit/library/boolean_logic/quantum_xor.py b/qiskit/circuit/library/boolean_logic/quantum_xor.py index 08b0055248fa..5b230345137d 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_xor.py +++ b/qiskit/circuit/library/boolean_logic/quantum_xor.py @@ -48,7 +48,7 @@ def __init__( .. plot:: from qiskit.circuit.library import XOR - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = XOR(5, seed=42) _generate_circuit_library_visualization(circuit) """ diff --git a/qiskit/circuit/library/fourier_checking.py b/qiskit/circuit/library/fourier_checking.py index b31dc9748e83..9035fbc360d4 100644 --- a/qiskit/circuit/library/fourier_checking.py +++ b/qiskit/circuit/library/fourier_checking.py @@ -66,7 +66,7 @@ def __init__(self, f: List[int], g: List[int]) -> None: .. plot:: from qiskit.circuit.library import FourierChecking - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization f = [1, -1, -1, -1] g = [1, 1, -1, -1] circuit = FourierChecking(f, g) diff --git a/qiskit/circuit/library/generalized_gates/gms.py b/qiskit/circuit/library/generalized_gates/gms.py index 46b28cac60af..fccb332d438c 100644 --- a/qiskit/circuit/library/generalized_gates/gms.py +++ b/qiskit/circuit/library/generalized_gates/gms.py @@ -44,7 +44,7 @@ class GMS(QuantumCircuit): .. plot:: from qiskit.circuit.library import GMS - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization import numpy as np circuit = GMS(num_qubits=3, theta=[[0, np.pi/4, np.pi/8], [0, 0, np.pi/2], diff --git a/qiskit/circuit/library/generalized_gates/gr.py b/qiskit/circuit/library/generalized_gates/gr.py index acb83909cce0..e79851db4779 100644 --- a/qiskit/circuit/library/generalized_gates/gr.py +++ b/qiskit/circuit/library/generalized_gates/gr.py @@ -47,7 +47,7 @@ class GR(QuantumCircuit): .. plot:: from qiskit.circuit.library import GR - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization import numpy as np circuit = GR(num_qubits=3, theta=np.pi/4, phi=np.pi/2) _generate_circuit_library_visualization(circuit) @@ -101,7 +101,7 @@ class GRX(GR): .. plot:: from qiskit.circuit.library import GRX - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization import numpy as np circuit = GRX(num_qubits=3, theta=np.pi/4) _generate_circuit_library_visualization(circuit) @@ -149,7 +149,7 @@ class GRY(GR): .. plot:: from qiskit.circuit.library import GRY - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization import numpy as np circuit = GRY(num_qubits=3, theta=np.pi/4) _generate_circuit_library_visualization(circuit) @@ -197,7 +197,7 @@ class GRZ(QuantumCircuit): .. plot:: from qiskit.circuit.library import GRZ - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization import numpy as np circuit = GRZ(num_qubits=3, phi=np.pi/2) _generate_circuit_library_visualization(circuit) diff --git a/qiskit/circuit/library/generalized_gates/mcmt.py b/qiskit/circuit/library/generalized_gates/mcmt.py index 1a529b8859e1..c6ec0bda116e 100644 --- a/qiskit/circuit/library/generalized_gates/mcmt.py +++ b/qiskit/circuit/library/generalized_gates/mcmt.py @@ -164,7 +164,7 @@ class MCMTVChain(MCMT): .. plot:: from qiskit.circuit.library import MCMTVChain, ZGate - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = MCMTVChain(ZGate(), 2, 2) _generate_circuit_library_visualization(circuit.decompose()) diff --git a/qiskit/circuit/library/generalized_gates/permutation.py b/qiskit/circuit/library/generalized_gates/permutation.py index 44576518d624..d9151c09f7f7 100644 --- a/qiskit/circuit/library/generalized_gates/permutation.py +++ b/qiskit/circuit/library/generalized_gates/permutation.py @@ -62,7 +62,7 @@ def __init__( .. plot:: from qiskit.circuit.library import Permutation - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization A = [2,4,3,0,1] circuit = Permutation(5, A) _generate_circuit_library_visualization(circuit.decompose()) @@ -130,7 +130,7 @@ def __init__( from qiskit.circuit.quantumcircuit import QuantumCircuit from qiskit.circuit.library import PermutationGate - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization A = [2,4,3,0,1] permutation = PermutationGate(A) circuit = QuantumCircuit(5) diff --git a/qiskit/circuit/library/graph_state.py b/qiskit/circuit/library/graph_state.py index 25a3ccc95a2c..ceefff7971db 100644 --- a/qiskit/circuit/library/graph_state.py +++ b/qiskit/circuit/library/graph_state.py @@ -42,7 +42,7 @@ class GraphState(QuantumCircuit): .. plot:: from qiskit.circuit.library import GraphState - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization import rustworkx as rx G = rx.generators.cycle_graph(5) circuit = GraphState(rx.adjacency_matrix(G)) diff --git a/qiskit/circuit/library/hidden_linear_function.py b/qiskit/circuit/library/hidden_linear_function.py index 4742145ab51f..1140f1866f08 100644 --- a/qiskit/circuit/library/hidden_linear_function.py +++ b/qiskit/circuit/library/hidden_linear_function.py @@ -56,7 +56,7 @@ class HiddenLinearFunction(QuantumCircuit): .. plot:: from qiskit.circuit.library import HiddenLinearFunction - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization A = [[1, 1, 0], [1, 0, 1], [0, 1, 1]] circuit = HiddenLinearFunction(A) _generate_circuit_library_visualization(circuit) diff --git a/qiskit/circuit/library/iqp.py b/qiskit/circuit/library/iqp.py index e18c49d531d0..db65fbe294ad 100644 --- a/qiskit/circuit/library/iqp.py +++ b/qiskit/circuit/library/iqp.py @@ -47,7 +47,7 @@ class IQP(QuantumCircuit): .. plot:: from qiskit.circuit.library import IQP - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization A = [[6, 5, 3], [5, 4, 5], [3, 5, 1]] circuit = IQP(A) _generate_circuit_library_visualization(circuit.decompose()) diff --git a/qiskit/circuit/library/phase_estimation.py b/qiskit/circuit/library/phase_estimation.py index d513bf2ad3af..3311adf46324 100644 --- a/qiskit/circuit/library/phase_estimation.py +++ b/qiskit/circuit/library/phase_estimation.py @@ -74,7 +74,7 @@ def __init__( from qiskit.circuit import QuantumCircuit from qiskit.circuit.library import PhaseEstimation - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization unitary = QuantumCircuit(2) unitary.x(0) unitary.y(1) diff --git a/qiskit/circuit/library/quantum_volume.py b/qiskit/circuit/library/quantum_volume.py index 43340dfff3ce..54a1b30dbec7 100644 --- a/qiskit/circuit/library/quantum_volume.py +++ b/qiskit/circuit/library/quantum_volume.py @@ -43,7 +43,7 @@ class QuantumVolume(QuantumCircuit): .. plot:: from qiskit.circuit.library import QuantumVolume - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = QuantumVolume(5, 6, seed=10, classical_permutation=False) _generate_circuit_library_visualization(circuit.decompose()) diff --git a/qiskit/providers/__init__.py b/qiskit/providers/__init__.py index d7a52193dd15..5b9e8eb0477e 100644 --- a/qiskit/providers/__init__.py +++ b/qiskit/providers/__init__.py @@ -462,13 +462,12 @@ def get_translation_stage_plugin(self): handles submitting the circuits to the backend to be executed and returning a :class:`~qiskit.providers.Job` object. Depending on the type of backend this typically involves serializing the circuit object into the API format used by a -backend. For example, on IBMQ backends from the ``qiskit-ibmq-provider`` +backend. For example, on IBM backends from the ``qiskit-ibm-provider`` package this involves converting from a quantum circuit and options into a -`qobj `__ JSON payload and submitting -that to the IBM Quantum API. Since every backend interface is different (and -in the case of the local simulators serialization may not be needed) it is -expected that the backend's :obj:`~qiskit.providers.BackendV2.run` method will -handle this conversion. +:mod:`.qpy` payload embedded in JSON and submitting that to the IBM Quantum +API. Since every backend interface is different (and in the case of the local +simulators serialization may not be needed) it is expected that the backend's +:obj:`~qiskit.providers.BackendV2.run` method will handle this conversion. An example run method would be something like:: diff --git a/qiskit/providers/fake_provider/__init__.py b/qiskit/providers/fake_provider/__init__.py index 943869114f85..dae7311c68e0 100644 --- a/qiskit/providers/fake_provider/__init__.py +++ b/qiskit/providers/fake_provider/__init__.py @@ -65,21 +65,8 @@ Please note that the simulation is done using a noise model generated from system snapshots obtained in the past (sometimes a few years ago) and the results are not representative of the latest behaviours of the real quantum system which the fake backend is mimicking. If you want to - run noisy simulations to compare with the real quantum system, please follow steps below to - generate a simulator mimics a real quantum system with the latest calibration results. - - .. code-block:: python - - from qiskit.providers.ibmq import IBMQ - from qiskit.providers.aer import AerSimulator - - # get a real backend from a real provider - provider = IBMQ.load_account() - backend = provider.get_backend('ibmq_manila') - - # generate a simulator that mimics the real quantum system with the latest calibration results - backend_sim = AerSimulator.from_backend(backend) - + run noisy simulations to compare with the real quantum system you will need to create a noise + model from the current properties of the backend manually. Fake Providers ============== diff --git a/qiskit/test/__init__.py b/qiskit/test/__init__.py index 362eea2ba0ee..09a9a6c22d0f 100644 --- a/qiskit/test/__init__.py +++ b/qiskit/test/__init__.py @@ -13,6 +13,6 @@ """Functionality and helpers for testing Qiskit.""" from .base import QiskitTestCase -from .decorators import requires_aer_provider, online_test, slow_test +from .decorators import requires_aer_provider, slow_test from .reference_circuits import ReferenceCircuits from .utils import Path diff --git a/qiskit/test/base.py b/qiskit/test/base.py index 19103fec59f2..9bac2a629ff0 100644 --- a/qiskit/test/base.py +++ b/qiskit/test/base.py @@ -193,8 +193,6 @@ def tearDown(self): @classmethod def setUpClass(cls): super().setUpClass() - # Determines if the TestCase is using IBMQ credentials. - cls.using_ibmq_credentials = False # Set logging to file and stdout if the LOG_LEVEL envar is set. cls.log = logging.getLogger(cls.__name__) if os.getenv("LOG_LEVEL"): diff --git a/qiskit/test/decorators.py b/qiskit/test/decorators.py index a15ab9ccc361..c4ca9c67459f 100644 --- a/qiskit/test/decorators.py +++ b/qiskit/test/decorators.py @@ -15,7 +15,6 @@ import collections.abc import functools -import os import socket import sys from typing import Union, Callable, Type, Iterable @@ -99,91 +98,6 @@ def _wrapper(*args, **kwargs): return _wrapper -def _get_credentials(): - """Finds the credentials for a specific test and options. - - Returns: - Credentials: set of credentials - - Raises: - SkipTest: when credentials can't be found - """ - try: - from qiskit.providers.ibmq.credentials import Credentials, discover_credentials - except ImportError as ex: - raise unittest.SkipTest( - "qiskit-ibmq-provider could not be found, " - "and is required for executing online tests. " - 'To install, run "pip install qiskit-ibmq-provider" ' - "or check your installation." - ) from ex - - if os.getenv("IBMQ_TOKEN") and os.getenv("IBMQ_URL"): - return Credentials(os.getenv("IBMQ_TOKEN"), os.getenv("IBMQ_URL")) - elif os.getenv("QISKIT_TESTS_USE_CREDENTIALS_FILE"): - # Attempt to read the standard credentials. - discovered_credentials = discover_credentials() - - if discovered_credentials: - # Decide which credentials to use for testing. - if len(discovered_credentials) > 1: - raise unittest.SkipTest( - "More than 1 credential set found, use: " - "IBMQ_TOKEN and IBMQ_URL env variables to " - "set credentials explicitly" - ) - - # Use the first available credentials. - return list(discovered_credentials.values())[0] - raise unittest.SkipTest( - "No IBMQ credentials found for running the test. This is required for running online tests." - ) - - -def online_test(func): - """Decorator that signals that the test uses the network (and the online API): - - It involves: - * determines if the test should be skipped by checking environment - variables. - * if the `USE_ALTERNATE_ENV_CREDENTIALS` environment variable is - set, it reads the credentials from an alternative set of environment - variables. - * if the test is not skipped, it reads `qe_token` and `qe_url` from - `Qconfig.py`, environment variables or qiskitrc. - * if the test is not skipped, it appends `qe_token` and `qe_url` as - arguments to the test function. - - Args: - func (callable): test function to be decorated. - - Returns: - callable: the decorated function. - """ - - @functools.wraps(func) - def _wrapper(self, *args, **kwargs): - # To avoid checking the connection in each test - global HAS_NET_CONNECTION # pylint: disable=global-statement - - if TEST_OPTIONS["skip_online"]: - raise unittest.SkipTest("Skipping online tests") - - if HAS_NET_CONNECTION is None: - HAS_NET_CONNECTION = _has_connection("qiskit.org", 443) - - if not HAS_NET_CONNECTION: - raise unittest.SkipTest("Test requires internet connection.") - - credentials = _get_credentials() - self.using_ibmq_credentials = credentials.is_ibmq() - kwargs.update({"qe_token": credentials.token, "qe_url": credentials.url}) - - return func(self, *args, **kwargs) - - return _wrapper - - def enforce_subclasses_call( methods: Union[str, Iterable[str]], attr: str = "_enforce_subclasses_call_cache" ) -> Callable[[Type], Type]: diff --git a/qiskit/test/ibmq_mock.py b/qiskit/test/ibmq_mock.py deleted file mode 100644 index 73b30f4f44ec..000000000000 --- a/qiskit/test/ibmq_mock.py +++ /dev/null @@ -1,45 +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. - -"""Mock functions for qiskit.IBMQ.""" - -from unittest.mock import MagicMock -import qiskit -from qiskit.providers import fake_provider as backend_mocks - - -def mock_get_backend(backend): - """Replace qiskit.IBMQ with a mock that returns a single backend. - - Note this will set the value of qiskit.IBMQ to a MagicMock object. It is - intended to be run as part of docstrings with jupyter-example in a hidden - cell so that later examples which rely on ibmq devices so that the docs can - be built without requiring configured credentials. If used outside of this - context be aware that you will have to manually restore qiskit.IBMQ the - value to qiskit.providers.ibmq.IBMQ after you finish using your mock. - - Args: - backend (str): The class name as a string for the fake device to - return from the mock IBMQ object. For example, FakeVigo. - Raises: - NameError: If the specified value of backend - """ - mock_ibmq = MagicMock() - mock_provider = MagicMock() - if not hasattr(backend_mocks, backend): - raise NameError( - "The specified backend name is not a valid mock from qiskit.providers.fake_provider." - ) - fake_backend = getattr(backend_mocks, backend)() - mock_provider.get_backend.return_value = fake_backend - mock_ibmq.get_provider.return_value = mock_provider - qiskit.IBMQ = mock_ibmq diff --git a/qiskit/test/mock/__init__.py b/qiskit/test/mock/__init__.py deleted file mode 100644 index 760a0b1772a9..000000000000 --- a/qiskit/test/mock/__init__.py +++ /dev/null @@ -1,40 +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. - -""" -Utilities for mocking the IBMQ provider, including job responses and backends. - -The module includes dummy provider, backends, and jobs. -The purpose of these classes is to fake backends for testing purposes: -testing local timeouts, arbitrary responses or behavior, etc. - -The mock devices are mainly for testing the compiler. -""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import the desired object directly 'qiskit.providers.fake_provider'.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/__init__.py b/qiskit/test/mock/backends/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/almaden/__init__.py b/qiskit/test/mock/backends/almaden/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/almaden/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/armonk/__init__.py b/qiskit/test/mock/backends/armonk/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/armonk/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/athens/__init__.py b/qiskit/test/mock/backends/athens/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/athens/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/belem/__init__.py b/qiskit/test/mock/backends/belem/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/belem/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/boeblingen/__init__.py b/qiskit/test/mock/backends/boeblingen/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/boeblingen/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/bogota/__init__.py b/qiskit/test/mock/backends/bogota/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/bogota/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/brooklyn/__init__.py b/qiskit/test/mock/backends/brooklyn/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/brooklyn/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/burlington/__init__.py b/qiskit/test/mock/backends/burlington/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/burlington/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/cairo/__init__.py b/qiskit/test/mock/backends/cairo/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/cairo/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/cambridge/__init__.py b/qiskit/test/mock/backends/cambridge/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/cambridge/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/casablanca/__init__.py b/qiskit/test/mock/backends/casablanca/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/casablanca/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/essex/__init__.py b/qiskit/test/mock/backends/essex/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/essex/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/guadalupe/__init__.py b/qiskit/test/mock/backends/guadalupe/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/guadalupe/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/hanoi/__init__.py b/qiskit/test/mock/backends/hanoi/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/hanoi/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/jakarta/__init__.py b/qiskit/test/mock/backends/jakarta/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/jakarta/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/johannesburg/__init__.py b/qiskit/test/mock/backends/johannesburg/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/johannesburg/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/kolkata/__init__.py b/qiskit/test/mock/backends/kolkata/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/kolkata/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/lagos/__init__.py b/qiskit/test/mock/backends/lagos/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/lagos/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/lima/__init__.py b/qiskit/test/mock/backends/lima/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/lima/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/london/__init__.py b/qiskit/test/mock/backends/london/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/london/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/manhattan/__init__.py b/qiskit/test/mock/backends/manhattan/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/manhattan/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/manila/__init__.py b/qiskit/test/mock/backends/manila/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/manila/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/melbourne/__init__.py b/qiskit/test/mock/backends/melbourne/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/melbourne/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/montreal/__init__.py b/qiskit/test/mock/backends/montreal/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/montreal/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/mumbai/__init__.py b/qiskit/test/mock/backends/mumbai/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/mumbai/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/nairobi/__init__.py b/qiskit/test/mock/backends/nairobi/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/nairobi/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/ourense/__init__.py b/qiskit/test/mock/backends/ourense/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/ourense/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/paris/__init__.py b/qiskit/test/mock/backends/paris/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/paris/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/poughkeepsie/__init__.py b/qiskit/test/mock/backends/poughkeepsie/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/poughkeepsie/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/quito/__init__.py b/qiskit/test/mock/backends/quito/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/quito/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/rochester/__init__.py b/qiskit/test/mock/backends/rochester/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/rochester/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/rome/__init__.py b/qiskit/test/mock/backends/rome/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/rome/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/rueschlikon/__init__.py b/qiskit/test/mock/backends/rueschlikon/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/rueschlikon/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/santiago/__init__.py b/qiskit/test/mock/backends/santiago/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/santiago/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/singapore/__init__.py b/qiskit/test/mock/backends/singapore/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/singapore/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/sydney/__init__.py b/qiskit/test/mock/backends/sydney/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/sydney/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/tenerife/__init__.py b/qiskit/test/mock/backends/tenerife/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/tenerife/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/tokyo/__init__.py b/qiskit/test/mock/backends/tokyo/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/tokyo/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/toronto/__init__.py b/qiskit/test/mock/backends/toronto/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/toronto/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/valencia/__init__.py b/qiskit/test/mock/backends/valencia/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/valencia/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/vigo/__init__.py b/qiskit/test/mock/backends/vigo/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/vigo/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/washington/__init__.py b/qiskit/test/mock/backends/washington/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/washington/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/yorktown/__init__.py b/qiskit/test/mock/backends/yorktown/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/yorktown/__init__.py +++ /dev/null @@ -1,32 +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. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/tools/__init__.py b/qiskit/tools/__init__.py index 6bddd22ba889..38354279117a 100644 --- a/qiskit/tools/__init__.py +++ b/qiskit/tools/__init__.py @@ -29,16 +29,9 @@ Monitoring ---------- -A helper module to get IBM backend information and submitted job status. - -.. autofunction:: job_monitor -.. autofunction:: backend_monitor -.. autofunction:: backend_overview - .. automodule:: qiskit.tools.events """ from .parallel import parallel_map -from .monitor import job_monitor, backend_monitor, backend_overview from .events import progressbar diff --git a/qiskit/tools/jupyter/__init__.py b/qiskit/tools/jupyter/__init__.py deleted file mode 100644 index 6bc05d46f743..000000000000 --- a/qiskit/tools/jupyter/__init__.py +++ /dev/null @@ -1,138 +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. - -""" -=========================================== -Jupyter Tools (:mod:`qiskit.tools.jupyter`) -=========================================== - -.. currentmodule:: qiskit.tools.jupyter - -A Collection of Jupyter magic functions and tools -that extend the functionality of Qiskit. - -Overview of all available backends -================================== - -.. code-block:: - - from qiskit.providers.ibmq import IBMQ - import qiskit.tools.jupyter - %matplotlib inline - - IBMQ.load_account() - - %qiskit_backend_overview - - -Detailed information on a single backend -======================================== - -.. code-block:: - - from qiskit.providers.ibmq import IBMQ - import qiskit.tools.jupyter - %matplotlib inline - - IBMQ.load_account() - provider = IBMQ.get_provider(hub='ibm-q') - backend = provider.get_backend('ibmq_vigo') - backend - - -Load Qiskit Job Watcher -======================= - -.. code-block:: - - import qiskit.tools.jupyter - %qiskit_job_watcher - - -HTMLProgressBar -=============== - -.. code-block:: - - import numpy as np - from qiskit.tools.parallel import parallel_map - import qiskit.tools.jupyter - - %qiskit_progress_bar - parallel_map(np.sin, np.linspace(0,10,100)); - - -Qiskit version table -==================== - -.. code-block:: - - import qiskit.tools.jupyter - %qiskit_version_table - - -Qiskit copyright -================ - -.. code-block:: - - import qiskit.tools.jupyter - %qiskit_copyright - -Monospaced output -================= - -.. code-block:: - - import qiskit.tools.jupyter - %monospaced_output - -""" -import warnings - -from IPython import get_ipython -from qiskit.providers.fake_provider import FakeBackend -from qiskit.utils import optionals as _optionals -from .jupyter_magics import ProgressBarMagic, StatusMagic -from .progressbar import HTMLProgressBar -from .version_table import VersionTable -from .copyright import Copyright -from .monospace import MonospacedOutput -from .job_watcher import JobWatcher, JobWatcherMagic - -_IP = get_ipython() -if _IP is not None: - _IP.register_magics(ProgressBarMagic) - _IP.register_magics(VersionTable) - _IP.register_magics(MonospacedOutput) - _IP.register_magics(Copyright) - _IP.register_magics(JobWatcherMagic) - if _optionals.HAS_MATPLOTLIB: - from .backend_overview import BackendOverview - from .backend_monitor import _backend_monitor - - _IP.register_magics(BackendOverview) - if _optionals.HAS_IBMQ: - from qiskit.providers.ibmq import IBMQBackend # pylint: disable=no-name-in-module - - HTML_FORMATTER = _IP.display_formatter.formatters["text/html"] - # Make _backend_monitor the html repr for IBM Q backends - HTML_FORMATTER.for_type(IBMQBackend, _backend_monitor) - HTML_FORMATTER.for_type(FakeBackend, _backend_monitor) - else: - warnings.warn( - "matplotlib can't be found, ensure you have matplotlib and other " - "visualization dependencies installed. You can run " - "'!pip install qiskit-terra[visualization]' to install it from " - "jupyter", - RuntimeWarning, - ) diff --git a/qiskit/tools/jupyter/backend_monitor.py b/qiskit/tools/jupyter/backend_monitor.py deleted file mode 100644 index ecb401521caf..000000000000 --- a/qiskit/tools/jupyter/backend_monitor.py +++ /dev/null @@ -1,588 +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. - -"""A module for monitoring backends.""" - -import types -import math -import datetime -from IPython.display import display -import matplotlib.pyplot as plt -from matplotlib.patches import Circle -import ipywidgets as widgets -from qiskit.exceptions import QiskitError -from qiskit.visualization.gate_map import plot_gate_map, plot_error_map -from qiskit.providers.fake_provider import FakeBackend - -try: - from qiskit.providers.ibmq import IBMQBackend -except ImportError: - pass - -MONTH_NAMES = { - 1: "Jan.", - 2: "Feb.", - 3: "Mar.", - 4: "Apr.", - 5: "May", - 6: "June", - 7: "July", - 8: "Aug.", - 9: "Sept.", - 10: "Oct.", - 11: "Nov.", - 12: "Dec.", -} - - -def _load_jobs_data(self, change): - """Loads backend jobs data""" - if change["new"] == 4 and not self._did_jobs: - self._did_jobs = True - year = widgets.Output( - layout=widgets.Layout(display="flex-inline", align_items="center", min_height="400px") - ) - - month = widgets.Output( - layout=widgets.Layout(display="flex-inline", align_items="center", min_height="400px") - ) - - week = widgets.Output( - layout=widgets.Layout(display="flex-inline", align_items="center", min_height="400px") - ) - - self.children[4].children = [year, month, week] - self.children[4].set_title(0, "Year") - self.children[4].set_title(1, "Month") - self.children[4].set_title(2, "Week") - self.children[4].selected_index = 1 - _build_job_history(self.children[4], self._backend) - - -def _backend_monitor(backend): - """A private function to generate a monitor widget - for a IBMQ backend repr. - - Args: - backend (IBMQBackend | FakeBackend): The backend. - - Raises: - QiskitError: Input is not an IBMQBackend - """ - if not isinstance(backend, IBMQBackend) and not isinstance(backend, FakeBackend): - raise QiskitError("Input variable is not of type IBMQBackend.") - title_style = "style='color:#ffffff;background-color:#000000;padding-top: 1%;" - title_style += "padding-bottom: 1%;padding-left: 1%; margin-top: 0px'" - title_html = f"

{backend.name()}

" - - details = [config_tab(backend)] - - tab_contents = ["Configuration"] - - # Empty jobs tab widget - jobs = widgets.Tab(layout=widgets.Layout(max_height="620px")) - - if not backend.configuration().simulator: - tab_contents.extend(["Qubit Properties", "Multi-Qubit Gates", "Error Map", "Job History"]) - - details.extend([qubits_tab(backend), gates_tab(backend), detailed_map(backend), jobs]) - - tabs = widgets.Tab(layout=widgets.Layout(overflow_y="scroll")) - tabs.children = details - for i in range(len(details)): - tabs.set_title(i, tab_contents[i]) - - # Make backend accessible to tabs widget - tabs._backend = backend - tabs._did_jobs = False - tabs._update = types.MethodType(_load_jobs_data, tabs) - - tabs.observe(tabs._update, names="selected_index") - - title_widget = widgets.HTML(value=title_html, layout=widgets.Layout(margin="0px 0px 0px 0px")) - - bmonitor = widgets.VBox( - [title_widget, tabs], - layout=widgets.Layout( - border="4px solid #000000", max_height="650px", min_height="650px", overflow_y="hidden" - ), - ) - display(bmonitor) - - -def config_tab(backend): - """The backend configuration widget. - - Args: - backend (IBMQBackend | FakeBackend): The backend. - - Returns: - grid: A GridBox widget. - """ - status = backend.status().to_dict() - config = backend.configuration().to_dict() - - config_dict = {**status, **config} - - upper_list = ["n_qubits"] - - if "quantum_volume" in config.keys(): - if config["quantum_volume"]: - upper_list.append("quantum_volume") - - upper_list.extend( - [ - "operational", - "status_msg", - "pending_jobs", - "backend_version", - "basis_gates", - "max_shots", - "max_experiments", - ] - ) - - lower_list = list(set(config_dict.keys()).difference(upper_list)) - # Remove gates because they are in a different tab - lower_list.remove("gates") - # Look for hamiltonian - if "hamiltonian" in lower_list: - htex = config_dict["hamiltonian"]["h_latex"] - config_dict["hamiltonian"] = "$$%s$$" % htex - - upper_str = "" - upper_str += """""" - - footer = "
" - - # Upper HBox widget data - - upper_str += "PropertyValue" - for key in upper_list: - upper_str += "{}{}".format( - key, - config_dict[key], - ) - upper_str += footer - - upper_table = widgets.HTMLMath( - value=upper_str, layout=widgets.Layout(width="100%", grid_area="left") - ) - - image_widget = widgets.Output( - layout=widgets.Layout( - display="flex-inline", - grid_area="right", - padding="10px 10px 10px 10px", - width="auto", - max_height="325px", - align_items="center", - ) - ) - - if not config["simulator"]: - with image_widget: - qubit_size = 24 - if config["n_qubits"] > 20: - qubit_size = 34 - gate_map = plot_gate_map(backend, qubit_size=qubit_size) - display(gate_map) - plt.close(gate_map) - - lower_str = "" - lower_str += """""" - lower_str += "" - for key in lower_list: - if key != "name": - lower_str += f"" - lower_str += footer - - lower_table = widgets.HTMLMath( - value=lower_str, layout=widgets.Layout(width="auto", grid_area="bottom") - ) - - grid = widgets.GridBox( - children=[upper_table, image_widget, lower_table], - layout=widgets.Layout( - grid_template_rows="auto auto", - grid_template_columns="31% 23% 23% 23%", - grid_template_areas=""" - "left right right right" - "bottom bottom bottom bottom" - """, - grid_gap="0px 0px", - ), - ) - - return grid - - -def qubits_tab(backend): - """The qubits properties widget - - Args: - backend (IBMQBackend | FakeBackend): The backend. - - Returns: - VBox: A VBox widget. - """ - props = backend.properties() - - header_html = "
{key}: {value}
" - update_date = props.last_update_date.strftime("%a %d %B %Y at %H:%M %Z") - header_html = header_html.format(key="last_update_date", value=update_date) - - update_date_widget = widgets.HTML(value=header_html) - - qubit_html = "
{key}{config_dict[key]}
" - qubit_html += """""" - - qubit_html += "" - qubit_footer = "
FrequencyT1T2
" - - gate_error_title = "" - - for index, qubit_data in enumerate(props.qubits): - name = "Q%s" % index - gate_data = [gate for gate in props.gates if gate.qubits == [index]] - - cal_data = dict.fromkeys(["T1", "T2", "frequency", "readout_error"], "Unknown") - for nduv in qubit_data: - if nduv.name in cal_data: - cal_data[nduv.name] = str(round(nduv.value, 5)) + " " + nduv.unit - - gate_names = [] - gate_error = [] - for gd in gate_data: - if gd.gate in ["id"]: - continue - try: - gate_error.append(str(round(props.gate_error(gd.gate, index), 5))) - gate_names.append(gd.gate.upper()) - except QiskitError: - pass - - if not gate_error_title: - for gname in gate_names: - gate_error_title += f"{gname}" - qubit_html += gate_error_title + "Readout error" - - qubit_html += f"{name}" - qubit_html += ( - f"{cal_data['frequency']}{cal_data['T1']}{cal_data['T2']}" - ) - for gerror in gate_error: - qubit_html += f"{gerror}" - qubit_html += f"{cal_data['readout_error']}" - - qubit_html += qubit_footer - - qubit_widget = widgets.HTML(value=qubit_html) - - out = widgets.VBox([update_date_widget, qubit_widget]) - - return out - - -def gates_tab(backend): - """The multiple qubit gate error widget. - - Args: - backend (IBMQBackend | FakeBackend): The backend. - - Returns: - VBox: A VBox widget. - """ - props = backend.properties() - - multi_qubit_gates = [g for g in props.gates if len(g.qubits) > 1] - - header_html = "
{key}: {value}
" - header_html = header_html.format(key="last_update_date", value=props.last_update_date) - - update_date_widget = widgets.HTML(value=header_html, layout=widgets.Layout(grid_area="top")) - - gate_html = "" - gate_html += """""" - - gate_html += "" - gate_footer = "
TypeGate error
" - - # Split gates into two columns - left_num = math.ceil(len(multi_qubit_gates) / 3) - mid_num = math.ceil((len(multi_qubit_gates) - left_num) / 2) - - left_table = gate_html - - for qub in range(left_num): - gate = multi_qubit_gates[qub] - qubits = gate.qubits - ttype = gate.gate - error = round(props.gate_error(gate.gate, qubits), 5) - - left_table += "%s" - left_table += "%s%s" - left_table = left_table % (f"{ttype}{qubits[0]}_{qubits[1]}", ttype, error) - left_table += gate_footer - - middle_table = gate_html - - for qub in range(left_num, left_num + mid_num): - gate = multi_qubit_gates[qub] - qubits = gate.qubits - ttype = gate.gate - error = round(props.gate_error(gate.gate, qubits), 5) - - middle_table += "%s" - middle_table += "%s%s" - middle_table = middle_table % (f"{ttype}{qubits[0]}_{qubits[1]}", ttype, error) - middle_table += gate_footer - - right_table = gate_html - - for qub in range(left_num + mid_num, len(multi_qubit_gates)): - gate = multi_qubit_gates[qub] - qubits = gate.qubits - ttype = gate.gate - error = round(props.gate_error(gate.gate, qubits), 5) - - right_table += "%s" - right_table += "%s%s" - right_table = right_table % (f"{ttype}{qubits[0]}_{qubits[1]}", ttype, error) - right_table += gate_footer - - left_table_widget = widgets.HTML(value=left_table, layout=widgets.Layout(grid_area="left")) - middle_table_widget = widgets.HTML( - value=middle_table, layout=widgets.Layout(grid_area="middle") - ) - right_table_widget = widgets.HTML(value=right_table, layout=widgets.Layout(grid_area="right")) - - grid = widgets.GridBox( - children=[update_date_widget, left_table_widget, middle_table_widget, right_table_widget], - layout=widgets.Layout( - grid_template_rows="auto auto", - grid_template_columns="33% 33% 33%", - grid_template_areas=""" - "top top top" - "left middle right" - """, - grid_gap="0px 0px", - ), - ) - - return grid - - -def detailed_map(backend): - """Widget for displaying detailed noise map. - - Args: - backend (IBMQBackend | FakeBackend): The backend. - - Returns: - GridBox: Widget holding noise map images. - """ - error_widget = widgets.Output( - layout=widgets.Layout(display="flex-inline", align_items="center") - ) - with error_widget: - display(plot_error_map(backend, figsize=(11, 9), show_title=False)) - return error_widget - - -def job_history(backend): - """Widget for displaying job history - - Args: - backend (IBMQBackend | FakeBackend): The backend. - - Returns: - Tab: A tab widget for history images. - """ - year = widgets.Output( - layout=widgets.Layout(display="flex-inline", align_items="center", min_height="400px") - ) - - month = widgets.Output( - layout=widgets.Layout(display="flex-inline", align_items="center", min_height="400px") - ) - - week = widgets.Output( - layout=widgets.Layout(display="flex-inline", align_items="center", min_height="400px") - ) - - tabs = widgets.Tab(layout=widgets.Layout(max_height="620px")) - tabs.children = [year, month, week] - tabs.set_title(0, "Year") - tabs.set_title(1, "Month") - tabs.set_title(2, "Week") - tabs.selected_index = 1 - - _build_job_history(tabs, backend) - return tabs - - -def _build_job_history(tabs, backend): - - past_year_date = datetime.datetime.now() - datetime.timedelta(days=365) - date_filter = {"creationDate": {"gt": past_year_date.isoformat()}} - jobs = backend.jobs(limit=None, db_filter=date_filter) - - with tabs.children[0]: - year_plot = plot_job_history(jobs, interval="year") - display(year_plot) - plt.close(year_plot) - - with tabs.children[1]: - month_plot = plot_job_history(jobs, interval="month") - display(month_plot) - plt.close(month_plot) - - with tabs.children[2]: - week_plot = plot_job_history(jobs, interval="week") - display(week_plot) - plt.close(week_plot) - - -def plot_job_history(jobs, interval="year"): - """Plots the job history of the user from the given list of jobs. - - Args: - jobs (list): A list of jobs with type IBMQjob. - interval (str): Interval over which to examine. - - Returns: - fig: A Matplotlib figure instance. - """ - - def get_date(job): - """Returns a datetime object from a IBMQJob instance. - - Args: - job (IBMQJob): A job. - - Returns: - dt: A datetime object. - """ - creation_date = job.creation_date() - - if isinstance(creation_date, datetime.datetime): - return creation_date - - return datetime.datetime.strptime(creation_date, "%Y-%m-%dT%H:%M:%S.%fZ") - - current_time = datetime.datetime.now() - - if interval == "year": - bins = [(current_time - datetime.timedelta(days=k * 365 / 12)) for k in range(12)] - elif interval == "month": - bins = [(current_time - datetime.timedelta(days=k)) for k in range(30)] - elif interval == "week": - bins = [(current_time - datetime.timedelta(days=k)) for k in range(7)] - - binned_jobs = [0] * len(bins) - - if interval == "year": - for job in jobs: - for ind, dat in enumerate(bins): - date = get_date(job) - if date.month == dat.month: - binned_jobs[ind] += 1 - break - else: - continue - else: - for job in jobs: - for ind, dat in enumerate(bins): - date = get_date(job) - if date.day == dat.day and date.month == dat.month: - binned_jobs[ind] += 1 - break - else: - continue - - nz_bins = [] - nz_idx = [] - for ind, val in enumerate(binned_jobs): - if val != 0: - nz_idx.append(ind) - nz_bins.append(val) - - total_jobs = sum(binned_jobs) - - colors = ["#003f5c", "#ffa600", "#374c80", "#ff764a", "#7a5195", "#ef5675", "#bc5090"] - - if interval == "year": - labels = [f"{str(bins[b].year)[2:]}-{MONTH_NAMES[bins[b].month]}" for b in nz_idx] - else: - labels = [f"{MONTH_NAMES[bins[b].month]}-{bins[b].day}" for b in nz_idx] - fig, ax = plt.subplots(1, 1, figsize=(5.5, 5.5)) - ax.pie( - nz_bins[::-1], - labels=labels, - colors=colors, - textprops={"fontsize": 14}, - rotatelabels=True, - counterclock=False, - radius=1, - ) - ax.add_artist(Circle((0, 0), 0.7, color="white", zorder=1)) - ax.text(0, 0, total_jobs, horizontalalignment="center", verticalalignment="center", fontsize=26) - return fig diff --git a/qiskit/tools/jupyter/backend_overview.py b/qiskit/tools/jupyter/backend_overview.py deleted file mode 100644 index f72ad8ba1938..000000000000 --- a/qiskit/tools/jupyter/backend_overview.py +++ /dev/null @@ -1,322 +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. - -"""A module for monitoring backends.""" - -import time -import threading -import types -from IPython.display import display -from IPython.core.magic import line_magic, Magics, magics_class -from IPython.core import magic_arguments -import matplotlib.pyplot as plt -import ipywidgets as widgets -from qiskit.tools.monitor.overview import get_unique_backends -from qiskit.visualization.gate_map import plot_gate_map - - -@magics_class -class BackendOverview(Magics): - """A class of status magic functions.""" - - @line_magic - @magic_arguments.magic_arguments() - @magic_arguments.argument( - "-i", "--interval", type=float, default=60, help="Interval for status check." - ) - def qiskit_backend_overview(self, line=""): - """A Jupyter magic function to monitor backends.""" - args = magic_arguments.parse_argstring(self.qiskit_backend_overview, line) - - unique_hardware_backends = get_unique_backends() - _value = "

Backend Overview

" - backend_title = widgets.HTML(value=_value, layout=widgets.Layout(margin="0px 0px 0px 0px")) - - build_back_widgets = [backend_widget(b) for b in unique_hardware_backends] - - _backends = [] - # Sort backends by operational or not - oper_ord_backends = [] - for n, back in enumerate(unique_hardware_backends): - if back.status().operational: - oper_ord_backends = [build_back_widgets[n]] + oper_ord_backends - _backends = [back] + _backends - else: - oper_ord_backends = oper_ord_backends + [build_back_widgets[n]] - _backends = _backends + [back] - - qubit_label = widgets.Label(value="Num. Qubits") - qv_label = widgets.Label(value="Quantum Vol.") - pend_label = widgets.Label( - value="Pending Jobs", layout=widgets.Layout(margin="5px 0px 0px 0px") - ) - least_label = widgets.Label( - value="Least Busy", layout=widgets.Layout(margin="10px 0px 0px 0px") - ) - oper_label = widgets.Label( - value="Operational", layout=widgets.Layout(margin="5px 0px 0px 0px") - ) - t12_label = widgets.Label( - value="Avg. T1 / T2", layout=widgets.Layout(margin="10px 0px 0px 0px") - ) - cx_label = widgets.Label( - value="Avg. CX Err.", layout=widgets.Layout(margin="8px 0px 0px 0px") - ) - meas_label = widgets.Label( - value="Avg. Meas. Err.", layout=widgets.Layout(margin="8px 0px 0px 0px") - ) - - labels_widget = widgets.VBox( - [ - qubit_label, - qv_label, - pend_label, - oper_label, - least_label, - t12_label, - cx_label, - meas_label, - ], - layout=widgets.Layout(margin="295px 0px 0px 0px", min_width="100px"), - ) - - backend_grid = GridBox_with_thread( - children=oper_ord_backends, - layout=widgets.Layout( - grid_template_columns="250px " * len(unique_hardware_backends), - grid_template_rows="auto", - grid_gap="0px 25px", - ), - ) - - backend_grid._backends = _backends # pylint: disable=attribute-defined-outside-init - backend_grid._update = types.MethodType( # pylint: disable=attribute-defined-outside-init - update_backend_info, backend_grid - ) - - backend_grid._thread = threading.Thread( # pylint: disable=attribute-defined-outside-init - target=backend_grid._update, args=(args.interval,) - ) - backend_grid._thread.start() - - back_box = widgets.HBox([labels_widget, backend_grid]) - - back_monitor = widgets.VBox([backend_title, back_box]) - display(back_monitor) - - -class GridBox_with_thread(widgets.GridBox): # pylint: disable=invalid-name - """A GridBox that will close an attached thread""" - - def __del__(self): - """Object disposal""" - if hasattr(self, "_thread"): - try: - self._thread.do_run = False - self._thread.join() - except Exception: # pylint: disable=broad-except - pass - self.close() - - -def backend_widget(backend): - """Creates a backend widget.""" - config = backend.configuration().to_dict() - props = backend.properties().to_dict() - - name = widgets.HTML(value=f"

{backend.name()}

", layout=widgets.Layout()) - - num_qubits = config["n_qubits"] - - qv_val = "-" - if "quantum_volume" in config.keys(): - if config["quantum_volume"]: - qv_val = config["quantum_volume"] - - qubit_count = widgets.HTML( - value=f"
{num_qubits}
", - layout=widgets.Layout(justify_content="center"), - ) - - qv_value = widgets.HTML( - value=f"
{qv_val}
", - layout=widgets.Layout(justify_content="center"), - ) - - cmap = widgets.Output( - layout=widgets.Layout( - min_width="250px", - max_width="250px", - max_height="250px", - min_height="250px", - justify_content="center", - align_items="center", - margin="0px 0px 0px 0px", - ) - ) - - with cmap: - _cmap_fig = plot_gate_map(backend, plot_directed=False, label_qubits=False) - if _cmap_fig is not None: - display(_cmap_fig) - # Prevents plot from showing up twice. - plt.close(_cmap_fig) - - pending = generate_jobs_pending_widget() - - is_oper = widgets.HTML(value="
", layout=widgets.Layout(justify_content="center")) - - least_busy = widgets.HTML(value="
", layout=widgets.Layout(justify_content="center")) - - t1_units = props["qubits"][0][0]["unit"] - avg_t1 = round(sum(q[0]["value"] for q in props["qubits"]) / num_qubits, 1) - avg_t2 = round(sum(q[1]["value"] for q in props["qubits"]) / num_qubits, 1) - t12_widget = widgets.HTML( - value=f"
{avg_t1} / {avg_t2} {t1_units}
", - layout=widgets.Layout(), - ) - - avg_cx_err = "NA" - if config["coupling_map"]: - sum_cx_err = 0 - num_cx = 0 - for gate in props["gates"]: - if gate["gate"] == "cx": - for param in gate["parameters"]: - if param["name"] == "gate_error": - # Value == 1.0 means gate effectively off - if param["value"] != 1.0: - sum_cx_err += param["value"] - num_cx += 1 - if num_cx > 0: - avg_cx_err = round(sum_cx_err / num_cx, 4) - - cx_widget = widgets.HTML(value=f"
{avg_cx_err}
", layout=widgets.Layout()) - - avg_meas_err = 0 - for qub in props["qubits"]: - for item in qub: - if item["name"] == "readout_error": - avg_meas_err += item["value"] - avg_meas_err = round(avg_meas_err / num_qubits, 4) - meas_widget = widgets.HTML(value=f"
{avg_meas_err}
", layout=widgets.Layout()) - - out = widgets.VBox( - [ - name, - cmap, - qubit_count, - qv_value, - pending, - is_oper, - least_busy, - t12_widget, - cx_widget, - meas_widget, - ], - layout=widgets.Layout(display="inline-flex", flex_flow="column", align_items="center"), - ) - - out._is_alive = True - return out - - -def update_backend_info(self, interval=60): - """Updates the monitor info - Called from another thread. - """ - my_thread = threading.current_thread() - current_interval = 0 - started = False - all_dead = False - stati = [None] * len(self._backends) - while getattr(my_thread, "do_run", True) and not all_dead: - if current_interval == interval or started is False: - for ind, back in enumerate(self._backends): - _value = self.children[ind].children[2].value - _head = _value.split("")[0] - try: - _status = back.status() - stati[ind] = _status - except Exception: # pylint: disable=broad-except - self.children[ind].children[2].value = _value.replace( - _head, "
" - ) - self.children[ind]._is_alive = False - else: - self.children[ind]._is_alive = True - self.children[ind].children[2].value = _value.replace(_head, "
") - - idx = list(range(len(self._backends))) - pending = [s.pending_jobs for s in stati] - _, least_idx = zip(*sorted(zip(pending, idx))) - - # Make sure least pending is operational - for ind in least_idx: - if stati[ind].operational: - least_pending_idx = ind - break - - for var in idx: - if var == least_pending_idx: - self.children[var].children[6].value = "
True
" - else: - self.children[var].children[6].value = "
False
" - - self.children[var].children[4].children[1].max = max( - self.children[var].children[4].children[1].max, pending[var] + 10 - ) - self.children[var].children[4].children[1].value = pending[var] - if stati[var].operational: - self.children[var].children[5].value = "
True
" - else: - self.children[var].children[5].value = "
False
" - - started = True - current_interval = 0 - time.sleep(1) - all_dead = not any(wid._is_alive for wid in self.children) - current_interval += 1 - - -def generate_jobs_pending_widget(): - """Generates a jobs_pending progress bar widget.""" - pbar = widgets.IntProgress( - value=0, - min=0, - max=50, - description="", - orientation="horizontal", - layout=widgets.Layout(max_width="180px"), - ) - pbar.style.bar_color = "#71cddd" - - pbar_current = widgets.Label(value=str(pbar.value), layout=widgets.Layout(min_width="auto")) - pbar_max = widgets.Label(value=str(pbar.max), layout=widgets.Layout(min_width="auto")) - - def _on_max_change(change): - pbar_max.value = str(change["new"]) - - def _on_val_change(change): - pbar_current.value = str(change["new"]) - - pbar.observe(_on_max_change, names="max") - pbar.observe(_on_val_change, names="value") - - jobs_widget = widgets.HBox( - [pbar_current, pbar, pbar_max], - layout=widgets.Layout(max_width="250px", min_width="250px", justify_content="center"), - ) - - return jobs_widget diff --git a/qiskit/tools/jupyter/copyright.py b/qiskit/tools/jupyter/copyright.py deleted file mode 100644 index d759a6ab5f4f..000000000000 --- a/qiskit/tools/jupyter/copyright.py +++ /dev/null @@ -1,42 +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. -# pylint: disable=unused-argument - -"""A module for monitoring backends.""" - -import datetime -from IPython.display import HTML, display -from IPython.core.magic import line_magic, Magics, magics_class - - -@magics_class -class Copyright(Magics): - """A class of status magic functions.""" - - @line_magic - def qiskit_copyright(self, line="", cell=None): - """A Jupyter magic function return qiskit copyright""" - now = datetime.datetime.now() - - html = "
" - html += "

This code is a part of Qiskit

" - html += "

© Copyright IBM 2017, %s.

" % now.year - html += "

This code is licensed under the Apache License, Version 2.0. You may
" - html += "obtain a copy of this license in the LICENSE.txt file in the root directory
" - html += "of this source tree or at http://www.apache.org/licenses/LICENSE-2.0." - - html += "

Any modifications or derivative works of this code must retain this
" - html += "copyright notice, and modified files need to carry a notice indicating
" - html += "that they have been altered from the originals.

" - html += "
" - return display(HTML(html)) diff --git a/qiskit/tools/jupyter/job_watcher.py b/qiskit/tools/jupyter/job_watcher.py deleted file mode 100644 index 7ad6f7014569..000000000000 --- a/qiskit/tools/jupyter/job_watcher.py +++ /dev/null @@ -1,167 +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. -# pylint: disable=unused-argument - -"""A module for the job watcher""" - -from IPython.core.magic import line_magic, Magics, magics_class -from qiskit.tools.events.pubsub import Subscriber -from qiskit.utils import optionals as _optionals - -from .job_widgets import build_job_viewer, make_clear_button, make_labels, create_job_widget -from .watcher_monitor import _job_monitor - - -@_optionals.HAS_IBMQ.require_in_instance -class JobWatcher(Subscriber): - """An IBM Q job watcher.""" - - def __init__(self): - super().__init__() - self.jobs = [] - self._init_subscriber() - self.job_viewer = None - self._clear_button = make_clear_button(self) - self._labels = make_labels() - self.refresh_viewer() - - def refresh_viewer(self): - """Refreshes the job viewer.""" - if self.job_viewer is not None: - self.job_viewer.children[0].children = [self._clear_button, self._labels] + list( - reversed(self.jobs) - ) - - def stop_viewer(self): - """Stops the job viewer.""" - if self.job_viewer: - self.job_viewer.close() - self.job_viewer = None - - def start_viewer(self): - """Starts the job viewer""" - self.job_viewer = build_job_viewer() - self.refresh_viewer() - - def update_single_job(self, update_info): - """Update a single job instance - - Args: - update_info (tuple): Updated job info. - """ - job_id = update_info[0] - found_job = False - ind = None - for idx, job in enumerate(self.jobs): - if job.job_id == job_id: - found_job = True - ind = idx - break - if found_job: - job_wid = self.jobs[ind] - # update status - if update_info[1] == "DONE": - stat = f"{update_info[1]}" - elif update_info[1] == "ERROR": - stat = f"{update_info[1]}" - elif update_info[1] == "CANCELLED": - stat = f"{update_info[1]}" - else: - stat = update_info[1] - job_wid.children[3].value = stat - # update queue - if update_info[2] == 0: - queue = "-" - else: - queue = str(update_info[2]) - job_wid.children[4].value = queue - # update msg - job_wid.children[5].value = update_info[3] - - def cancel_job(self, job_id): - """Cancels a job in the watcher - - Args: - job_id (str): Job id to remove. - - Raises: - Exception: Job id not found. - """ - from qiskit.providers.ibmq.job.exceptions import ( # pylint: disable=no-name-in-module - IBMQJobApiError, - ) - - do_pop = False - ind = None - for idx, job in enumerate(self.jobs): - if job.job_id == job_id: - do_pop = True - ind = idx - break - if not do_pop: - raise Exception("job_id not found") - if "CANCELLED" not in self.jobs[ind].children[3].value: - try: - self.jobs[ind].job.cancel() - status = self.jobs[ind].job.status() - except IBMQJobApiError: - pass - else: - self.update_single_job((self.jobs[ind].job_id, status.name, 0, status.value)) - - def clear_done(self): - """Clears the done jobs from the list.""" - _temp_jobs = [] - do_refresh = False - for job in self.jobs: - job_str = job.children[3].value - if not (("DONE" in job_str) or ("CANCELLED" in job_str) or ("ERROR" in job_str)): - _temp_jobs.append(job) - else: - job.close() - do_refresh = True - if do_refresh: - self.jobs = _temp_jobs - self.refresh_viewer() - - def _init_subscriber(self): - def _add_job(job): - status = job.status() - job_widget = create_job_widget( - self, job, job.backend(), status.name, job.queue_position(), status.value - ) - self.jobs.append(job_widget) - self.refresh_viewer() - _job_monitor(job, status, self) - - self.subscribe("ibmq.job.start", _add_job) - - -@magics_class -class JobWatcherMagic(Magics): - """A class for enabling/disabling the job watcher.""" - - @line_magic - def qiskit_job_watcher(self, line="", cell=None): - """A Jupyter magic function to enable job watcher.""" - _JOB_WATCHER.stop_viewer() - _JOB_WATCHER.start_viewer() - - @line_magic - def qiskit_disable_job_watcher(self, line="", cell=None): - """A Jupyter magic function to disable job watcher.""" - _JOB_WATCHER.stop_viewer() - - -if _optionals.HAS_IBMQ: - # The Jupyter job watcher instance - _JOB_WATCHER = JobWatcher() diff --git a/qiskit/tools/jupyter/job_widgets.py b/qiskit/tools/jupyter/job_widgets.py deleted file mode 100644 index f854d5ee1031..000000000000 --- a/qiskit/tools/jupyter/job_widgets.py +++ /dev/null @@ -1,160 +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. - -"""A module of widgets for job tracking""" - -import ipywidgets as widgets -from IPython.display import display, Javascript - - -def make_clear_button(watcher): - """Makes the clear button - - Args: - watcher (widget): The watcher widget instance. - - Returns: - widget: The clear button widget. - """ - clear = widgets.Button( - description="Clear", - button_style="primary", - layout=widgets.Layout(width="70px", grid_area="right", padding="0px 0px 0px 0px"), - ) - - def on_clear_button_clicked(_): - watcher.clear_done() - - clear.on_click(on_clear_button_clicked) - - clear_button = widgets.GridBox( - children=[clear], - layout=widgets.Layout( - width="100%", - grid_template_columns="20% 20% 20% 20% 20%", - grid_template_areas=""" - ". . . . right " - """, - ), - ) - return clear_button - - -def make_labels(): - """Makes the labels widget. - - Returns: - widget: The labels widget. - """ - labels0 = widgets.HTML(value="
Job ID
", layout=widgets.Layout(width="190px")) - labels1 = widgets.HTML(value="
Backend
", layout=widgets.Layout(width="145px")) - labels2 = widgets.HTML(value="
Status
", layout=widgets.Layout(width="95px")) - labels3 = widgets.HTML(value="
Queue
", layout=widgets.Layout(width="70px")) - labels4 = widgets.HTML(value="
Message
") - - labels = widgets.HBox( - children=[labels0, labels1, labels2, labels3, labels4], - layout=widgets.Layout(width="600px", margin="0px 0px 0px 37px"), - ) - return labels - - -def create_job_widget(watcher, job, backend, status="", queue_pos=None, msg=""): - """Creates a widget corresponding to a particular job instance. - - Args: - watcher (widget): The job watcher instance. - job (IBMQJob): The job. - backend (str): The backend the job is running on. - status (str): The job status. - queue_pos (int): Queue position, if any. - msg (str): Job message, if any. - - Returns: - widget: The job widget - """ - job_id = job.job_id() - - id_label = widgets.HTML(value=f"{job_id}", layout=widgets.Layout(width="190px")) - backend_label = widgets.HTML(value=f"{backend}", layout=widgets.Layout(width="145px")) - status_label = widgets.HTML(value=f"{status}", layout=widgets.Layout(width="95px")) - if queue_pos is None: - queue_pos = "-" - else: - queue_pos = str(queue_pos) - queue_label = widgets.HTML(value=f"{queue_pos}", layout=widgets.Layout(width="70px")) - - msg_label = widgets.HTML( - value=f"

{msg}

", - layout=widgets.Layout(overflow_x="scroll"), - ) - - close_button = widgets.Button( - button_style="", icon="close", layout=widgets.Layout(width="32px", margin="0px 5px 0px 0px") - ) - close_button.style.button_color = "white" - - def cancel_on_click(_): - watcher.cancel_job(job_id) - - close_button.on_click(cancel_on_click) - - job_grid = widgets.HBox( - children=[close_button, id_label, backend_label, status_label, queue_label, msg_label], - layout=widgets.Layout(min_width="700px", max_width="700px"), - ) - job_grid.job_id = job_id - job_grid.job = job - return job_grid - - -def build_job_viewer(): - """Builds the job viewer widget - - Returns: - widget: Job viewer. - """ - acc = widgets.Accordion( - children=[widgets.VBox(layout=widgets.Layout(max_width="710px", min_width="710px"))], - layout=widgets.Layout( - width="auto", - max_width="750px", - max_height="500px", - overflow_y="scroll", - overflow_x="hidden", - ), - ) - acc.set_title(0, "IBMQ Jobs") - acc.selected_index = None - acc.layout.visibility = "hidden" - display(acc) - acc._dom_classes = ["job_widget"] - display( - Javascript( - """ - const isLab = window['Jupyter'] === undefined; - const notebook = document.querySelector( isLab ? 'div.jp-Notebook' : '#site'); - const jobWidget = document.querySelector('div.job_widget'); - notebook.prepend(jobWidget); - jobWidget.style.zIndex = '999'; - jobWidget.style.position = isLab ? 'sticky' : 'fixed'; - jobWidget.style.boxShadow = '5px 5px 5px -3px black'; - jobWidget.style.opacity = '0.95'; - if (isLab) { - jobWidget.style.top = '0'; - jobWidget.style.left = '0'; - } - """ - ) - ) - acc.layout.visibility = "visible" - return acc diff --git a/qiskit/tools/jupyter/jupyter_magics.py b/qiskit/tools/jupyter/jupyter_magics.py deleted file mode 100644 index 5fc9d92b7bb6..000000000000 --- a/qiskit/tools/jupyter/jupyter_magics.py +++ /dev/null @@ -1,190 +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. - -"""A module of magic functions""" - -import time -import threading -from IPython import get_ipython -from IPython.display import display -from IPython.core import magic_arguments -from IPython.core.magic import cell_magic, line_magic, Magics, magics_class, register_line_magic - -from qiskit.utils import optionals as _optionals -from qiskit.utils.deprecation import deprecate_func -import qiskit -from qiskit.tools.events.progressbar import TextProgressBar -from .progressbar import HTMLProgressBar -from .library import circuit_library_widget - - -def _html_checker(job_var, interval, status, header, _interval_set=False): - """Internal function that updates the status - of a HTML job monitor. - - Args: - job_var (BaseJob): The job to keep track of. - interval (int): The status check interval - status (widget): HTML ipywidget for output to screen - header (str): String representing HTML code for status. - _interval_set (bool): Was interval set by user? - """ - job_status = job_var.status() - job_status_name = job_status.name - job_status_msg = job_status.value - status.value = header % (job_status_msg) - while job_status_name not in ["DONE", "CANCELLED"]: - time.sleep(interval) - job_status = job_var.status() - job_status_name = job_status.name - job_status_msg = job_status.value - if job_status_name == "ERROR": - break - if job_status_name == "QUEUED": - job_status_msg += " (%s)" % job_var.queue_position() - if job_var.queue_position() is None: - interval = 2 - elif not _interval_set: - interval = max(job_var.queue_position(), 2) - else: - if not _interval_set: - interval = 2 - status.value = header % (job_status_msg) - - status.value = header % (job_status_msg) - - -@magics_class -class StatusMagic(Magics): - """A class of status magic functions.""" - - @cell_magic - @magic_arguments.magic_arguments() - @magic_arguments.argument( - "-i", "--interval", type=float, default=None, help="Interval for status check." - ) - @_optionals.HAS_IPYWIDGETS.require_in_call - def qiskit_job_status(self, line="", cell=None): - """A Jupyter magic function to check the status of a Qiskit job instance.""" - import ipywidgets as widgets - - args = magic_arguments.parse_argstring(self.qiskit_job_status, line) - - if args.interval is None: - args.interval = 2 - _interval_set = False - else: - _interval_set = True - - # Split cell lines to get LHS variables - cell_lines = cell.split("\n") - line_vars = [] - for cline in cell_lines: - if "=" in cline and "==" not in cline: - line_vars.append(cline.replace(" ", "").split("=")[0]) - elif ".append(" in cline: - line_vars.append(cline.replace(" ", "").split("(")[0]) - - # Execute the cell - self.shell.ex(cell) - - # Look for all vars that are BaseJob instances - jobs = [] - for var in line_vars: - iter_var = False - if "#" not in var: - # The line var is a list or array, but we cannot parse the index - # so just iterate over the whole array for jobs. - if "[" in var: - var = var.split("[")[0] - iter_var = True - elif ".append" in var: - var = var.split(".append")[0] - iter_var = True - - if iter_var: - for item in self.shell.user_ns[var]: - if isinstance(item, qiskit.providers.job.Job): - jobs.append(item) - else: - if isinstance(self.shell.user_ns[var], qiskit.providers.job.Job): - jobs.append(self.shell.user_ns[var]) - - # Must have one job class - if not any(jobs): - raise Exception("Cell must contain at least one variable of BaseJob type.") - - # List index of job if checking status of multiple jobs. - multi_job = False - if len(jobs) > 1: - multi_job = True - - job_checkers = [] - # Loop over every BaseJob that was found. - for idx, job_var in enumerate(jobs): - style = "font-size:16px;" - if multi_job: - idx_str = "[%s]" % idx - else: - idx_str = "" - header = f"

Job Status {idx_str}: %s

" - status = widgets.HTML(value=header % job_var.status().value) - - thread = threading.Thread( - target=_html_checker, args=(job_var, args.interval, status, header, _interval_set) - ) - thread.start() - job_checkers.append(status) - - # Group all HTML widgets into single vertical layout - box = widgets.VBox(job_checkers) - display(box) - - -@magics_class -class ProgressBarMagic(Magics): - """A class of progress bar magic functions.""" - - @line_magic - @magic_arguments.magic_arguments() - @magic_arguments.argument( - "-t", "--type", type=str, default="html", help="Type of progress bar, 'html' or 'text'." - ) - def qiskit_progress_bar(self, line="", cell=None): # pylint: disable=unused-argument - """A Jupyter magic function to generate progressbar.""" - args = magic_arguments.parse_argstring(self.qiskit_progress_bar, line) - if args.type == "html": - pbar = HTMLProgressBar() - elif args.type == "text": - pbar = TextProgressBar() - else: - raise qiskit.QiskitError("Invalid progress bar type.") - - return pbar - - -if _optionals.HAS_MATPLOTLIB and get_ipython(): - - @register_line_magic - @deprecate_func( - since="0.25.0", - additional_msg="This was originally only for internal documentation and is no longer used.", - ) - def circuit_library_info(circuit: qiskit.QuantumCircuit) -> None: - """Displays library information for a quantum circuit. - - Args: - circuit: Input quantum circuit. - """ - shell = get_ipython() - circ = shell.ev(circuit) - circuit_library_widget(circ) diff --git a/qiskit/tools/jupyter/library.py b/qiskit/tools/jupyter/library.py deleted file mode 100644 index 57a27ece8cdc..000000000000 --- a/qiskit/tools/jupyter/library.py +++ /dev/null @@ -1,189 +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. - -# pylint: disable=invalid-name,no-name-in-module,ungrouped-imports - -"""A circuit library widget module""" - -import ipywidgets as wid -from IPython.display import display -from qiskit import QuantumCircuit -from qiskit.utils import optionals as _optionals -from qiskit.utils.deprecation import deprecate_func - - -@_optionals.HAS_MATPLOTLIB.require_in_call -def _generate_circuit_library_visualization(circuit: QuantumCircuit): - import matplotlib.pyplot as plt - - circuit = circuit.decompose() - ops = circuit.count_ops() - num_nl = circuit.num_nonlocal_gates() - _fig, (ax0, ax1) = plt.subplots(2, 1) - circuit.draw("mpl", ax=ax0) - ax1.axis("off") - ax1.grid(visible=None) - ax1.table( - [[circuit.name], [circuit.width()], [circuit.depth()], [sum(ops.values())], [num_nl]], - rowLabels=["Circuit Name", "Width", "Depth", "Total Gates", "Non-local Gates"], - ) - plt.tight_layout() - plt.show() - - -@deprecate_func( - since="0.25.0", - additional_msg="This is unused by Qiskit, and no replacement will be publicly provided.", - package_name="qiskit-terra", -) -def circuit_data_table(circuit: QuantumCircuit) -> wid.HTML: - """Create a HTML table widget for a given quantum circuit. - - Args: - circuit: Input quantum circuit. - - Returns: - Output widget. - """ - - circuit = circuit.decompose() - ops = circuit.count_ops() - num_nl = circuit.num_nonlocal_gates() - - html = "" - html += """""" - html += f"" - html += f"" - html += f"" - html += f"" - html += f"" - html += "
{circuit.name}
Width{circuit.width()}
Depth{circuit.depth()}
Total Gates{sum(ops.values())}
Non-local Gates{num_nl}
" - - out_wid = wid.HTML(html) - return out_wid - - -head_style = ( - "font-family: IBM Plex Sans, Arial, Helvetica, sans-serif;" - " font-size: 20px; font-weight: medium;" -) - -property_label = wid.HTML( - f"

Circuit Properties

", - layout=wid.Layout(margin="0px 0px 10px 0px"), -) - - -@deprecate_func( - since="0.25.0", - additional_msg="This is unused by Qiskit, and no replacement will be publicly provided.", - package_name="qiskit-terra", -) -def properties_widget(circuit: QuantumCircuit) -> wid.VBox: - """Create a HTML table widget with header for a given quantum circuit. - - Args: - circuit: Input quantum circuit. - - Returns: - Output widget. - """ - properties = wid.VBox( - children=[property_label, circuit_data_table(circuit)], - layout=wid.Layout(width="40%", height="auto"), - ) - return properties - - -@deprecate_func( - since="0.25.0", - additional_msg="This is unused by Qiskit, and no replacement will be publicly provided.", - package_name="qiskit-terra", -) -def circuit_diagram_widget() -> wid.Box: - """Create a circuit diagram widget. - - Returns: - Output widget. - """ - # The max circuit height corresponds to a 20Q circuit with flat - # classical register. - top_out = wid.Output( - layout=wid.Layout( - width="100%", - height="auto", - max_height="1000px", - overflow="hidden scroll", - ) - ) - - top = wid.Box(children=[top_out], layout=wid.Layout(width="100%", height="auto")) - - return top - - -@deprecate_func( - since="0.25.0", - additional_msg="This is unused by Qiskit, and no replacement will be publicly provided.", - package_name="qiskit-terra", -) -def circuit_library_widget(circuit: QuantumCircuit) -> None: - """Create a circuit library widget. - - Args: - circuit: Input quantum circuit. - """ - sep_length = str(min(20, 495)) - - # The separator widget - sep = wid.HTML( - f"
", - layout=wid.Layout(height="auto", max_height="495px", margin="40px 0px 0px 20px"), - ) - bottom = wid.HBox( - children=[properties_widget(circuit), sep], - layout=wid.Layout(max_height="550px", height="auto"), - ) - - top = circuit_diagram_widget() - - with top.children[0]: - display(circuit.decompose().draw(output="mpl")) - - display(wid.VBox(children=[top, bottom], layout=wid.Layout(width="100%", height="auto"))) diff --git a/qiskit/tools/jupyter/monospace.py b/qiskit/tools/jupyter/monospace.py deleted file mode 100644 index 0ff0b99c8bfb..000000000000 --- a/qiskit/tools/jupyter/monospace.py +++ /dev/null @@ -1,29 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 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. -# pylint: disable=unused-argument - -"""A Jupyter magic to choose a real monospaced fonts, if available.""" - -from IPython.display import HTML, display -from IPython.core.magic import line_magic, Magics, magics_class - - -@magics_class -class MonospacedOutput(Magics): - """A class for setting "Courier New" for output code.""" - - @line_magic - def monospaced_output(self, line="", cell=None): - """A Jupyter magic function to set "Courier New" for output code.""" - html = """""" - display(HTML(html)) diff --git a/qiskit/tools/jupyter/progressbar.py b/qiskit/tools/jupyter/progressbar.py deleted file mode 100644 index 06531935d2de..000000000000 --- a/qiskit/tools/jupyter/progressbar.py +++ /dev/null @@ -1,122 +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. - -# This file is part of QuTiP: Quantum Toolbox in Python. -# -# Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names -# of its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -"""Progress bars module""" - -import time - -from qiskit.tools.events.progressbar import BaseProgressBar -from qiskit.utils import optionals as _optionals - - -@_optionals.HAS_IPYWIDGETS.require_in_instance -class HTMLProgressBar(BaseProgressBar): - """ - A simple HTML progress bar for using in IPython notebooks. - """ - - def __init__(self): - super().__init__() - self.progress_bar = None - self.label = None - self.box = None - self._init_subscriber() - - def _init_subscriber(self): - def _initialize_progress_bar(num_tasks): - """When an event of compilation starts, this function will be called, and - will initialize the progress bar. - - Args: - num_tasks: Number of compilation tasks the progress bar will track - """ - self.start(num_tasks) - - self.subscribe("terra.parallel.start", _initialize_progress_bar) - - def _update_progress_bar(progress): - """When an event of compilation completes, this function will be called, and - will update the progress bar indication. - - Args: - progress: Number of tasks completed - """ - self.update(progress) - - self.subscribe("terra.parallel.done", _update_progress_bar) - - def _finish_progress_bar(): - """When an event of compilation finishes (meaning that there's no more circuits to - compile), this function will be called, unsubscribing from all events and - finishing the progress bar.""" - self.unsubscribe("terra.parallel.start", _initialize_progress_bar) - self.unsubscribe("terra.parallel.done", _update_progress_bar) - self.unsubscribe("terra.parallel.finish", _finish_progress_bar) - self.finished() - - self.subscribe("terra.parallel.finish", _finish_progress_bar) - - def start(self, iterations): - import ipywidgets as widgets - from IPython.display import display - - self.touched = True - self.iter = int(iterations) - self.t_start = time.time() - self.progress_bar = widgets.IntProgress(min=0, max=self.iter, value=0) - self.progress_bar.bar_style = "info" - self.label = widgets.HTML() - self.box = widgets.VBox(children=[self.label, self.progress_bar]) - display(self.box) - - def update(self, n): - self.progress_bar.value += 1 - lbl = "Completed %s/%s: Est. remaining time: %s." - self.label.value = lbl % (n, self.iter, self.time_remaining_est(n)) - - def finished(self): - self.t_done = time.time() - self.progress_bar.bar_style = "success" - self.label.value = "Elapsed time: %s" % self.time_elapsed() diff --git a/qiskit/tools/jupyter/version_table.py b/qiskit/tools/jupyter/version_table.py deleted file mode 100644 index 9d54ed080946..000000000000 --- a/qiskit/tools/jupyter/version_table.py +++ /dev/null @@ -1,67 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2021. -# -# 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=unused-argument - -"""A module for monitoring backends.""" - -import time -from sys import modules -from IPython.display import HTML, display -from IPython.core.magic import line_magic, Magics, magics_class -import qiskit -from qiskit.utils import local_hardware_info - - -@magics_class -class VersionTable(Magics): - """A class of status magic functions.""" - - @line_magic - def qiskit_version_table(self, line="", cell=None): - """ - Print an HTML-formatted table with version numbers for Qiskit and its - dependencies. This should make it possible to reproduce the environment - and the calculation later on. - """ - html = "

Version Information

" - html += "" - html += "" - - packages = {"qiskit": qiskit.__version__} - qiskit_modules = {module.split(".")[0] for module in modules.keys() if "qiskit" in module} - - for qiskit_module in qiskit_modules: - packages[qiskit_module] = getattr(modules[qiskit_module], "__version__", None) - - for name, version in packages.items(): - if version: - html += f"" - - html += "" - - local_hw_info = local_hardware_info() - sys_info = [ - ("Python version", local_hw_info["python_version"]), - ("Python compiler", local_hw_info["python_compiler"]), - ("Python build", local_hw_info["python_build"]), - ("OS", "%s" % local_hw_info["os"]), - ("CPUs", "%s" % local_hw_info["cpus"]), - ("Memory (Gb)", "%s" % local_hw_info["memory"]), - ] - - for name, version in sys_info: - html += f"" - - html += "" % time.strftime("%a %b %d %H:%M:%S %Y %Z") - html += "
SoftwareVersion
{name}{version}
System information
{name}{version}
%s
" - - return display(HTML(html)) diff --git a/qiskit/tools/jupyter/watcher_monitor.py b/qiskit/tools/jupyter/watcher_monitor.py deleted file mode 100644 index 7d12dfc9bd03..000000000000 --- a/qiskit/tools/jupyter/watcher_monitor.py +++ /dev/null @@ -1,74 +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. - -"""A module of widgets for job monitoring""" -import sys -import time -import threading - - -def _job_monitor(job, status, watcher): - """Monitor the status of a IBMQJob instance. - - Args: - job (BaseJob): Job to monitor. - status (Enum): Job status. - watcher (JobWatcher): Job watcher instance - """ - thread = threading.Thread(target=_job_checker, args=(job, status, watcher)) - thread.start() - - -def _job_checker(job, status, watcher): - """A simple job status checker - - Args: - job (BaseJob): The job to check. - status (Enum): Job status. - watcher (JobWatcher): Job watcher instance - - """ - prev_status_name = None - prev_queue_pos = None - interval = 2 - exception_count = 0 - - while status.name not in ["DONE", "CANCELLED", "ERROR"]: - time.sleep(interval) - try: - status = job.status() - exception_count = 0 - - if status.name == "QUEUED": - queue_pos = job.queue_position() - if queue_pos != prev_queue_pos: - - update_info = (job.job_id(), status.name, queue_pos, status.value) - - watcher.update_single_job(update_info) - interval = max(queue_pos, 2) - prev_queue_pos = queue_pos - - elif status.name != prev_status_name: - update_info = (job.job_id(), status.name, 0, status.value) - - watcher.update_single_job(update_info) - interval = 2 - prev_status_name = status.name - - # pylint: disable=broad-except - except Exception: - exception_count += 1 - if exception_count == 5: - update_info = (job.job_id(), "NA", 0, "Could not query job.") - watcher.update_single_job(update_info) - sys.exit() diff --git a/qiskit/tools/monitor/__init__.py b/qiskit/tools/monitor/__init__.py deleted file mode 100644 index cd0f22b90e48..000000000000 --- a/qiskit/tools/monitor/__init__.py +++ /dev/null @@ -1,16 +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. - -"""A module for monitoring jobs, backends, etc.""" - -from .job_monitor import job_monitor -from .overview import backend_monitor, backend_overview diff --git a/qiskit/tools/monitor/job_monitor.py b/qiskit/tools/monitor/job_monitor.py deleted file mode 100644 index 3dabf5e2296b..000000000000 --- a/qiskit/tools/monitor/job_monitor.py +++ /dev/null @@ -1,107 +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. - - -"""A module for monitoring various qiskit functionality""" - -import sys -import time - - -def _text_checker( - job, interval, _interval_set=False, quiet=False, output=sys.stdout, line_discipline="\r" -): - """A text-based job status checker - - Args: - job (BaseJob): The job to check. - interval (int): The interval at which to check. - _interval_set (bool): Was interval time set by user? - quiet (bool): If True, do not print status messages. - output (file): The file like object to write status messages to. - By default this is sys.stdout. - line_discipline (string): character emitted at start of a line of job monitor output, - This defaults to \\r. - - """ - status = job.status() - msg = status.value - prev_msg = msg - msg_len = len(msg) - - if not quiet: - print("{}{}: {}".format(line_discipline, "Job Status", msg), end="", file=output) - while status.name not in ["DONE", "CANCELLED", "ERROR"]: - time.sleep(interval) - status = job.status() - msg = status.value - - if status.name == "QUEUED": - msg += " (%s)" % job.queue_position() - if job.queue_position() is None: - interval = 2 - elif not _interval_set: - interval = max(job.queue_position(), 2) - else: - if not _interval_set: - interval = 2 - - # Adjust length of message so there are no artifacts - if len(msg) < msg_len: - msg += " " * (msg_len - len(msg)) - elif len(msg) > msg_len: - msg_len = len(msg) - - if msg != prev_msg and not quiet: - print("{}{}: {}".format(line_discipline, "Job Status", msg), end="", file=output) - prev_msg = msg - if not quiet: - print("", file=output) - - -def job_monitor(job, interval=None, quiet=False, output=sys.stdout, line_discipline="\r"): - """Monitor the status of a IBMQJob instance. - - Args: - job (BaseJob): Job to monitor. - interval (int): Time interval between status queries. - quiet (bool): If True, do not print status messages. - output (file): The file like object to write status messages to. - By default this is sys.stdout. - line_discipline (string): character emitted at start of a line of job monitor output, - This defaults to \\r. - - Examples: - - .. code-block:: python - - from qiskit import BasicAer, transpile - from qiskit.circuit import QuantumCircuit - from qiskit.tools.monitor import job_monitor - sim_backend = BasicAer.get_backend("qasm_simulator") - qc = QuantumCircuit(2, 2) - qc.h(0) - qc.cx(0, 1) - qc.measure_all() - tqc = transpile(qc, sim_backend) - job_sim = sim_backend.run(tqc) - job_monitor(job_sim) - """ - if interval is None: - _interval_set = False - interval = 5 - else: - _interval_set = True - - _text_checker( - job, interval, _interval_set, quiet=quiet, output=output, line_discipline=line_discipline - ) diff --git a/qiskit/tools/monitor/overview.py b/qiskit/tools/monitor/overview.py deleted file mode 100644 index 41853fb3ad26..000000000000 --- a/qiskit/tools/monitor/overview.py +++ /dev/null @@ -1,247 +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. - -"""A module for viewing the details of all available devices. -""" - -import math -from qiskit.exceptions import QiskitError, MissingOptionalLibraryError - - -def get_unique_backends(): - """Gets the unique backends that are available. - - Returns: - list: Unique available backends. - - Raises: - QiskitError: No backends available. - MissingOptionalLibraryError: If qiskit-ibmq-provider is not installed - """ - try: - from qiskit.providers.ibmq import IBMQ - except ImportError as ex: - raise MissingOptionalLibraryError( - libname="qiskit-ibmq-provider", - name="get_unique_backends", - pip_install="pip install qiskit-ibmq-provider", - ) from ex - backends = [] - for provider in IBMQ.providers(): - for backend in provider.backends(): - backends.append(backend) - unique_hardware_backends = [] - unique_names = [] - for back in backends: - if back.name() not in unique_names and not back.configuration().simulator: - unique_hardware_backends.append(back) - unique_names.append(back.name()) - if not unique_hardware_backends: - raise QiskitError("No backends available.") - return unique_hardware_backends - - -def backend_monitor(backend): - """Monitor a single IBMQ backend. - - Args: - backend (IBMQBackend): Backend to monitor. - Raises: - QiskitError: Input is not a IBMQ backend. - MissingOptionalLibraryError: If qiskit-ibmq-provider is not installed - - Examples: - .. code-block:: python - - from qiskit.providers.ibmq import IBMQ - from qiskit.tools.monitor import backend_monitor - provider = IBMQ.get_provider(hub='ibm-q') - backend_monitor(provider.backends.ibmq_lima) - """ - try: - from qiskit.providers.ibmq import IBMQBackend - except ImportError as ex: - raise MissingOptionalLibraryError( - libname="qiskit-ibmq-provider", - name="backend_monitor", - pip_install="pip install qiskit-ibmq-provider", - ) from ex - - if not isinstance(backend, IBMQBackend): - raise QiskitError("Input variable is not of type IBMQBackend.") - config = backend.configuration().to_dict() - status = backend.status().to_dict() - config_dict = {**status, **config} - - print(backend.name()) - print("=" * len(backend.name())) - print("Configuration") - print("-" * 13) - offset = " " - - upper_list = [ - "n_qubits", - "operational", - "status_msg", - "pending_jobs", - "backend_version", - "basis_gates", - "local", - "simulator", - ] - - lower_list = list(set(config_dict.keys()).difference(upper_list)) - # Remove gates because they are in a different tab - lower_list.remove("gates") - for item in upper_list + lower_list: - print(offset + item + ":", config_dict[item]) - - # Stop here if simulator - if config["simulator"]: - return - - print() - props = backend.properties() - qubit_header = None - sep = " / " - - for index, qubit_data in enumerate(props.qubits): - name = "Q%s" % index - gate_data = [gate for gate in props.gates if gate.qubits == [index]] - - cal_data = dict.fromkeys(["T1", "T2", "frequency", "readout_error"], "Unknown") - for nduv in qubit_data: - if nduv.name in cal_data: - cal_data[nduv.name] = format(nduv.value, ".5f") + " " + nduv.unit - - gate_names = [] - gate_error = [] - for gd in gate_data: - if gd.gate in ["id"]: - continue - try: - gate_error.append(format(props.gate_error(gd.gate, index), ".5f")) - gate_names.append(gd.gate.upper() + " err") - except QiskitError: - pass - - if not qubit_header: - qubit_header = ( - "Qubits [Name / Freq / T1 / T2" + sep.join([""] + gate_names) + " / Readout err]" - ) - print(qubit_header) - print("-" * len(qubit_header)) - - qstr = sep.join( - [name, cal_data["frequency"], cal_data["T1"], cal_data["T2"]] - + gate_error - + [cal_data["readout_error"]] - ) - - print(offset + qstr) - - print() - multi_qubit_gates = [g for g in props.gates if len(g.qubits) > 1] - multi_header = "Multi-Qubit Gates [Name / Type / Gate Error]" - print(multi_header) - print("-" * len(multi_header)) - - for gate in multi_qubit_gates: - qubits = gate.qubits - ttype = gate.gate - error = "Unknown" - try: - error = format(props.gate_error(gate.gate, qubits), ".5f") - except QiskitError: - pass - mstr = sep.join([f"{ttype}{qubits[0]}_{qubits[1]}", ttype, str(error)]) - print(offset + mstr) - - -def backend_overview(): - """Gives overview information on all the IBMQ - backends that are available. - - Examples: - - .. code-block:: python - - from qiskit.providers.ibmq import IBMQ - from qiskit.tools.monitor import backend_overview - provider = IBMQ.get_provider(hub='ibm-q') - backend_overview() - """ - unique_hardware_backends = get_unique_backends() - _backends = [] - # Sort backends by operational or not - for idx, back in enumerate(unique_hardware_backends): - if back.status().operational: - _backends = [back] + _backends - else: - _backends = _backends + [back] - - stati = [back.status() for back in _backends] - idx = list(range(len(_backends))) - pending = [s.pending_jobs for s in stati] - _, least_idx = zip(*sorted(zip(pending, idx))) - - # Make sure least pending is operational - for ind in least_idx: - if stati[ind].operational: - least_pending_idx = ind - break - - num_rows = math.ceil(len(_backends) / 3) - - count = 0 - num_backends = len(_backends) - for _ in range(num_rows): - max_len = 0 - str_list = [""] * 8 - for idx in range(3): - offset = " " * 10 if idx else "" - config = _backends[count].configuration().to_dict() - props = _backends[count].properties().to_dict() - num_qubits = config["n_qubits"] - str_list[0] += " " * (max_len - len(str_list[0])) + offset - str_list[0] += _backends[count].name() - - str_list[1] += " " * (max_len - len(str_list[1])) + offset - str_list[1] += "-" * len(_backends[count].name()) - - str_list[2] += " " * (max_len - len(str_list[2])) + offset - str_list[2] += "Num. Qubits: %s" % config["n_qubits"] - - str_list[3] += " " * (max_len - len(str_list[3])) + offset - str_list[3] += "Pending Jobs: %s" % stati[count].pending_jobs - - str_list[4] += " " * (max_len - len(str_list[4])) + offset - str_list[4] += "Least busy: %s" % (count == least_pending_idx) - - str_list[5] += " " * (max_len - len(str_list[5])) + offset - str_list[5] += "Operational: %s" % stati[count].operational - - str_list[6] += " " * (max_len - len(str_list[6])) + offset - str_list[6] += "Avg. T1: %s" % round( - sum(q[0]["value"] for q in props["qubits"]) / num_qubits, 1 - ) - str_list[7] += " " * (max_len - len(str_list[7])) + offset - str_list[7] += "Avg. T2: %s" % round( - sum(q[1]["value"] for q in props["qubits"]) / num_qubits, 1 - ) - count += 1 - if count == num_backends: - break - max_len = max(len(s) for s in str_list) - - print("\n".join(str_list)) - print("\n" * 2) diff --git a/qiskit/visualization/library.py b/qiskit/visualization/library.py new file mode 100644 index 000000000000..5832a475f8fd --- /dev/null +++ b/qiskit/visualization/library.py @@ -0,0 +1,37 @@ +# 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. + +# pylint: disable=invalid-name,no-name-in-module,ungrouped-imports + +"""A circuit library visualization""" + +from qiskit import QuantumCircuit +from qiskit.utils import optionals as _optionals + + +@_optionals.HAS_MATPLOTLIB.require_in_call +def _generate_circuit_library_visualization(circuit: QuantumCircuit): + import matplotlib.pyplot as plt + + circuit = circuit.decompose() + ops = circuit.count_ops() + num_nl = circuit.num_nonlocal_gates() + _fig, (ax0, ax1) = plt.subplots(2, 1) + circuit.draw("mpl", ax=ax0) + ax1.axis("off") + ax1.grid(visible=None) + ax1.table( + [[circuit.name], [circuit.width()], [circuit.depth()], [sum(ops.values())], [num_nl]], + rowLabels=["Circuit Name", "Width", "Depth", "Total Gates", "Non-local Gates"], + ) + plt.tight_layout() + plt.show() diff --git a/releasenotes/notes/remove-ibmq-4bb57a04991da9af.yaml b/releasenotes/notes/remove-ibmq-4bb57a04991da9af.yaml new file mode 100644 index 000000000000..831b55ea9fdf --- /dev/null +++ b/releasenotes/notes/remove-ibmq-4bb57a04991da9af.yaml @@ -0,0 +1,34 @@ +--- +upgrade: + - | + The deprecated ``qiskit.IBMQ`` object hase been removed. This alias object + was marked as deprecated in the 0.23.0 release. This alias object lazily + redirected attribute access to ``qiskit.providers.ibmq.IBMQ``. As the + ``qiskit-ibmq-provider`` package has now been retired and superseded by + ``qiskit-ibm-provider`` package which maintains its own namespace + maintaining this alias is no longer relevant. If you + were relying on the ``qiskit.IBMQ`` alias you should migrate your usage to + the ``qiskit-ibm-provider`` package, see the + `migration guide `__ + for more details. + - | + Removed the deprecated module ``qiskit.tools.jupyter`` which previously + included jupyter magics and widgets for interactively visualizing some data + from Qiskit. This module was deprecated in Qiskit 0.46.0. Most of this + functionality was directly tied to the legacy ``qiskit-ibmq-provider`` + package and was no longer valid so the module was removed. Similar + functionality is available from the ``qiskit_ibm_provider.jupyter`` module + in the `qiskit-ibm-provider __` package. + - | + Removed the deprecated module ``qiskit.tools.monitor`` which previously + included tools for tracking :class:`.JobV1` job instances, primarily from + the legacy ``qiskit-ibm-provider`` package. This module was marked as + deprecated in Qiskit 0.46.0. It is being removed because it was directly + tied to the legacy ``qiskit-ibm-provider`` package. + - | + Removed the deprecated import path ``qiskit.test.mock`` which previously was + used to redirect imports for the mock backends to their newer location in + the :mod:`qiskit.providers.fake_provider`. This module was marked as + deprecated in Qiskit 0.37.0. If you were using this module you should update + your imports from ``qiskit.test.mock`` to + :mod:`qiskit.providers.fake_provider` instead. diff --git a/requirements-optional.txt b/requirements-optional.txt index 0e94040e69c1..36985cdd7cd9 100644 --- a/requirements-optional.txt +++ b/requirements-optional.txt @@ -8,12 +8,9 @@ # Test-runner enhancements. fixtures testtools -jupyter # Interactivity. -ipykernel ipython -ipywidgets>=7.3.0 matplotlib>=3.3 pillow>=4.2.1 pydot diff --git a/test/python/mock/test_mock_module_deprecation.py b/test/python/mock/test_mock_module_deprecation.py deleted file mode 100644 index d2355c84717b..000000000000 --- a/test/python/mock/test_mock_module_deprecation.py +++ /dev/null @@ -1,28 +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. - -"""Test for deprecation of qiskit.test.mock module.""" -from qiskit.test import QiskitTestCase - - -class MockModuleDeprecationTest(QiskitTestCase): - """Test for deprecation of qiskit.test.mock module.""" - - def test_deprecated_mock_module(self): - """Test that the mock module is deprecated.""" - # pylint: disable=unused-import,no-name-in-module - with self.assertWarns(DeprecationWarning): - from qiskit.test.mock import FakeWashington - with self.assertWarns(DeprecationWarning): - from qiskit.test.mock.backends import FakeWashington - with self.assertWarns(DeprecationWarning): - from qiskit.test.mock.backends.washington import FakeWashington diff --git a/test/python/notebooks/test_backend_tools.ipynb b/test/python/notebooks/test_backend_tools.ipynb deleted file mode 100644 index d2d892b9cd56..000000000000 --- a/test/python/notebooks/test_backend_tools.ipynb +++ /dev/null @@ -1,156 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-09T09:29:08.426863Z", - "start_time": "2019-08-09T09:29:08.423361Z" - } - }, - "outputs": [], - "source": [ - "import os\n", - "import sys\n", - "\n", - "cwd = os.getcwd()\n", - "qiskit_dir = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(cwd))))\n", - "sys.path.append(qiskit_dir)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-09T09:29:08.669360Z", - "start_time": "2019-08-09T09:29:08.612812Z" - } - }, - "outputs": [], - "source": [ - "from qiskit import IBMQ\n", - "from qiskit.tools.jupyter import *" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-09T09:29:11.253688Z", - "start_time": "2019-08-09T09:29:08.791274Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "IBMQ.load_account()" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-09T09:29:25.505599Z", - "start_time": "2019-08-09T09:29:11.266347Z" - } - }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "99942fff1dc24cdf9a105c018f6a1260", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(HTML(value=\"

=3.8 on osx and windows", - ) - def test_jupyter_jobs_pbars(self): - """Test Jupyter progress bars and job status functionality""" - self._execute_notebook(os.path.join(self.notebook_dir, "test_pbar_status.ipynb")) - - @unittest.skipIf(not optionals.HAS_MATPLOTLIB, "matplotlib not available.") - @slow_test - def test_backend_tools(self): - """Test Jupyter backend tools.""" - self._execute_notebook(os.path.join(self.notebook_dir, "test_backend_tools.ipynb")) - - -if __name__ == "__main__": - unittest.main(verbosity=2) diff --git a/test/python/tools/monitor/__init__.py b/test/python/tools/monitor/__init__.py deleted file mode 100644 index 146d5c0e0b91..000000000000 --- a/test/python/tools/monitor/__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. - -"""Tests for backend monitor-related functionality.""" diff --git a/test/python/tools/monitor/test_backend_monitor.py b/test/python/tools/monitor/test_backend_monitor.py deleted file mode 100644 index a2c4802802e6..000000000000 --- a/test/python/tools/monitor/test_backend_monitor.py +++ /dev/null @@ -1,103 +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. - -"""Tests for the wrapper functionality.""" - -import sys -import unittest -from unittest.mock import patch -from unittest.mock import MagicMock -from io import StringIO - -import qiskit -from qiskit import providers -from qiskit.tools.monitor import backend_overview, backend_monitor -from qiskit.test import QiskitTestCase -from qiskit.providers.fake_provider import FakeProviderFactory, FakeBackend, FakeVigo - - -class TestBackendOverview(QiskitTestCase): - """Tools test case.""" - - def _restore_ibmq(self): - if not self.import_error: - qiskit.IBMQ = self.ibmq_back - else: - del qiskit.IBMQ - if self.prov_backup: - providers.ibmq = self.prov_backup - else: - del providers.ibmq - - def _restore_ibmq_mod(self): - if self.ibmq_module_backup is not None: - sys.modules["qiskit.providers.ibmq"] = self.ibmq_module_backup - else: - sys.modules.pop("qiskit.providers.ibmq") - - def setUp(self): - super().setUp() - ibmq_mock = MagicMock() - ibmq_mock.IBMQBackend = FakeBackend - if "qiskit.providers.ibmq" in sys.modules: - self.ibmq_module_backup = sys.modules["qiskit.providers.ibmq"] - else: - self.ibmq_module_backup = None - sys.modules["qiskit.providers.ibmq"] = ibmq_mock - self.addCleanup(self._restore_ibmq_mod) - - if hasattr(qiskit, "IBMQ"): - self.import_error = False - else: - self.import_error = True - qiskit.IBMQ = None - self.ibmq_back = qiskit.IBMQ - qiskit.IBMQ = FakeProviderFactory() - self.addCleanup(self._restore_ibmq) - if hasattr(providers, "ibmq"): - self.prov_backup = providers.ibmq - else: - self.prov_backup = None - providers.ibmq = MagicMock() - - @patch("qiskit.tools.monitor.overview.get_unique_backends", return_value=[FakeVigo()]) - def test_backend_overview(self, _): - """Test backend_overview""" - with patch("sys.stdout", new=StringIO()) as fake_stdout: - backend_overview() - stdout = fake_stdout.getvalue() - self.assertIn("Operational:", stdout) - self.assertIn("Avg. T1:", stdout) - self.assertIn("Num. Qubits:", stdout) - - @patch("qiskit.tools.monitor.overview.get_unique_backends", return_value=[FakeVigo()]) - def test_backend_monitor(self, _): - """Test backend_monitor""" - for back in [FakeVigo()]: - if not back.configuration().simulator: - backend = back - break - with patch("sys.stdout", new=StringIO()) as fake_stdout: - backend_monitor(backend) - - stdout = fake_stdout.getvalue() - self.assertIn("Configuration", stdout) - self.assertIn("Qubits [Name / Freq / T1 / T2 / ", stdout) - for gate in backend.properties().gates: - if gate.gate not in ["id"] and len(gate.qubits) == 1: - self.assertIn(gate.gate.upper() + " err", stdout) - self.assertIn("Readout err", stdout) - self.assertIn("Multi-Qubit Gates [Name / Type / Gate Error]", stdout) - - -if __name__ == "__main__": - unittest.main(verbosity=2) diff --git a/test/python/tools/monitor/test_job_monitor.py b/test/python/tools/monitor/test_job_monitor.py deleted file mode 100644 index 17409ac1022a..000000000000 --- a/test/python/tools/monitor/test_job_monitor.py +++ /dev/null @@ -1,43 +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. - -"""Tests for the wrapper functionality.""" - -import io -import unittest -from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit -from qiskit import BasicAer -from qiskit import execute -from qiskit.tools.monitor import job_monitor -from qiskit.test import QiskitTestCase - - -class TestJobMonitor(QiskitTestCase): - """Tools test case.""" - - def test_job_monitor(self): - """Test job_monitor""" - qreg = QuantumRegister(2) - creg = ClassicalRegister(2) - qc = QuantumCircuit(qreg, creg) - qc.h(qreg[0]) - qc.cx(qreg[0], qreg[1]) - qc.measure(qreg, creg) - backend = BasicAer.get_backend("qasm_simulator") - job_sim = execute([qc] * 10, backend) - output = io.StringIO() - job_monitor(job_sim, output=output) - self.assertEqual(job_sim.status().name, "DONE") - - -if __name__ == "__main__": - unittest.main(verbosity=2) diff --git a/tools/update_fake_backends.py b/tools/update_fake_backends.py index 32f26a1c2411..415a5c68fd55 100755 --- a/tools/update_fake_backends.py +++ b/tools/update_fake_backends.py @@ -19,7 +19,7 @@ import json import os -from qiskit.providers.ibmq import IBMQ +from qiskit_ibm_provider import IBMProvider from qiskit.circuit.parameterexpression import ParameterExpression @@ -57,9 +57,7 @@ def _main(): parser.add_argument("--hub", type=str, default=None) parser.add_argument("--group", type=str, default=None) args = parser.parse_args() - provider = IBMQ.load_account() - if args.hub or args.group or args.project: - provider = IBMQ.get_provider(hub=args.hub, group=args.group, project=args.project) + provider = IBMProvider() ibmq_backends = provider.backends() for backend in ibmq_backends: raw_name = backend.name()