From dcdb95a0d50b3a5bf01f4649064923a605f2e1a3 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Wed, 20 Dec 2023 14:23:39 +0000 Subject: [PATCH 1/3] Remove `Aer` and imports from `qiskit.providers.aer` This removes the `Aer` object and the metapath shenanigans that enabled importing from `qiskit.providers.aer`. All references to `qiskit.providers.aer` in the Terra repository are updated to use `qiskit_aer` instead, which is a drop-in replacement following Aer 0.11. This commit leaves the IBMQ object in place for now, since its removal will affect many other places in the repository and should be handled separately. --- qiskit/__init__.py | 55 -------------- qiskit/namespace.py | 76 ------------------- qiskit/providers/fake_provider/__init__.py | 2 +- .../providers/fake_provider/fake_backend.py | 32 ++++---- qiskit/test/decorators.py | 12 +-- qiskit/utils/optionals.py | 6 +- .../remove-aer-hooks-1144714bbbdd0fe8.yaml | 8 ++ test/python/transpiler/test_sabre_swap.py | 2 +- tools/find_optional_imports.py | 2 +- 9 files changed, 32 insertions(+), 163 deletions(-) delete mode 100644 qiskit/namespace.py create mode 100644 releasenotes/notes/remove-aer-hooks-1144714bbbdd0fe8.yaml diff --git a/qiskit/__init__.py b/qiskit/__init__.py index 664cd8c57c5c..4d9f17825bc8 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -44,16 +44,6 @@ "qiskit._accelerate.convert_2q_block_matrix" ] = qiskit._accelerate.convert_2q_block_matrix - -# Extend namespace for backwards compat -from qiskit import namespace - -# Add hook to redirect imports from qiskit.providers.aer* to qiskit_aer* -# this is necessary for backwards compatibility for users when qiskit-aer -# and qiskit-terra shared the qiskit namespace -new_meta_path_finder = namespace.QiskitElementImport("qiskit.providers.aer", "qiskit_aer") -sys.meta_path = [new_meta_path_finder] + sys.meta_path - # qiskit errors operator from qiskit.exceptions import QiskitError, MissingOptionalLibraryError @@ -82,49 +72,6 @@ from .version import __version__ -class AerWrapper: - """Lazy loading wrapper for Aer provider.""" - - def __init__(self): - self.aer = None - - def __bool__(self): - if self.aer is None: - try: - from qiskit.providers import aer - - self.aer = aer.Aer - warnings.warn( - "The qiskit.Aer entry point will be deprecated in a future release and " - "subsequently removed. Instead you should use this " - "directly from the root of the qiskit-aer package.", - PendingDeprecationWarning, - stacklevel=2, - ) - except ImportError: - return False - return True - - def __getattr__(self, attr): - if not self.aer: - try: - from qiskit.providers import aer - - self.aer = aer.Aer - warnings.warn( - "The qiskit.Aer entry point will be deprecated in a future release and " - "subsequently removed. Instead you should use this " - "directly from the root of the qiskit-aer package.", - PendingDeprecationWarning, - stacklevel=2, - ) - except ImportError as ex: - raise MissingOptionalLibraryError( - "qiskit-aer", "Aer provider", "pip install qiskit-aer" - ) from ex - return getattr(self.aer, attr) - - class IBMQWrapper: """Lazy loading wrapper for IBMQ provider.""" @@ -174,11 +121,9 @@ def __getattr__(self, attr): return getattr(self.ibmq, attr) -Aer = AerWrapper() IBMQ = IBMQWrapper() __all__ = [ - "Aer", "AncillaRegister", "BasicAer", "ClassicalRegister", diff --git a/qiskit/namespace.py b/qiskit/namespace.py deleted file mode 100644 index c33d637258bc..000000000000 --- a/qiskit/namespace.py +++ /dev/null @@ -1,76 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -# pylint: disable=unused-argument - -"""Module for utilities to manually construct qiskit namespace""" - -import sys -from importlib.abc import MetaPathFinder, Loader -import importlib - - -def _new_namespace(fullname, old_namespace, new_package): - names = fullname.split(".") - new_namespace_names = new_package.split(".") - old_namespace_names = old_namespace.split(".") - fullname = ".".join(new_namespace_names + names[len(old_namespace_names) :]) - return fullname - - -class QiskitLoader(Loader): - """Load qiskit element as a namespace package.""" - - def __init__(self, new_package, old_namespace): - super().__init__() - self.new_package = new_package - self.old_namespace = old_namespace - - def module_repr(self, module): - return repr(module) - - def create_module(self, spec): - return self.load_module(spec.name) - - def exec_module(self, module): - """Executes the module. Not needed in Qiskit.""" - pass - - def load_module(self, fullname): - old_name = fullname - fullname = _new_namespace(fullname, self.old_namespace, self.new_package) - module = importlib.import_module(fullname) - sys.modules[old_name] = module - return module - - -class QiskitElementImport(MetaPathFinder): - """Meta importer to enable unified qiskit namespace.""" - - def __init__(self, old_namespace, new_package): - super().__init__() - self.old_namespace = old_namespace - self.new_package = new_package - - def find_spec(self, fullname, path=None, target=None): - """Return the ModuleSpec for Qiskit element.""" - if fullname.startswith(self.old_namespace): - try: - importlib.import_module( - _new_namespace(fullname, self.old_namespace, self.new_package) - ) - return importlib.util.spec_from_loader( - fullname, QiskitLoader(self.new_package, self.old_namespace), origin="qiskit" - ) - except ModuleNotFoundError: - return None - return None diff --git a/qiskit/providers/fake_provider/__init__.py b/qiskit/providers/fake_provider/__init__.py index 943869114f85..35e768a7debc 100644 --- a/qiskit/providers/fake_provider/__init__.py +++ b/qiskit/providers/fake_provider/__init__.py @@ -71,7 +71,7 @@ .. code-block:: python from qiskit.providers.ibmq import IBMQ - from qiskit.providers.aer import AerSimulator + from qiskit_aer import AerSimulator # get a real backend from a real provider provider = IBMQ.load_account() diff --git a/qiskit/providers/fake_provider/fake_backend.py b/qiskit/providers/fake_provider/fake_backend.py index 35e993185bfe..81e2cfc646a9 100644 --- a/qiskit/providers/fake_provider/fake_backend.py +++ b/qiskit/providers/fake_provider/fake_backend.py @@ -118,9 +118,9 @@ def _parse_channels(self, channels): def _setup_sim(self): if _optionals.HAS_AER: - from qiskit.providers import aer + from qiskit_aer import AerSimulator - self.sim = aer.AerSimulator() + self.sim = AerSimulator() if self.target and self._props_dict: noise_model = self._get_noise_model_from_backend_v2() self.sim.set_options(noise_model=noise_model) @@ -199,9 +199,9 @@ def _default_options(cls): default values set """ if _optionals.HAS_AER: - from qiskit.providers import aer + from qiskit_aer import AerSimulator - return aer.AerSimulator._default_options() + return AerSimulator._default_options() else: return basicaer.QasmSimulatorPy._default_options() @@ -375,13 +375,13 @@ def _get_noise_model_from_backend_v2( from qiskit.circuit import Delay from qiskit.providers.exceptions import BackendPropertyError - from qiskit.providers.aer.noise import NoiseModel - from qiskit.providers.aer.noise.device.models import ( + from qiskit_aer.noise import NoiseModel + from qiskit_aer.noise.device.models import ( _excited_population, basic_device_gate_errors, basic_device_readout_errors, ) - from qiskit.providers.aer.noise.passes import RelaxationNoisePass + from qiskit_aer.noise.passes import RelaxationNoisePass if self._props_dict is None: self._set_props_dict_from_json() @@ -402,7 +402,7 @@ def _get_noise_model_from_backend_v2( with warnings.catch_warnings(): warnings.filterwarnings( "ignore", - module="qiskit.providers.aer.noise.device.models", + module="qiskit_aer.noise.device.models", ) gate_errors = basic_device_gate_errors( properties, @@ -458,10 +458,10 @@ def __init__(self, configuration, time_alive=10): def _setup_sim(self): if _optionals.HAS_AER: - from qiskit.providers import aer - from qiskit.providers.aer.noise import NoiseModel + from qiskit_aer import AerSimulator + from qiskit_aer.noise import NoiseModel - self.sim = aer.AerSimulator() + self.sim = AerSimulator() if self.properties(): noise_model = NoiseModel.from_backend(self) self.sim.set_options(noise_model=noise_model) @@ -526,9 +526,9 @@ def properties(self): @classmethod def _default_options(cls): if _optionals.HAS_AER: - from qiskit.providers import aer + from qiskit_aer import QasmSimulator - return aer.QasmSimulator._default_options() + return QasmSimulator._default_options() else: return basicaer.QasmSimulatorPy._default_options() @@ -553,11 +553,11 @@ def run(self, run_input, **kwargs): ) if pulse_job: if _optionals.HAS_AER: - from qiskit.providers import aer - from qiskit.providers.aer.pulse import PulseSystemModel + from qiskit_aer import Aer + from qiskit_aer.pulse import PulseSystemModel system_model = PulseSystemModel.from_backend(self) - sim = aer.Aer.get_backend("pulse_simulator") + sim = Aer.get_backend("pulse_simulator") job = sim.run(circuits, system_model=system_model, **kwargs) else: raise QiskitError("Unable to run pulse schedules without qiskit-aer installed") diff --git a/qiskit/test/decorators.py b/qiskit/test/decorators.py index a15ab9ccc361..2f1ed064f201 100644 --- a/qiskit/test/decorators.py +++ b/qiskit/test/decorators.py @@ -17,11 +17,10 @@ import functools import os import socket -import sys from typing import Union, Callable, Type, Iterable import unittest -from qiskit.utils import wrap_method +from qiskit.utils import wrap_method, optionals from .testing_options import get_test_options HAS_NET_CONNECTION = None @@ -55,14 +54,7 @@ def is_aer_provider_available(): Returns: bool: True if simulator executable is available """ - # TODO: HACK FROM THE DEPTHS OF DESPAIR AS AER DOES NOT WORK ON MAC - if sys.platform == "darwin": - return False - try: - import qiskit.providers.aer # pylint: disable=unused-import - except ImportError: - return False - return True + return bool(optionals.HAS_AER) def requires_aer_provider(test_item): diff --git a/qiskit/utils/optionals.py b/qiskit/utils/optionals.py index cb69c6b5136c..f27f648f7842 100644 --- a/qiskit/utils/optionals.py +++ b/qiskit/utils/optionals.py @@ -29,8 +29,8 @@ :widths: 25 75 * - .. py:data:: HAS_AER - - :mod:`Qiskit Aer ` provides high-performance simulators for the - quantum circuits constructed within Qiskit. + - `Qiskit Aer ` provides high-performance simulators for + the quantum circuits constructed within Qiskit. * - .. py:data:: HAS_IBMQ - The :mod:`Qiskit IBMQ Provider ` is used for accessing IBM Quantum @@ -224,7 +224,7 @@ _logger = _logging.getLogger(__name__) HAS_AER = _LazyImportTester( - "qiskit.providers.aer", + "qiskit_aer", name="Qiskit Aer", install="pip install qiskit-aer", ) diff --git a/releasenotes/notes/remove-aer-hooks-1144714bbbdd0fe8.yaml b/releasenotes/notes/remove-aer-hooks-1144714bbbdd0fe8.yaml new file mode 100644 index 000000000000..6167c29aeb5a --- /dev/null +++ b/releasenotes/notes/remove-aer-hooks-1144714bbbdd0fe8.yaml @@ -0,0 +1,8 @@ +--- +upgrade: + - | + The object ``qiskit.Aer`` has been removed following its deprecation in Qiskit 0.46. You can + instead use ``qiskit_aer.Aer``, which is a drop-in replacement. + - | + Importing from ``qiskit.providers.aer`` will no longer work, following its deprecation in Qiskit + 0.46. You should instead import from ``qiskit_aer``, which is a drop-in replacement. diff --git a/test/python/transpiler/test_sabre_swap.py b/test/python/transpiler/test_sabre_swap.py index 100e7882c782..5fbee24dc778 100644 --- a/test/python/transpiler/test_sabre_swap.py +++ b/test/python/transpiler/test_sabre_swap.py @@ -274,7 +274,7 @@ def test_no_infinite_loop(self, method): if not optionals.HAS_AER: return - from qiskit import Aer + from qiskit_aer import Aer sim = Aer.get_backend("aer_simulator") in_results = sim.run(qc, shots=4096).result().get_counts() diff --git a/tools/find_optional_imports.py b/tools/find_optional_imports.py index 5ee23c882d2d..1a7e0862ed60 100755 --- a/tools/find_optional_imports.py +++ b/tools/find_optional_imports.py @@ -29,7 +29,7 @@ def _main(): "ipywidgets", "scipy.stats", "matplotlib", - "qiskit.providers.aer", + "qiskit_aer", "qiskit.providers.ibmq", "qiskit.ignis", "qiskit.aqua", From f73360ed4c31df2763dbb398baa667a821ac2f2c Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Wed, 20 Dec 2023 20:49:43 +0000 Subject: [PATCH 2/3] Fix randomised-test Aer usage --- test/randomized/test_transpiler_equivalence.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/randomized/test_transpiler_equivalence.py b/test/randomized/test_transpiler_equivalence.py index 0fd1d501a67f..320ffb2f09af 100644 --- a/test/randomized/test_transpiler_equivalence.py +++ b/test/randomized/test_transpiler_equivalence.py @@ -56,7 +56,7 @@ import hypothesis.strategies as st -from qiskit import transpile, Aer, qasm2 +from qiskit import transpile, qasm2 from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister from qiskit.circuit import Measure, Reset, Gate, Barrier from qiskit.providers.fake_provider import ( @@ -91,10 +91,11 @@ ) from qiskit.test.base import dicts_almost_equal - # pylint: disable=wildcard-import,unused-wildcard-import from qiskit.circuit.library.standard_gates import * +from qiskit_aer import Aer # pylint: disable=wrong-import-order + default_profile = "transpiler_equivalence" settings.register_profile( default_profile, From e336eb75a3a645972a20089df4ea0dfcf7e1eade Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Thu, 21 Dec 2023 12:05:36 +0000 Subject: [PATCH 3/3] Remove claim of pulse simulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com> --- .../providers/fake_provider/fake_backend.py | 26 +++++++------------ .../remove-aer-hooks-1144714bbbdd0fe8.yaml | 5 ++++ 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/qiskit/providers/fake_provider/fake_backend.py b/qiskit/providers/fake_provider/fake_backend.py index 81e2cfc646a9..c228b947dd83 100644 --- a/qiskit/providers/fake_provider/fake_backend.py +++ b/qiskit/providers/fake_provider/fake_backend.py @@ -348,7 +348,7 @@ def run(self, run_input, **options): "QuantumCircuit, Schedule, or a list of either" % circuits ) if pulse_job: # pulse job - raise QiskitError("Pulse simulation is currently not supported for V2 fake backends.") + raise QiskitError("Pulse simulation is currently not supported for fake backends.") # circuit job if not _optionals.HAS_AER: warnings.warn("Aer not found using BasicAer and no noise", RuntimeWarning) @@ -552,20 +552,12 @@ def run(self, run_input, **kwargs): "QuantumCircuit, Schedule, or a list of either" % circuits ) if pulse_job: - if _optionals.HAS_AER: - from qiskit_aer import Aer - from qiskit_aer.pulse import PulseSystemModel - - system_model = PulseSystemModel.from_backend(self) - sim = Aer.get_backend("pulse_simulator") - job = sim.run(circuits, system_model=system_model, **kwargs) - else: - raise QiskitError("Unable to run pulse schedules without qiskit-aer installed") - else: - if self.sim is None: - self._setup_sim() - if not _optionals.HAS_AER: - warnings.warn("Aer not found using BasicAer and no noise", RuntimeWarning) - self.sim._options = self._options - job = self.sim.run(circuits, **kwargs) + raise QiskitError("Pulse simulation is currently not supported for fake backends.") + # circuit job + if not _optionals.HAS_AER: + warnings.warn("Aer not found using BasicAer and no noise", RuntimeWarning) + if self.sim is None: + self._setup_sim() + self.sim._options = self._options + job = self.sim.run(circuits, **kwargs) return job diff --git a/releasenotes/notes/remove-aer-hooks-1144714bbbdd0fe8.yaml b/releasenotes/notes/remove-aer-hooks-1144714bbbdd0fe8.yaml index 6167c29aeb5a..c500d013c2c6 100644 --- a/releasenotes/notes/remove-aer-hooks-1144714bbbdd0fe8.yaml +++ b/releasenotes/notes/remove-aer-hooks-1144714bbbdd0fe8.yaml @@ -6,3 +6,8 @@ upgrade: - | Importing from ``qiskit.providers.aer`` will no longer work, following its deprecation in Qiskit 0.46. You should instead import from ``qiskit_aer``, which is a drop-in replacement. + - | + Pulse jobs are no longer supported in fake backends, following the deprecation and removal + of the underlying simulation functionality in Qiskit Aer. For pulse-level simulation, outside + the context of circuit objects, consider using a special-purpose library such as + `Qiskit Dynamics `__.