Repository: Qiskit/qiskit

Issue:
Fix import time regression for 1.0.0
### What should we add?

In the lead up to qiskit 1.0.0rc1 a noticeable import time regression got merged as part of some of the internal code refactoring. This was flagged by asv:

 https://qiskit.github.io/qiskit/#import.QiskitImport.time_qiskit_import
 
(Ignore the big runtime improvement at: 151fb799 that was caused by an asv environment issue and the import failing)

It looks like this is caused by scipy being imported as part of `qiskit.synthesis` which is part of the base `import qiskit`. Prior to the final 1.0.0 we should fix this.


Discussion:

Resolving patch:
diff --git a/qiskit/quantum_info/operators/operator.py b/qiskit/quantum_info/operators/operator.py
--- a/qiskit/quantum_info/operators/operator.py
+++ b/qiskit/quantum_info/operators/operator.py
@@ -21,7 +21,6 @@
 from numbers import Number
 from typing import TYPE_CHECKING
 
-import scipy.linalg
 import numpy as np
 
 from qiskit.circuit.instruction import Instruction
@@ -528,6 +527,8 @@ def power(self, n: float) -> Operator:
         if isinstance(n, int):
             ret._data = np.linalg.matrix_power(self.data, n)
         else:
+            import scipy.linalg
+
             # Experimentally, for fractional powers this seems to be 3x faster than
             # calling scipy.linalg.fractional_matrix_power(self.data, n)
             decomposition, unitary = scipy.linalg.schur(self.data, output="complex")
diff --git a/qiskit/synthesis/unitary/aqc/aqc.py b/qiskit/synthesis/unitary/aqc/aqc.py
--- a/qiskit/synthesis/unitary/aqc/aqc.py
+++ b/qiskit/synthesis/unitary/aqc/aqc.py
@@ -12,18 +12,21 @@
 """A generic implementation of Approximate Quantum Compiler."""
 from __future__ import annotations
 
+import typing
 from functools import partial
 
 from collections.abc import Callable
 from typing import Protocol
 
 import numpy as np
-from scipy.optimize import OptimizeResult, minimize
 
 from qiskit.quantum_info import Operator
 
 from .approximate import ApproximateCircuit, ApproximatingObjective
 
+if typing.TYPE_CHECKING:
+    import scipy.optimize
+
 
 class Minimizer(Protocol):
     """Callable Protocol for minimizer.
@@ -52,7 +55,7 @@ def __call__(
         x0: np.ndarray,  # pylint: disable=invalid-name
         jac: Callable[[np.ndarray], np.ndarray] | None = None,
         bounds: list[tuple[float, float]] | None = None,
-    ) -> OptimizeResult:
+    ) -> scipy.optimize.OptimizeResult:
         """Minimize the objective function.
 
         This interface is based on `SciPy's optimize module <https://docs.scipy.org/doc
@@ -112,9 +115,11 @@ def __init__(
                 ``L-BFGS-B`` method is used with max iterations set to 1000.
             seed: a seed value to be used by a random number generator.
         """
+        import scipy.optimize
+
         super().__init__()
         self._optimizer = optimizer or partial(
-            minimize, args=(), method="L-BFGS-B", options={"maxiter": 1000}
+            scipy.optimize.minimize, args=(), method="L-BFGS-B", options={"maxiter": 1000}
         )
 
         self._seed = seed
diff --git a/tools/find_optional_imports.py b/tools/find_optional_imports.py
--- a/tools/find_optional_imports.py
+++ b/tools/find_optional_imports.py
@@ -27,6 +27,8 @@ def _main():
         "pydot",
         "pygments",
         "ipywidgets",
+        "scipy.linalg",
+        "scipy.optimize",
         "scipy.stats",
         "matplotlib",
         "qiskit_aer",


Test patch: