Skip to content

Commit 0e4ad97

Browse files
authored
feat: implement lazy loading for optional dependencies (#621)
1 parent a1b09f3 commit 0e4ad97

File tree

8 files changed

+83
-136
lines changed

8 files changed

+83
-136
lines changed

src/optimagic/config.py

Lines changed: 20 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import importlib.util
12
from pathlib import Path
23

34
import pandas as pd
@@ -15,105 +16,29 @@
1516
CRITERION_PENALTY_SLOPE = 0.1
1617
CRITERION_PENALTY_CONSTANT = 100
1718

18-
# ======================================================================================
19-
# Check Available Packages
20-
# ======================================================================================
21-
22-
try:
23-
from petsc4py import PETSc # noqa: F401
24-
except ImportError:
25-
IS_PETSC4PY_INSTALLED = False
26-
else:
27-
IS_PETSC4PY_INSTALLED = True
28-
29-
try:
30-
import nlopt # noqa: F401
31-
except ImportError:
32-
IS_NLOPT_INSTALLED = False
33-
else:
34-
IS_NLOPT_INSTALLED = True
35-
36-
try:
37-
import pybobyqa # noqa: F401
38-
except ImportError:
39-
IS_PYBOBYQA_INSTALLED = False
40-
else:
41-
IS_PYBOBYQA_INSTALLED = True
42-
43-
try:
44-
import dfols # noqa: F401
45-
except ImportError:
46-
IS_DFOLS_INSTALLED = False
47-
else:
48-
IS_DFOLS_INSTALLED = True
49-
50-
try:
51-
import pygmo # noqa: F401
52-
except ImportError:
53-
IS_PYGMO_INSTALLED = False
54-
else:
55-
IS_PYGMO_INSTALLED = True
56-
57-
try:
58-
import cyipopt # noqa: F401
59-
except ImportError:
60-
IS_CYIPOPT_INSTALLED = False
61-
else:
62-
IS_CYIPOPT_INSTALLED = True
63-
64-
try:
65-
import fides # noqa: F401
66-
except ImportError:
67-
IS_FIDES_INSTALLED = False
68-
else:
69-
IS_FIDES_INSTALLED = True
7019

71-
try:
72-
import jax # noqa: F401
73-
except ImportError:
74-
IS_JAX_INSTALLED = False
75-
else:
76-
IS_JAX_INSTALLED = True
20+
def _is_installed(module_name: str) -> bool:
21+
"""Return True if the given module is installed, otherwise False."""
22+
return importlib.util.find_spec(module_name) is not None
7723

7824

79-
try:
80-
import tranquilo # noqa: F401
81-
except ImportError:
82-
IS_TRANQUILO_INSTALLED = False
83-
else:
84-
IS_TRANQUILO_INSTALLED = True
85-
86-
87-
try:
88-
import numba # noqa: F401
89-
except ImportError:
90-
IS_NUMBA_INSTALLED = False
91-
else:
92-
IS_NUMBA_INSTALLED = True
93-
94-
95-
try:
96-
import iminuit # noqa: F401
97-
except ImportError:
98-
IS_IMINUIT_INSTALLED = False
99-
else:
100-
IS_IMINUIT_INSTALLED = True
101-
102-
103-
try:
104-
import nevergrad # noqa: F401
105-
except ImportError:
106-
IS_NEVERGRAD_INSTALLED = False
107-
else:
108-
IS_NEVERGRAD_INSTALLED = True
109-
25+
# ======================================================================================
26+
# Check Available Packages
27+
# ======================================================================================
11028

111-
try:
112-
from bayes_opt import BayesianOptimization # noqa: F401
113-
except ImportError:
114-
IS_BAYESOPT_INSTALLED = False
115-
else:
116-
IS_BAYESOPT_INSTALLED = True
29+
IS_PETSC4PY_INSTALLED = _is_installed("petsc4py")
30+
IS_NLOPT_INSTALLED = _is_installed("nlopt")
31+
IS_PYBOBYQA_INSTALLED = _is_installed("pybobyqa")
32+
IS_DFOLS_INSTALLED = _is_installed("dfols")
33+
IS_PYGMO_INSTALLED = _is_installed("pygmo")
34+
IS_CYIPOPT_INSTALLED = _is_installed("cyipopt")
35+
IS_FIDES_INSTALLED = _is_installed("fides")
36+
IS_JAX_INSTALLED = _is_installed("jax")
37+
IS_TRANQUILO_INSTALLED = _is_installed("tranquilo")
38+
IS_NUMBA_INSTALLED = _is_installed("numba")
39+
IS_IMINUIT_INSTALLED = _is_installed("iminuit")
40+
IS_NEVERGRAD_INSTALLED = _is_installed("nevergrad")
41+
IS_BAYESOPT_INSTALLED = _is_installed("bayes_opt")
11742

11843

11944
# ======================================================================================

src/optimagic/optimizers/fides.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@
2929
PositiveInt,
3030
)
3131

32-
if IS_FIDES_INSTALLED:
33-
from fides import Optimizer, hessian_approximation
34-
3532

3633
@mark.minimizer(
3734
name="fides",
@@ -163,6 +160,8 @@ def fides_internal(
163160
"You can install it with `pip install fides>=0.7.4`."
164161
)
165162

163+
from fides import Optimizer
164+
166165
fides_options = {
167166
"delta_init": trustregion_initial_radius,
168167
"eta": trustregion_increase_threshold,
@@ -251,6 +250,8 @@ def _process_exitflag(exitflag):
251250

252251

253252
def _create_hessian_updater_from_user_input(hessian_update_strategy):
253+
from fides import hessian_approximation
254+
254255
hessians_needing_residuals = (
255256
hessian_approximation.FX,
256257
hessian_approximation.SSM,

src/optimagic/optimizers/ipopt.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,6 @@
3030
YesNoBool,
3131
)
3232

33-
if IS_CYIPOPT_INSTALLED:
34-
import cyipopt
35-
3633

3734
@mark.minimizer(
3835
name="ipopt",
@@ -349,11 +346,14 @@ class Ipopt(Algorithm):
349346
def _solve_internal_problem(
350347
self, problem: InternalOptimizationProblem, x0: NDArray[np.float64]
351348
) -> InternalOptimizeResult:
352-
if not self.algo_info.is_available:
349+
if not IS_CYIPOPT_INSTALLED:
353350
raise NotInstalledError(
354-
"The 'ipopt' algorithm requires the cyipopt package to be installed. "
355-
"You can it with: `conda install -c conda-forge cyipopt`."
351+
"The 'ipopt' algorithm requires the cyipopt package to be installed.\n"
352+
"You can install it with: `conda install -c conda-forge cyipopt`."
356353
)
354+
355+
import cyipopt
356+
357357
if self.acceptable_tol <= self.convergence_ftol_rel:
358358
raise ValueError(
359359
"The acceptable tolerance must be larger than the desired tolerance."

src/optimagic/optimizers/nag_optimizers.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,6 @@
3131
)
3232
from optimagic.utilities import calculate_trustregion_initial_radius
3333

34-
if IS_PYBOBYQA_INSTALLED:
35-
import pybobyqa
36-
37-
if IS_DFOLS_INSTALLED:
38-
import dfols
39-
40-
4134
CONVERGENCE_MINIMAL_TRUSTREGION_RADIUS_TOLERANCE = 1e-8
4235
"""float: Stop when the lower trust region radius falls below this value."""
4336

@@ -486,6 +479,8 @@ def nag_dfols_internal(
486479
"For additional installation instructions visit: ",
487480
r"https://numericalalgorithmsgroup.github.io/dfols/build/html/install.html",
488481
)
482+
import dfols
483+
489484
if trustregion_method_to_replace_extra_points == "momentum":
490485
trustregion_use_momentum = True
491486
elif trustregion_method_to_replace_extra_points in ["geometry_improving", None]:
@@ -788,6 +783,7 @@ def nag_pybobyqa_internal(
788783
r"https://numericalalgorithmsgroup.github.io/pybobyqa/build/html/"
789784
"install.html",
790785
)
786+
import pybobyqa
791787

792788
if convergence_criterion_value is None:
793789
convergence_criterion_value = -np.inf

src/optimagic/optimizers/nevergrad_optimizers.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import math
44
from dataclasses import dataclass
5-
from typing import Any, Literal
5+
from typing import TYPE_CHECKING, Any, Literal
66

77
import numpy as np
88
from numpy.typing import NDArray
@@ -29,7 +29,7 @@
2929
PositiveInt,
3030
)
3131

32-
if IS_NEVERGRAD_INSTALLED:
32+
if TYPE_CHECKING:
3333
import nevergrad as ng
3434

3535

@@ -77,6 +77,8 @@ def _solve_internal_problem(
7777
if not IS_NEVERGRAD_INSTALLED:
7878
raise NotInstalledError(NEVERGRAD_NOT_INSTALLED_ERROR)
7979

80+
import nevergrad as ng
81+
8082
configured_optimizer = ng.optimizers.ConfPSO(
8183
transform=self.transform,
8284
popsize=self.population_size,
@@ -160,6 +162,8 @@ def _solve_internal_problem(
160162
if not IS_NEVERGRAD_INSTALLED:
161163
raise NotInstalledError(NEVERGRAD_NOT_INSTALLED_ERROR)
162164

165+
import nevergrad as ng
166+
163167
cma_options = {
164168
"AdaptSigma": self.step_size_adaptive,
165169
"CSA_dampfac": self.CSA_dampfac,
@@ -285,6 +289,8 @@ def _solve_internal_problem(
285289
if not IS_NEVERGRAD_INSTALLED:
286290
raise NotInstalledError(NEVERGRAD_NOT_INSTALLED_ERROR)
287291

292+
import nevergrad as ng
293+
288294
configured_optimizer = ng.optimizers.ParametrizedOnePlusOne(
289295
noise_handling=self.noise_handling,
290296
mutation=self.mutation,
@@ -363,6 +369,8 @@ def _solve_internal_problem(
363369
if not IS_NEVERGRAD_INSTALLED:
364370
raise NotInstalledError(NEVERGRAD_NOT_INSTALLED_ERROR)
365371

372+
import nevergrad as ng
373+
366374
configured_optimizer = ng.optimizers.DifferentialEvolution(
367375
scale=self.scale,
368376
recommendation=self.recommendation,
@@ -418,6 +426,8 @@ def _solve_internal_problem(
418426
if not IS_NEVERGRAD_INSTALLED:
419427
raise NotInstalledError(NEVERGRAD_NOT_INSTALLED_ERROR)
420428

429+
import nevergrad as ng
430+
421431
configured_optimizer = ng.optimizers.BayesOptim(
422432
init_budget=self.init_budget,
423433
pca=self.pca,
@@ -470,6 +480,8 @@ def _solve_internal_problem(
470480
if not IS_NEVERGRAD_INSTALLED:
471481
raise NotInstalledError(NEVERGRAD_NOT_INSTALLED_ERROR)
472482

483+
import nevergrad as ng
484+
473485
configured_optimizer = ng.optimizers.EMNA(
474486
isotropic=self.isotropic,
475487
naive=self.noise_handling,
@@ -518,6 +530,8 @@ def _solve_internal_problem(
518530
if not IS_NEVERGRAD_INSTALLED:
519531
raise NotInstalledError(NEVERGRAD_NOT_INSTALLED_ERROR)
520532

533+
import nevergrad as ng
534+
521535
configured_optimizer = ng.optimizers.cGA
522536

523537
res = _nevergrad_internal(
@@ -561,6 +575,8 @@ def _solve_internal_problem(
561575
if not IS_NEVERGRAD_INSTALLED:
562576
raise NotInstalledError(NEVERGRAD_NOT_INSTALLED_ERROR)
563577

578+
import nevergrad as ng
579+
564580
configured_optimizer = ng.optimizers.EDA
565581

566582
res = _nevergrad_internal(
@@ -606,6 +622,8 @@ def _solve_internal_problem(
606622
if not IS_NEVERGRAD_INSTALLED:
607623
raise NotInstalledError(NEVERGRAD_NOT_INSTALLED_ERROR)
608624

625+
import nevergrad as ng
626+
609627
configured_optimizer = ng.optimizers.ParametrizedTBPSA(
610628
naive=self.noise_handling,
611629
initial_popsize=self.initial_popsize,
@@ -658,6 +676,8 @@ def _solve_internal_problem(
658676
if not IS_NEVERGRAD_INSTALLED:
659677
raise NotInstalledError(NEVERGRAD_NOT_INSTALLED_ERROR)
660678

679+
import nevergrad as ng
680+
661681
configured_optimizer = ng.optimizers.RandomSearchMaker(
662682
stupid=False,
663683
middle_point=self.middle_point,
@@ -715,6 +735,8 @@ def _solve_internal_problem(
715735
if not IS_NEVERGRAD_INSTALLED:
716736
raise NotInstalledError(NEVERGRAD_NOT_INSTALLED_ERROR)
717737

738+
import nevergrad as ng
739+
718740
configured_optimizer = ng.optimizers.SamplingSearch(
719741
sampler=self.sampler,
720742
scrambled=self.scrambled,
@@ -820,6 +842,8 @@ def _solve_internal_problem(
820842
if not IS_NEVERGRAD_INSTALLED:
821843
raise NotInstalledError(NEVERGRAD_NOT_INSTALLED_ERROR)
822844

845+
import nevergrad as ng
846+
823847
configured_optimizer = getattr(ng.optimizers, self.optimizer)
824848

825849
res = _nevergrad_internal(
@@ -903,6 +927,8 @@ def _solve_internal_problem(
903927
if not IS_NEVERGRAD_INSTALLED:
904928
raise NotInstalledError(NEVERGRAD_NOT_INSTALLED_ERROR)
905929

930+
import nevergrad as ng
931+
906932
configured_optimizer = getattr(ng.optimizers, self.optimizer)
907933

908934
res = _nevergrad_internal(
@@ -948,6 +974,8 @@ def _nevergrad_internal(
948974
949975
"""
950976

977+
import nevergrad as ng
978+
951979
param = ng.p.Array(
952980
init=x0,
953981
)

0 commit comments

Comments
 (0)