Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrap qiskit.Aer and qiskit.IBMQ with lazy loading object #5619

Merged
merged 17 commits into from
Feb 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ stages:
python -m pip install --upgrade pip
pip install -U -r requirements.txt -r requirements-dev.txt -c constraints.txt
pip install -c constraints.txt -e .
pip install "qiskit-ibmq-provider" "qiskit-aer" "z3-solver" "qiskit-ignis" "qiskit-aqua" "pyscf<1.7.4" "matplotlib<3.3.0" sphinx nbsphinx sphinx_rtd_theme cvxpy -c constraints.txt
pip install "qiskit-ibmq-provider" "qiskit-aer" "z3-solver" "git+https://github.com/Qiskit/qiskit-ignis" "qiskit-aqua" "pyscf<1.7.4" "matplotlib<3.3.0" sphinx nbsphinx sphinx_rtd_theme cvxpy -c constraints.txt
python setup.py build_ext --inplace
sudo apt install -y graphviz pandoc
pip check
Expand Down
82 changes: 59 additions & 23 deletions qiskit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

"""Main Qiskit public functionality."""


import pkgutil
import sys
import warnings
Expand Down Expand Up @@ -49,28 +48,6 @@

_config = _user_config.get_config()

# Try to import the Aer provider if installed.
try:
from qiskit.providers.aer import Aer
except ImportError:
suppress_warnings = os.environ.get('QISKIT_SUPPRESS_PACKAGING_WARNINGS', '')
if suppress_warnings.upper() != 'Y':
if not _config.get('suppress_packaging_warnings') or suppress_warnings.upper() == 'N':
warnings.warn('Could not import the Aer provider from the qiskit-aer '
'package. Install qiskit-aer or check your installation.',
RuntimeWarning)
# Try to import the IBMQ provider if installed.
try:
from qiskit.providers.ibmq import IBMQ
except ImportError:
suppress_warnings = os.environ.get('QISKIT_SUPPRESS_PACKAGING_WARNINGS', '')
if suppress_warnings.upper() != 'Y':
if not _config.get('suppress_packaging_warnings') or suppress_warnings.upper() == 'N':
warnings.warn('Could not import the IBMQ provider from the '
'qiskit-ibmq-provider package. Install '
'qiskit-ibmq-provider or check your installation.',
RuntimeWarning)

# 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 # noqa
Expand All @@ -88,3 +65,62 @@
"Using Qiskit with Python 3.6 is deprecated as of the 0.17.0 release. "
"Support for running Qiskit with Python 3.6 will be removed in a "
"future release.", DeprecationWarning)


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
except ImportError:
return False
return True

def __getattr__(self, attr):
if not self.aer:
try:
from qiskit.providers import aer
self.aer = aer.Aer
except ImportError as exc:
raise ImportError('Could not import the Aer provider from the '
'qiskit-aer package. Install qiskit-aer or '
'check your installation.') from exc
return getattr(self.aer, attr)


class IBMQWrapper:
"""Lazy loading wraooer 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
except ImportError:
return False
return True

def __getattr__(self, attr):
if not self.ibmq:
try:
from qiskit.providers import ibmq
self.ibmq = ibmq.IBMQ
except ImportError as exc:
raise ImportError('Could not import the IBMQ provider from the '
'qiskit-ibmq-provider package. Install '
'qiskit-ibmq-provider or check your '
'installation.') from exc
return getattr(self.ibmq, attr)


Aer = AerWrapper()
IBMQ = IBMQWrapper()
2 changes: 1 addition & 1 deletion qiskit/test/mock/fake_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from qiskit.exceptions import QiskitError

try:
from qiskit import Aer
from qiskit.providers.aer import Aer
HAS_AER = True
except ImportError:
HAS_AER = False
Expand Down
7 changes: 0 additions & 7 deletions qiskit/user_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ class UserConfig:
circuit_mpl_style = default
circuit_mpl_style_path = ~/.qiskit:<default location>
transpile_optimization_level = 1
suppress_packaging_warnings = False
parallel = False
num_processes = 4

Expand Down Expand Up @@ -116,12 +115,6 @@ def read_config_file(self):
self.settings['transpile_optimization_level'] = (
transpile_optimization_level)

# Parse package warnings
package_warnings = self.config_parser.getboolean(
'default', 'suppress_packaging_warnings', fallback=False)
if package_warnings:
self.settings['suppress_packaging_warnings'] = package_warnings

# Parse parallel
parallel_enabled = self.config_parser.getboolean(
'default', 'parallel', fallback=PARALLEL_DEFAULT)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
upgrade:
- |
The ``qiskit.Aer`` and ``qiskit.IBMQ`` top level attributes are now lazy
loaded. This means that the objects will always now exist and warnings will
no longer be raised on import if ``qiskit-aer`` or ``qiskit-ibmq-provider``
are not installed (or can't be found by Python). If you were checking for
the presence of ``qiskit-aer`` or ``qiskit-ibmq-provider`` using these
module attributes and explicitly comparing to ``None`` or looking for the
absence of the attribute this no longer will work because they are always
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
defined as an object now. In other words running something like::

try:
from qiskit import Aer
except ImportError:
print("Aer not available")

or::

try:
from qiskit import IBMQ
except ImportError:
print("IBMQ not available")

will no longer work. Instead to determine if those providers are present
you can either explicitly use ``qiskit.providers.aer.Aer`` and
``qiskit.providers.ibmq.IBMQ``::

try:
from qiskit.providers.aer import Aer
except ImportError:
print("Aer not available")

try:
from qiskit.providers.ibmq import IBMQ
except ImportError:
print("IBMQ not available")

or check ``bool(qiskit.Aer)`` and ``bool(qiskit.IBMQ)`` instead, for
example::

import qiskit

if not qiskit.Aer:
print("Aer not available")
if not qiskit.IBMQ:
print("IBMQ not available")

This change was necessary to avoid potential import cycle issues between
the qiskit packages and also to improve the import time when Aer or IBMQ
are not being used.
- |
The user config file option ``suppress_packaging_warnings`` option in the
user config file and the ``QISKIT_SUPPRESS_PACKAGING_WARNINGS`` environment
variable no longer has any effect and will be silently ignored. These
warnings have been removed and will no longer be emitted at import time
from the qiskit module.
10 changes: 6 additions & 4 deletions test/python/algorithms/test_vqe.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

# pylint: disable=no-name-in-module,import-error

""" Test VQE """

import unittest
Expand Down Expand Up @@ -167,7 +169,7 @@ def test_with_aer_statevector(self):
"""Test VQE with Aer's statevector_simulator."""
try:
# pylint: disable=import-outside-toplevel
from qiskit import Aer
from qiskit.providers.aer import Aer
except Exception as ex: # pylint: disable=broad-except
self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex)))
return
Expand All @@ -190,7 +192,7 @@ def test_with_aer_qasm(self):
"""Test VQE with Aer's qasm_simulator."""
try:
# pylint: disable=import-outside-toplevel
from qiskit import Aer
from qiskit.providers.aer import Aer
except Exception as ex: # pylint: disable=broad-except
self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex)))
return
Expand All @@ -215,7 +217,7 @@ def test_with_aer_qasm_snapshot_mode(self):
"""Test the VQE using Aer's qasm_simulator snapshot mode."""
try:
# pylint: disable=import-outside-toplevel
from qiskit import Aer
from qiskit.providers.aer import Aer
except Exception as ex: # pylint: disable=broad-except
self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex)))
return
Expand Down Expand Up @@ -308,7 +310,7 @@ def test_vqe_expectation_select(self):
"""Test expectation selection with Aer's qasm_simulator."""
try:
# pylint: disable=import-outside-toplevel
from qiskit import Aer
from qiskit.providers.aer import Aer
except Exception as ex: # pylint: disable=broad-except
self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex)))
return
Expand Down
8 changes: 5 additions & 3 deletions test/python/opflow/test_state_op_meas_evals.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

# pylint: disable=no-name-in-module,import-error

""" Test Operator construction, including OpPrimitives and singletons. """

import unittest
Expand Down Expand Up @@ -63,7 +65,7 @@ def test_coefficients_correctly_propagated(self):
"""Test that the coefficients in SummedOp and states are correctly used."""
try:
# pylint: disable=import-outside-toplevel
from qiskit import Aer
from qiskit.providers.aer import Aer
except Exception as ex: # pylint: disable=broad-except
self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex)))
return
Expand All @@ -89,7 +91,7 @@ def test_is_measurement_correctly_propagated(self):
"""Test if is_measurement property of StateFn is propagated to converted StateFn."""
try:
# pylint: disable=import-outside-toplevel
from qiskit import Aer
from qiskit.providers.aer import Aer
except Exception as ex: # pylint: disable=broad-except
self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex)))
return
Expand All @@ -103,7 +105,7 @@ def test_parameter_binding_on_listop(self):
"""Test passing a ListOp with differing parameters works with the circuit sampler."""
try:
# pylint: disable=import-outside-toplevel
from qiskit import Aer
from qiskit.providers.aer import Aer
except Exception as ex: # pylint: disable=broad-except
self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex)))
return
Expand Down
46 changes: 1 addition & 45 deletions test/python/test_user_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,6 @@ def test_empty_file_read(self):
self.assertEqual({},
config.settings)

def test_invalid_suppress_packaging_warnings(self):
test_config = """
[default]
suppress_packaging_warnings = 76
"""
self.addCleanup(os.remove, self.file_path)
with open(self.file_path, 'w') as file:
file.write(test_config)
file.flush()
config = user_config.UserConfig(self.file_path)
self.assertRaises(ValueError, config.read_config_file)

def test_invalid_optimization_level(self):
test_config = """
[default]
Expand Down Expand Up @@ -100,37 +88,6 @@ def test_optimization_level_valid(self):
'parallel_enabled': user_config.PARALLEL_DEFAULT},
config.settings)

def test_valid_suppress_packaging_warnings_false(self):
test_config = """
[default]
suppress_packaging_warnings = false
"""
self.addCleanup(os.remove, self.file_path)
with open(self.file_path, 'w') as file:
file.write(test_config)
file.flush()
config = user_config.UserConfig(self.file_path)
config.read_config_file()
self.assertEqual(
{'parallel_enabled': user_config.PARALLEL_DEFAULT},
config.settings)

def test_valid_suppress_packaging_warnings_true(self):
test_config = """
[default]
suppress_packaging_warnings = true
"""
self.addCleanup(os.remove, self.file_path)
with open(self.file_path, 'w') as file:
file.write(test_config)
file.flush()
config = user_config.UserConfig(self.file_path)
config.read_config_file()
self.assertEqual(
{'parallel_enabled': user_config.PARALLEL_DEFAULT,
'suppress_packaging_warnings': True},
config.settings)

def test_invalid_num_processes(self):
test_config = """
[default]
Expand Down Expand Up @@ -197,6 +154,5 @@ def test_all_options_valid(self):
'circuit_mpl_style_path': ['~', '~/.qiskit'],
'transpile_optimization_level': 3,
'num_processes': 15,
'parallel_enabled': False,
'suppress_packaging_warnings': True},
'parallel_enabled': False},
config.settings)