Skip to content

Commit

Permalink
Optimise import qiskit with lazy imports (Qiskit/qiskit#7525)
Browse files Browse the repository at this point in the history
* Unify lazy handling of optional dependencies

This introduces new `HAS_X` variables for each of Qiskit's optional
dependencies, and provides a simple unified interface to them from
`qiskit.utils.optionals`.  These objects lazily test for their
dependency when evaluated in a Boolean context, and have two `require_`
methods to unify the exception handling.  `require_now` tests
immediately for the dependency and raises `MissingOptionalLibraryError`
if it is not present, and `require_in_call` is a decorator that lazily
tests for the dependencies when the function is called.

These remove the burden of raising nice exceptions from the usage
points; a function marked `HAS_MATPLOTLIB.require_in_call` can now
safely `import matplotlib` without special handling, for example.  This
also provides a unified way for consumers of `qiskit` (such as the test
suite) to query the presence of libraries.

All tests are now lazy, and imports are moved to the point of usage, not
the point of import of the module.  This means that `import qiskit` is
significantly faster for people who have many of the optional
dependencies installed; rather than them all being loaded at initial
import just to test their presence, they will now be loaded on demand.

* Optimise time taken for `import qiskit`

This makes several imports lazy, only being imported when they are
actually called and used.  In particular, no component of `scipy` is
imported during `import qiskit` now, nor is `pkg_resources` (which is
surprisingly heavy).

No changes were made to algorithms or opflow, since these are not
immediately imported during `import qiskit`, and likely require more
significant work than the rest of the library.

* Import missing to-be-deprecated names

* Convert straggler tests to require_now

* Correct requirements in test cases

* Add `require_in_instance` class decorator

Effectively this is just a wrapper around `__init__`, except that this
class-decorator form will do the right thing even if `__init__` isn't
explicitly defined on the given class.

The implementation of `wrap_method` is a replacement for the older
`test.decorators._wrap_method`, which didn't handle all the possible
special cases as well, and messed up the documentation of its wrapped
functions.  That wasn't so important when it was just a private
function, but now it has become public (so that
`test.decorators.enforce_subclasses_call` can still use it from
`qiskit.utils`), it needed reworking to be more polished.

* Privatise non-public names rather than del

* Add tests of `require_in_instance`

* Fix typos in documentation

* Add section on requirements to CONTRIBUTING

* Update documentation on HoareOptimizer error

* Remove UK localisation

* Mention more uses of PIL in documentation

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
jakelishman and mergify[bot] authored Feb 2, 2022
1 parent 08c9ea0 commit aef12f6
Showing 1 changed file with 3 additions and 3 deletions.
6 changes: 3 additions & 3 deletions test/integration/test_fake_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from qiskit.execute_function import execute
from qiskit.test.base import QiskitTestCase
from qiskit.test.mock import FakeProvider, FakeLegacyProvider
from qiskit.test.mock.fake_backend import HAS_AER
from qiskit.utils import optionals


FAKE_PROVIDER = FakeProvider()
Expand All @@ -51,7 +51,7 @@ def setUpClass(cls):
optimization_level=[0, 1, 2, 3],
)
def test_circuit_on_fake_backend(self, backend, optimization_level):
if not HAS_AER and backend.configuration().num_qubits > 20:
if not optionals.HAS_AER and backend.configuration().num_qubits > 20:
self.skipTest(
"Unable to run fake_backend %s without qiskit-aer"
% backend.configuration().backend_name
Expand All @@ -73,7 +73,7 @@ def test_circuit_on_fake_backend(self, backend, optimization_level):
optimization_level=[0, 1, 2, 3],
)
def test_circuit_on_fake_legacy_backend(self, backend, optimization_level):
if not HAS_AER and backend.configuration().num_qubits > 20:
if not optionals.HAS_AER and backend.configuration().num_qubits > 20:
self.skipTest(
"Unable to run fake_backend %s without qiskit-aer"
% backend.configuration().backend_name
Expand Down

0 comments on commit aef12f6

Please sign in to comment.