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

Optimise import qiskit with lazy imports #7525

Merged
merged 19 commits into from
Feb 2, 2022
Merged

Conversation

jakelishman
Copy link
Member

Summary

  • Speed up import qiskit significantly - 1.17s to 0.57s (2x faster) for a Python environment with almost all the optional dependencies, and 0.78s to 0.52s (33% faster) for a Python environment with only Terra and its requirements.
  • Add a new module qiskit.utils.optionals, with many lazy-evaluation HAS_X variables (e.g. HAS_MATPLOTLIB, HAS_AER), which can be used for lazy testing of optional dependencies, and unified handling of errors if they are not present.

Details and comments

These two commits significantly speed up import qiskit by deferring many imports to their call sites. In particular, no component of Scipy is loaded, nor is pkg_resources (which is surprisingly heavy). I didn't change any scipy imports in algorithms or opflow, because these aren't imported by default, and I thought they might be a bit more work for lesser benefit.

The first commit in this patch also introduces many new HAS_X variables, to unify handling of optional dependencies all the way through the codebase. Previously, many modules were doing ad-hoc testing with

try:
    import optional_dependency
    HAS_OPTIONAL = True
except ImportError:
    HAS_OPTIONAL = False

class MyClass:
    def __init__(self):
        if not HAS_OPTIONAL:
            raise MissingOptionalLibraryError(...)

This form has a couple of downsides:

  1. We try to import the optional dependency at import time, which adds to the weight of import qiskit if the dependency is installed, even if it won't be used.
  2. It clutters many functions with error-handling code, even though they are doing the same thing.
  3. Several modules were repeating the exact same code in a couple of places.

The first commit introduces some new lazy-loader objects (LazyImportTester and LazySubprocessTester), which can be evaluated as Booleans, and will lazily test the presence of the dependencies. This is similar to the old HAS_MATPLOTLIB variable. There is a new module qiskit.utils.optionals that exposes many HAS_X variables of these types. In addition to the lazy Boolean checkers, they also come with unified error handling: modules now can do

@HAS_MATPLOTLIB.require_in_call
def needs_matplotlib(self):
    import matplotlib

def chooses_based_on_argument(self, arg):
    if arg == "text":
        ...
    elif arg == "matplotlib":
        HAS_MATPLOTLIB.require_now("matplotlib drawer")
        import matplotlib
        ...

Both require forms will raise a suitable MissingOptionalLibraryError if the dependency is not available, at the point of call - the decorator raises when the inner function is called, not when the decorator is applied.

Moving the variables into qiskit.utils.optionals is also nice for the test suite - instead of it having to repeat the code or import names from random locations, there's a simple namespace where all the objects exist (with documentation on each one in Sphinx).

Fix #7498, as a side-effect - all the instances where initialisation code was logging are now deferred to their usage sites, so the logs will actually make sense and be useful. You can import every single module defined in Terra without us issuing a log message now.

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.
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.
@jakelishman jakelishman added performance Changelog: New Feature Include in the "Added" section of the changelog labels Jan 13, 2022
@jakelishman jakelishman added this to the 0.20 milestone Jan 13, 2022
@coveralls
Copy link

coveralls commented Jan 13, 2022

Pull Request Test Coverage Report for Build 1783494894

  • 423 of 548 (77.19%) changed or added relevant lines in 54 files are covered.
  • 23 unchanged lines in 13 files lost coverage.
  • Overall coverage increased (+0.2%) to 83.354%

Changes Missing Coverage Covered Lines Changed/Added Lines %
qiskit/opflow/gradients/hessian.py 5 6 83.33%
qiskit/transpiler/passes/routing/algorithms/bip_model.py 4 5 80.0%
qiskit/utils/classtools.py 64 65 98.46%
qiskit/visualization/circuit_visualization.py 5 6 83.33%
qiskit/visualization/gate_map.py 6 7 85.71%
qiskit/visualization/matplotlib.py 4 5 80.0%
qiskit/visualization/pulse/interpolation.py 0 1 0.0%
qiskit/visualization/pulse/matplotlib.py 3 4 75.0%
qiskit/visualization/utils.py 4 5 80.0%
qiskit/algorithms/optimizers/nlopts/nloptimizer.py 2 4 50.0%
Files with Coverage Reduction New Missed Lines %
qiskit/algorithms/optimizers/nlopts/nloptimizer.py 1 40.63%
qiskit/circuit/parameterexpression.py 1 87.4%
qiskit/circuit/qpy_serialization.py 1 87.14%
qiskit/tools/jupyter/init.py 1 0%
qiskit/tools/jupyter/job_watcher.py 1 0%
qiskit/tools/jupyter/progressbar.py 1 0%
qiskit/transpiler/passes/optimization/crosstalk_adaptive_schedule.py 1 9.36%
qiskit/visualization/pass_manager_visualization.py 1 18.84%
qiskit/transpiler/passes/optimization/hoare_opt.py 2 11.61%
qiskit/visualization/pulse/matplotlib.py 2 13.17%
Totals Coverage Status
Change from base Build 1781777612: 0.2%
Covered Lines: 52223
Relevant Lines: 62652

💛 - Coveralls

@woodsp-ibm
Copy link
Member

I guess one downside of the new way, from a user perspective, I write code with a bunch of imports and it all seems fine as it loads and starts running. But instead of it failing fast as it used to, I now find it fails some time later if some optional dependency is not present when it finally goes to construct/uses something. The code may have expended significant computational resources by that point. And I might fix that and run again, go further and hit another missing optional. The fail early that was done before would catch the optional dependence early; but has other considerations that you point out such as loading resources that are never used in practice.

@jakelishman
Copy link
Member Author

This doesn't actually change anything about that - before, all the imports would silently fail immediately, setting (e.g.) _HAS_SKQUANT = False, and then you wouldn't get an error til you instantiated the class. In this form, you still get the error in the exact same place, but you also don't pay the import cost til then either.

@woodsp-ibm
Copy link
Member

Ah right, as you say the behavior in terms of it failing later is unaltered by your change. I know in Aqua we used to indicate the failure to load but guess that was removed as it may have been too noisy if you did not care about installing the optional.

@jakelishman
Copy link
Member Author

Yeah, I don't know when it changed, but it was probably the right call - this PR identified 26 different optional dependencies across Terra alone. The code from Aqua seems to be responsible for about 4 of them, which is a bit more reasonable, but still, they're pretty niche optimisers, and I'd imagine most people wouldn't be bothered. In general, you should get the error when you instantiate the optimiser classes, which is likely going to be one of the first things you do.

Copy link
Member

@mtreinish mtreinish left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome thanks for doing this. The API around managing optional dependencies is so much cleaner here and much easier to manage than the ad-hoc solution we were doing before. It's nice enough that I'm slightly tempted to violate the stable branch policy so we can start using this in downstream qiskit packages sooner rather than later :) That coupled with fixing the stray debug logging and the import time improvements this really nice!

Just have a few questions and comments inline

qiskit/opflow/gradients/gradient.py Outdated Show resolved Hide resolved
qiskit/opflow/gradients/hessian.py Outdated Show resolved Hide resolved
qiskit/opflow/gradients/hessian.py Outdated Show resolved Hide resolved
Comment on lines +1406 to +1411
# This weird duplicated lazy structure is for backwards compatibility; Qiskit has historically
# always made ``two_qubit_cnot_decompose`` available publicly immediately on import, but it's quite
# expensive to construct, and we want to defer the obejct's creation until it's actually used. We
# only need to pass through the public methods that take `self` as a parameter. Using `__getattr__`
# doesn't work because it is only called if the normal resolution methods fail. Using
# `__getattribute__` is too messy for a simple one-off use object.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, too bad I was excited when I opened #7498 that we'd have the first use case for module level getattr now that we're >=3.7 but I guess not. Having this comment here is useful for explaining this I should have added one for the lazy aer and ibmq wrappers

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I forgot that module-level getattr was available now - the getattr I'm referring to here is the class one (i.e. why I defined all the methods manually). I think it might end up a bit convoluted here - it also needs to be reachable from qiskit.quantum_info.synthesis, and we'd need to protect an internal Qiskit import in other places, which would be a bit weird.

qiskit/test/base.py Outdated Show resolved Hide resolved
@@ -79,6 +79,7 @@
RemoveDiagonalGatesBeforeMeasure
RemoveResetInZeroState
CrosstalkAdaptiveSchedule
HoareOptimizer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good catch but kind of an unrelated change.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah, it's because I put in a Sphinx cross-ref to it somewhere (I think in the HAS_Z3 docs?), and it didn't link correctly - it looks totally unrelated, but there was some method in the madness.

qiskit/utils/lazy_tester.py Show resolved Hide resolved
qiskit/utils/optionals.py Outdated Show resolved Hide resolved
@@ -127,11 +127,13 @@
from qiskit.visualization.transition_visualization import visualize_transition
from qiskit.visualization.array import array_to_latex

from .circuit_visualization import circuit_drawer, HAS_PIL, HAS_PDFLATEX, HAS_PDFTOCAIRO
# NOTE (Jake Lishman, 2022-01-12): for backwards compatibility. Deprecate these paths in Terra 0.21.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to not deprecate these now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alternate paths aren't available in Terra 0.19, so we can't warn until the alternative is already in place, and I know that both Ignis and ibmq-provider import HAS_MATPLOTLIB from here (technically qiskit.tools.visualization, but that's just a pass-through to here).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, something is still using qiskit.tools.visualization we made that move like 3 years ago. :)

qiskit/visualization/__init__.py Outdated Show resolved Hide resolved
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.
@jakelishman jakelishman requested a review from mtreinish January 17, 2022 18:29
@jlapeyre
Copy link
Contributor

A couple of lines in CONTRIBUTING.md might be useful. Something about when to add a dependency to requirements.txt and when/how to add it to optionals.py.
(This looks so useful, I'm surprised there's no package for it, or at least how-to for something similar)

@jakelishman
Copy link
Member Author

Added a section into CONTRIBUTING in fd74223.

The dependency-checker classes themselves I suppose could be split off into a separate package, but there would be some additional work around making the error handling nice and extensible there. I think the majority of the benefit comes from defining all the HAS_X variables, and those would still need to be written by each project, as in utils.optionals.

mtreinish
mtreinish previously approved these changes Feb 1, 2022
Copy link
Member

@mtreinish mtreinish left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM. I think everything is covered here. Thanks for doing this. I left a few questions/comments inline but nothing work blocking this over. Some things we can always adjust in follow ups as necessary, especially given the size of this PR it'll be good to merge this sooner rather than later.

qiskit/utils/__init__.py Show resolved Hide resolved
return types.MethodType(self._method, obj)


class _WrappedMethod:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh, I appreciate the thoroughness on this class and the details look good to me. I'm just wondering if these edge cases came up in practice in the use of the class decorator, or this was just to be defensive against something needs them currently. Like I guess I'm wondering if something is trying to use _WrappedMethod on a dict subclass or something.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They come up far more than you'd expect - things like wrapping method from type and object are part of the core usage - wrapping a non-existent __init__ needs to access something from object, and wrapping __init_subclass__ (which we do in the test classes) needs to access something from type. Wrapping classmethod and staticmethod instances I think already happens in what we use it for now.

Using builtins as the wrappers happened when I was designing the class in the interpreter - I tried to use print as a wrapper for a really poor man's debugger.

qiskit/utils/optionals.py Outdated Show resolved Hide resolved
qiskit/utils/optionals.py Outdated Show resolved Hide resolved
@@ -479,7 +476,7 @@ def test_state_hessian(self, method):
state_hess.assign_parameters(value_dict).eval(), correct_values[i], decimal=1
)

@unittest.skipIf(not _HAS_JAX, "Skipping test due to missing jax module.")
@unittest.skipIf(not optionals.HAS_JAX, "Skipping test due to missing jax module.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A potential follow-on, not something we should do in this pr. Is to add a skip test decorator to the lazy importer class. So we can just make this @optionals.HAS_JAX.test_requires or something

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure on that - it would couple the objects to our testing framework, and that's not ideal for something we're publicly exporting.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I was thinking more just having the decorator raise unittest.SkipTest which is in stdlib and while it's specifically part of unittest framework I think all the python testing frameworks support it. But it's not critical and I agree a gray area of whether we want that as part of the api. We can discuss it later if we think it's something worthwhile

Comment on lines +73 to +74
if test_generator():
self.fail("did not evaluate false")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an advantage to this vs L71? This should be equivalent to self.assertFalse(test_generator())

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's a slightly superfluous test - I'm not 100% sure what I was thinking, but I suspect it was along the lines of "check a few possible implicit Boolean contexts". Strictly, all three tests here should be identical, I think. Happy to remove it (and the similar test on line 65 if you'd prefer to reduce noise.

@mtreinish
Copy link
Member

Marking this as on hold until we get all the 0.19.2 PRs backported (just to avoid potential merge conflicts). After we're ready to release 0.19.2 I'll tag this as automerge.

@mtreinish mtreinish added the on hold Can not fix yet label Feb 1, 2022
@mergify mergify bot merged commit 531e62f into Qiskit:main Feb 2, 2022
@jakelishman jakelishman deleted the lazy-import branch February 2, 2022 12:35
ElePT pushed a commit to ElePT/qiskit that referenced this pull request Jun 27, 2023
* 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>
ElePT pushed a commit to ElePT/qiskit-algorithms-test that referenced this pull request Jul 17, 2023
* 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>
ElePT pushed a commit to ElePT/qiskit-algorithms that referenced this pull request Jul 27, 2023
* 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>
ElePT pushed a commit to ElePT/qiskit-ibm-provider that referenced this pull request Oct 9, 2023
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: New Feature Include in the "Added" section of the changelog performance
Projects
None yet
Development

Successfully merging this pull request may close these issues.

DEBUG log messages emitted on imports
5 participants