From d16c36fa0bf75d4305c3e67343acf074357e7a2e Mon Sep 17 00:00:00 2001 From: Janos Gabler Date: Tue, 12 Nov 2024 16:53:21 +0100 Subject: [PATCH 1/8] Better string representation for algorithm classes. --- .tools/create_algo_selection_code.py | 12 +++- src/optimagic/algorithms.py | 12 +++- src/optimagic/optimization/algorithm.py | 34 ++++++++++- src/optimagic/visualization/history_plots.py | 61 ++++++++++++++------ 4 files changed, 96 insertions(+), 23 deletions(-) diff --git a/.tools/create_algo_selection_code.py b/.tools/create_algo_selection_code.py index 6d28fde6d..29c0b4983 100644 --- a/.tools/create_algo_selection_code.py +++ b/.tools/create_algo_selection_code.py @@ -389,11 +389,19 @@ def _available(self) -> list[Type[Algorithm]]: ] @property - def All(self) -> list[str]: + def All(self) -> list[Type[Algorithm]]: + return self._all() + + @property + def Available(self) -> list[Type[Algorithm]]: + return self._available() + + @property + def AllNames(self) -> list[str]: return [a.__algo_info__.name for a in self._all()] # type: ignore @property - def Available(self) -> list[str]: + def AvailableNames(self) -> list[str]: return [a.__algo_info__.name for a in self._available()] # type: ignore @property diff --git a/src/optimagic/algorithms.py b/src/optimagic/algorithms.py index 748189786..4341744b4 100644 --- a/src/optimagic/algorithms.py +++ b/src/optimagic/algorithms.py @@ -94,11 +94,19 @@ def _available(self) -> list[Type[Algorithm]]: ] @property - def All(self) -> list[str]: + def All(self) -> list[Type[Algorithm]]: + return self._all() + + @property + def Available(self) -> list[Type[Algorithm]]: + return self._available() + + @property + def AllNames(self) -> list[str]: return [a.__algo_info__.name for a in self._all()] # type: ignore @property - def Available(self) -> list[str]: + def AvailableNames(self) -> list[str]: return [a.__algo_info__.name for a in self._available()] # type: ignore @property diff --git a/src/optimagic/optimization/algorithm.py b/src/optimagic/optimization/algorithm.py index 3bef1d09f..7f776cf90 100644 --- a/src/optimagic/optimization/algorithm.py +++ b/src/optimagic/optimization/algorithm.py @@ -1,6 +1,6 @@ import typing import warnings -from abc import ABC, abstractmethod +from abc import ABC, ABCMeta, abstractmethod from dataclasses import dataclass, replace from typing import Any @@ -143,8 +143,38 @@ def __post_init__(self) -> None: raise TypeError(msg) +class AlgorithmMeta(ABCMeta): + """Metaclass to get repr, algo_info and name for classes, not just instances.""" + + def __repr__(self) -> str: + if hasattr(self, "__algo_info__") and self.__algo_info__ is not None: + out = f"om.algos.{self.__algo_info__.name}" + else: + out = self.__class__.__name__ + return out + + @property + def name(self) -> str: + if hasattr(self, "__algo_info__") and self.__algo_info__ is not None: + out = self.__algo_info__.name + else: + out = self.__class__.__name__ + return out + + @property + def algo_info(self) -> AlgoInfo: + if not hasattr(self, "__algo_info__") or self.__algo_info__ is None: + msg = ( + f"The algorithm {self.name} does not have have the __algo_info__ " + "attribute. Use the `mark.minimizer` decorator to add this attribute." + ) + raise AttributeError(msg) + + return self.__algo_info__ + + @dataclass(frozen=True) -class Algorithm(ABC): +class Algorithm(ABC, metaclass=AlgorithmMeta): @abstractmethod def _solve_internal_problem( self, problem: InternalOptimizationProblem, x0: NDArray[np.float64] diff --git a/src/optimagic/visualization/history_plots.py b/src/optimagic/visualization/history_plots.py index 68a538638..1d2247e71 100644 --- a/src/optimagic/visualization/history_plots.py +++ b/src/optimagic/visualization/history_plots.py @@ -1,5 +1,7 @@ +import inspect import itertools from pathlib import Path +from typing import Any import numpy as np import plotly.graph_objects as go @@ -7,6 +9,7 @@ from optimagic.config import PLOTLY_PALETTE, PLOTLY_TEMPLATE from optimagic.logging.logger import LogReader, SQLiteLogOptions +from optimagic.optimization.algorithm import Algorithm from optimagic.optimization.history_tools import get_history_arrays from optimagic.optimization.optimize_result import OptimizeResult from optimagic.parameters.tree_registry import get_registry @@ -50,23 +53,7 @@ def criterion_plot( # Process inputs # ================================================================================== - if not isinstance(names, list) and names is not None: - names = [names] - - if not isinstance(results, dict): - if isinstance(results, list): - names = range(len(results)) if names is None else names - if len(names) != len(results): - raise ValueError("len(results) needs to be equal to len(names).") - results = dict(zip(names, results, strict=False)) - else: - name = 0 if names is None else names - if isinstance(name, list): - if len(name) > 1: - raise ValueError("len(results) needs to be equal to len(names).") - else: - name = name[0] - results = {name: results} + results = _harmonize_inputs_to_dict(results, names) if not isinstance(palette, list): palette = [palette] @@ -180,6 +167,46 @@ def criterion_plot( return fig +def _harmonize_inputs_to_dict(results, names): + """Convert all valid inputs for results and names to dict[str, OptimizeResult].""" + # convert scalar case to list case + if not isinstance(names, list) and names is not None: + names = [names] + + if isinstance(results, OptimizeResult): + results = [results] + + if names is not None and len(names) != len(results): + raise ValueError("len(results) needs to be equal to len(names).") + + # handle dict case + if isinstance(results, dict): + if names is not None: + results_dict = dict(zip(names, results, strict=False)) + else: + results_dict = results + + # unlabeled iterable of results + else: + names = range(len(results)) if names is None else names + results_dict = dict(zip(names, results, strict=False)) + + # convert keys to strings + results_dict = {_convert_key_to_str(k): v for k, v in results_dict.items()} + + return results_dict + + +def _convert_key_to_str(key: Any) -> str: + if inspect.isclass(key) and issubclass(key, Algorithm): + out = key.__algo_info__.name # type: ignore + elif isinstance(key, Algorithm): + out = key.__algo_info__.name # type: ignore + else: + out = str(key) + return out + + def params_plot( result, selector=None, From 1dcb6429e36606d3e0fea0e0efbe6456d9798fb0 Mon Sep 17 00:00:00 2001 From: Janos Gabler Date: Tue, 12 Nov 2024 19:10:18 +0100 Subject: [PATCH 2/8] Do not use .__algo_info__ when .algo_info is available. --- .tools/create_algo_selection_code.py | 33 +++++++++---------- src/optimagic/algorithms.py | 13 +++----- src/optimagic/visualization/history_plots.py | 4 +-- .../optimization/test_history_collection.py | 2 +- .../optimization/test_many_algorithms.py | 2 +- .../test_with_nonlinear_constraints.py | 2 +- 6 files changed, 25 insertions(+), 31 deletions(-) diff --git a/.tools/create_algo_selection_code.py b/.tools/create_algo_selection_code.py index 29c0b4983..07ae8ae7f 100644 --- a/.tools/create_algo_selection_code.py +++ b/.tools/create_algo_selection_code.py @@ -109,7 +109,7 @@ def _get_algorithms_in_module(module: ModuleType) -> dict[str, Type[Algorithm]]: } algos = {} for candidate in candidate_dict.values(): - name = candidate.__algo_info__.name + name = candidate.algo_info.name if issubclass(candidate, Algorithm) and candidate is not Algorithm: algos[name] = candidate return algos @@ -119,7 +119,7 @@ def _get_algorithms_in_module(module: ModuleType) -> dict[str, Type[Algorithm]]: # Functions to filter algorithms by selectors # ====================================================================================== def _is_gradient_based(algo: Type[Algorithm]) -> bool: - return algo.__algo_info__.needs_jac # type: ignore + return algo.algo_info.needs_jac def _is_gradient_free(algo: Type[Algorithm]) -> bool: @@ -127,7 +127,7 @@ def _is_gradient_free(algo: Type[Algorithm]) -> bool: def _is_global(algo: Type[Algorithm]) -> bool: - return algo.__algo_info__.is_global # type: ignore + return algo.algo_info.is_global def _is_local(algo: Type[Algorithm]) -> bool: @@ -135,31 +135,31 @@ def _is_local(algo: Type[Algorithm]) -> bool: def _is_bounded(algo: Type[Algorithm]) -> bool: - return algo.__algo_info__.supports_bounds # type: ignore + return algo.algo_info.supports_bounds def _is_linear_constrained(algo: Type[Algorithm]) -> bool: - return algo.__algo_info__.supports_linear_constraints # type: ignore + return algo.algo_info.supports_linear_constraints def _is_nonlinear_constrained(algo: Type[Algorithm]) -> bool: - return algo.__algo_info__.supports_nonlinear_constraints # type: ignore + return algo.algo_info.supports_nonlinear_constraints def _is_scalar(algo: Type[Algorithm]) -> bool: - return algo.__algo_info__.solver_type == AggregationLevel.SCALAR # type: ignore + return algo.algo_info.solver_type == AggregationLevel.SCALAR def _is_least_squares(algo: Type[Algorithm]) -> bool: - return algo.__algo_info__.solver_type == AggregationLevel.LEAST_SQUARES # type: ignore + return algo.algo_info.solver_type == AggregationLevel.LEAST_SQUARES def _is_likelihood(algo: Type[Algorithm]) -> bool: - return algo.__algo_info__.solver_type == AggregationLevel.LIKELIHOOD # type: ignore + return algo.algo_info.solver_type == AggregationLevel.LIKELIHOOD def _is_parallel(algo: Type[Algorithm]) -> bool: - return algo.__algo_info__.supports_parallelism # type: ignore + return algo.algo_info.supports_parallelism def _get_filters() -> dict[str, Callable[[Type[Algorithm]], bool]]: @@ -385,7 +385,7 @@ def _all(self) -> list[Type[Algorithm]]: def _available(self) -> list[Type[Algorithm]]: _all = self._all() return [ - a for a in _all if a.__algo_info__.is_available # type: ignore + a for a in _all if a.algo_info.is_available # type: ignore ] @property @@ -398,22 +398,19 @@ def Available(self) -> list[Type[Algorithm]]: @property def AllNames(self) -> list[str]: - return [a.__algo_info__.name for a in self._all()] # type: ignore + return [str(a.name) for a in self._all()] @property def AvailableNames(self) -> list[str]: - return [a.__algo_info__.name for a in self._available()] # type: ignore + return [str(a.name) for a in self._available()] @property def _all_algorithms_dict(self) -> dict[str, Type[Algorithm]]: - return {a.__algo_info__.name: a for a in self._all()} # type: ignore + return {str(a.name): a for a in self._all()} @property def _available_algorithms_dict(self) -> dict[str, Type[Algorithm]]: - return { - a.__algo_info__.name: a # type: ignore - for a in self._available() - } + return {str(a.name): a for a in self._available()} """) return out diff --git a/src/optimagic/algorithms.py b/src/optimagic/algorithms.py index 4341744b4..a892f5a51 100644 --- a/src/optimagic/algorithms.py +++ b/src/optimagic/algorithms.py @@ -90,7 +90,7 @@ def _available(self) -> list[Type[Algorithm]]: return [ a for a in _all - if a.__algo_info__.is_available # type: ignore + if a.algo_info.is_available # type: ignore ] @property @@ -103,22 +103,19 @@ def Available(self) -> list[Type[Algorithm]]: @property def AllNames(self) -> list[str]: - return [a.__algo_info__.name for a in self._all()] # type: ignore + return [str(a.name) for a in self._all()] @property def AvailableNames(self) -> list[str]: - return [a.__algo_info__.name for a in self._available()] # type: ignore + return [str(a.name) for a in self._available()] @property def _all_algorithms_dict(self) -> dict[str, Type[Algorithm]]: - return {a.__algo_info__.name: a for a in self._all()} # type: ignore + return {str(a.name): a for a in self._all()} @property def _available_algorithms_dict(self) -> dict[str, Type[Algorithm]]: - return { - a.__algo_info__.name: a # type: ignore - for a in self._available() - } + return {str(a.name): a for a in self._available()} @dataclass(frozen=True) diff --git a/src/optimagic/visualization/history_plots.py b/src/optimagic/visualization/history_plots.py index 1d2247e71..50b3f09e9 100644 --- a/src/optimagic/visualization/history_plots.py +++ b/src/optimagic/visualization/history_plots.py @@ -199,9 +199,9 @@ def _harmonize_inputs_to_dict(results, names): def _convert_key_to_str(key: Any) -> str: if inspect.isclass(key) and issubclass(key, Algorithm): - out = key.__algo_info__.name # type: ignore + out = str(key.name) elif isinstance(key, Algorithm): - out = key.__algo_info__.name # type: ignore + out = str(key.name) else: out = str(key) return out diff --git a/tests/optimagic/optimization/test_history_collection.py b/tests/optimagic/optimization/test_history_collection.py index b94d097db..743b8cf43 100644 --- a/tests/optimagic/optimization/test_history_collection.py +++ b/tests/optimagic/optimization/test_history_collection.py @@ -17,7 +17,7 @@ OPTIMIZERS = [] BOUNDED = [] for name, algo in AVAILABLE_ALGORITHMS.items(): - info = algo.__algo_info__ + info = algo.algo_info if not info.disable_history: if info.supports_parallelism: OPTIMIZERS.append(name) diff --git a/tests/optimagic/optimization/test_many_algorithms.py b/tests/optimagic/optimization/test_many_algorithms.py index 1882aeb08..47fbe7553 100644 --- a/tests/optimagic/optimization/test_many_algorithms.py +++ b/tests/optimagic/optimization/test_many_algorithms.py @@ -28,7 +28,7 @@ BOUNDED_ALGORITHMS = [] for name, algo in LOCAL_ALGORITHMS.items(): - if algo.__algo_info__.supports_bounds: + if algo.algo_info.supports_bounds: BOUNDED_ALGORITHMS.append(name) diff --git a/tests/optimagic/optimization/test_with_nonlinear_constraints.py b/tests/optimagic/optimization/test_with_nonlinear_constraints.py index 1df8bef3a..baaef2f8f 100644 --- a/tests/optimagic/optimization/test_with_nonlinear_constraints.py +++ b/tests/optimagic/optimization/test_with_nonlinear_constraints.py @@ -121,7 +121,7 @@ def test_nonlinear_optimization(nlc_2d_example, algorithm, constr_type): warnings.simplefilter("ignore") result = maximize(algorithm=algorithm, **kwargs[constr_type]) - if NLC_ALGORITHMS[algorithm].__algo_info__.is_global: + if NLC_ALGORITHMS[algorithm].algo_info.is_global: decimal = 0 else: decimal = 4 From 79217499327bb4c42cfef304d65db0a0f088bca1 Mon Sep 17 00:00:00 2001 From: Janos Gabler Date: Tue, 12 Nov 2024 19:24:26 +0100 Subject: [PATCH 3/8] Update changelog. --- CHANGES.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5d6f8fc09..0aa867272 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,9 +4,27 @@ This is a record of all past optimagic releases and what went into them in rever chronological order. We follow [semantic versioning](https://semver.org/) and all releases are available on [Anaconda.org](https://anaconda.org/optimagic-dev/optimagic). -Following the [scientific python guidelines](https://scientific-python.org/specs/spec-0000/) -we drop the official support for Python 3.9. +## 0.5.1 + +This is a minor release that introduces the new algorithm selection tool and several +small improvements. + +To learn more about the algorithm selection feature check out the following resources: + +- [How to specify and configure algorithms](https://optimagic.readthedocs.io/en/latest/how_to/how_to_specify_algorithm_and_algo_options.html) +- [How to select local optimizers](https://optimagic.readthedocs.io/en/latest/how_to/how_to_algorithm_selection.html) + +- {gh}`549` Add support for Python 3.13 ({ghuser}`timmens`) +- {gh}`550` and {gh}`534` implement the new algorithm selection tool ({ghuser}`janosg`) +- {gh}`548` and {gh}`531` improve the documentation ({ghuser}`ChristianZimpelmann`) +- {gh}`544` Adjusts the results processing of the nag optimizers to be compatible + with the latest releases ({ghuser}`timmens`) +- {gh}`543` Adds support for numpy 2.x ({ghuser}`timmens`) +- {gh}`536` Adds a how-to guide for choosing local optimizers ({ghuser}`mpetrosian`) +- {gh}`535` Allows algorithm classes and instances in estimation functions + ({ghuser}`timmens`) +- {gh}`532` Makes several small improvements to the documentation. ## 0.5.0 From 5d4f5e4e20eedb44ffa53720d86ce5db2fcf4b3b Mon Sep 17 00:00:00 2001 From: Janos Gabler Date: Tue, 12 Nov 2024 19:29:45 +0100 Subject: [PATCH 4/8] Fix mypy? --- .tools/create_algo_selection_code.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.tools/create_algo_selection_code.py b/.tools/create_algo_selection_code.py index 07ae8ae7f..17e79989f 100644 --- a/.tools/create_algo_selection_code.py +++ b/.tools/create_algo_selection_code.py @@ -119,7 +119,7 @@ def _get_algorithms_in_module(module: ModuleType) -> dict[str, Type[Algorithm]]: # Functions to filter algorithms by selectors # ====================================================================================== def _is_gradient_based(algo: Type[Algorithm]) -> bool: - return algo.algo_info.needs_jac + return algo.algo_info.needs_jac # type: ignore def _is_gradient_free(algo: Type[Algorithm]) -> bool: @@ -127,7 +127,7 @@ def _is_gradient_free(algo: Type[Algorithm]) -> bool: def _is_global(algo: Type[Algorithm]) -> bool: - return algo.algo_info.is_global + return algo.algo_info.is_global # type: ignore def _is_local(algo: Type[Algorithm]) -> bool: @@ -135,31 +135,31 @@ def _is_local(algo: Type[Algorithm]) -> bool: def _is_bounded(algo: Type[Algorithm]) -> bool: - return algo.algo_info.supports_bounds + return algo.algo_info.supports_bounds # type: ignore def _is_linear_constrained(algo: Type[Algorithm]) -> bool: - return algo.algo_info.supports_linear_constraints + return algo.algo_info.supports_linear_constraints # type: ignore def _is_nonlinear_constrained(algo: Type[Algorithm]) -> bool: - return algo.algo_info.supports_nonlinear_constraints + return algo.algo_info.supports_nonlinear_constraints # type: ignore def _is_scalar(algo: Type[Algorithm]) -> bool: - return algo.algo_info.solver_type == AggregationLevel.SCALAR + return algo.algo_info.solver_type == AggregationLevel.SCALAR # type: ignore def _is_least_squares(algo: Type[Algorithm]) -> bool: - return algo.algo_info.solver_type == AggregationLevel.LEAST_SQUARES + return algo.algo_info.solver_type == AggregationLevel.LEAST_SQUARES # type: ignore def _is_likelihood(algo: Type[Algorithm]) -> bool: - return algo.algo_info.solver_type == AggregationLevel.LIKELIHOOD + return algo.algo_info.solver_type == AggregationLevel.LIKELIHOOD # type: ignore def _is_parallel(algo: Type[Algorithm]) -> bool: - return algo.algo_info.supports_parallelism + return algo.algo_info.supports_parallelism # type: ignore def _get_filters() -> dict[str, Callable[[Type[Algorithm]], bool]]: From 09f471969c7e545471a01f776e617a074fe9f043 Mon Sep 17 00:00:00 2001 From: Janos Gabler Date: Tue, 12 Nov 2024 19:51:52 +0100 Subject: [PATCH 5/8] Add tests. --- src/optimagic/visualization/history_plots.py | 2 +- tests/optimagic/test_algo_selection.py | 6 +++ .../visualization/test_history_plots.py | 46 ++++++++++++++++++- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/optimagic/visualization/history_plots.py b/src/optimagic/visualization/history_plots.py index 50b3f09e9..4c4797b53 100644 --- a/src/optimagic/visualization/history_plots.py +++ b/src/optimagic/visualization/history_plots.py @@ -182,7 +182,7 @@ def _harmonize_inputs_to_dict(results, names): # handle dict case if isinstance(results, dict): if names is not None: - results_dict = dict(zip(names, results, strict=False)) + results_dict = dict(zip(names, list(results.values()), strict=False)) else: results_dict = results diff --git a/tests/optimagic/test_algo_selection.py b/tests/optimagic/test_algo_selection.py index c42cdae87..31906c7c5 100644 --- a/tests/optimagic/test_algo_selection.py +++ b/tests/optimagic/test_algo_selection.py @@ -24,3 +24,9 @@ def test_scipy_cobyla_is_present(): assert hasattr(algos.NonlinearConstrained.GradientFree.Local, "scipy_cobyla") assert hasattr(algos.NonlinearConstrained.Local.GradientFree, "scipy_cobyla") assert hasattr(algos.Local.NonlinearConstrained.GradientFree, "scipy_cobyla") + + +def test_algorithm_lists(): + assert len(algos.All) >= len(algos.Available) + assert len(algos.AllNames) == len(algos.All) + assert len(algos.AvailableNames) == len(algos.Available) diff --git a/tests/optimagic/visualization/test_history_plots.py b/tests/optimagic/visualization/test_history_plots.py index 70078b137..32bab8358 100644 --- a/tests/optimagic/visualization/test_history_plots.py +++ b/tests/optimagic/visualization/test_history_plots.py @@ -7,7 +7,11 @@ from optimagic.logging import SQLiteLogOptions from optimagic.optimization.optimize import minimize from optimagic.parameters.bounds import Bounds -from optimagic.visualization.history_plots import criterion_plot, params_plot +from optimagic.visualization.history_plots import ( + _harmonize_inputs_to_dict, + criterion_plot, + params_plot, +) @pytest.fixture() @@ -130,3 +134,43 @@ def test_criterion_plot_wrong_inputs(): with pytest.raises(ValueError): criterion_plot(["bla", "bla"], names="blub") + + +def test_harmonize_inputs_to_dict_single_result(): + res = minimize(fun=lambda x: x @ x, params=np.arange(5), algorithm="scipy_lbfgsb") + assert _harmonize_inputs_to_dict(results=res, names=None) == {"0": res} + + +def test_harmonize_inputs_to_dict_single_result_with_name(): + res = minimize(fun=lambda x: x @ x, params=np.arange(5), algorithm="scipy_lbfgsb") + assert _harmonize_inputs_to_dict(results=res, names="bla") == {"bla": res} + + +def test_harmonize_inputs_to_dict_list_results(): + res = minimize(fun=lambda x: x @ x, params=np.arange(5), algorithm="scipy_lbfgsb") + results = [res, res] + assert _harmonize_inputs_to_dict(results=results, names=None) == { + "0": res, + "1": res, + } + + +def test_harmonize_inputs_to_dict_dict_input(): + res = minimize(fun=lambda x: x @ x, params=np.arange(5), algorithm="scipy_lbfgsb") + results = {"bla": res, "blub": res} + assert _harmonize_inputs_to_dict(results=results, names=None) == results + + +def test_harmonize_inputs_to_dict_dict_input_with_names(): + res = minimize(fun=lambda x: x @ x, params=np.arange(5), algorithm="scipy_lbfgsb") + results = {"bla": res, "blub": res} + got = _harmonize_inputs_to_dict(results=results, names=["a", "b"]) + expected = {"a": res, "b": res} + assert got == expected + + +def test_harmonize_inputs_to_dict_invalid_names(): + results = [None] + names = ["a", "b"] + with pytest.raises(ValueError): + _harmonize_inputs_to_dict(results=results, names=names) From 060337d059ecab82160be74339bfdd978ea456ab Mon Sep 17 00:00:00 2001 From: Tim Mensinger Date: Wed, 13 Nov 2024 16:23:30 +0100 Subject: [PATCH 6/8] Switch back to try-except-pass instead of contextlib.suppress --- src/estimagic/__init__.py | 3 +-- src/optimagic/__init__.py | 4 +--- src/optimagic/optimizers/pygmo_optimizers.py | 5 +++-- src/optimagic/optimizers/tao_optimizers.py | 5 +++-- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/estimagic/__init__.py b/src/estimagic/__init__.py index 056f823e4..a987f8082 100644 --- a/src/estimagic/__init__.py +++ b/src/estimagic/__init__.py @@ -1,11 +1,10 @@ -import contextlib import warnings from dataclasses import dataclass try: import pdbp # noqa: F401 except ImportError: - contextlib.suppress(Exception) + pass from estimagic import utilities from estimagic.bootstrap import BootstrapResult, bootstrap diff --git a/src/optimagic/__init__.py b/src/optimagic/__init__.py index e1cbd6a3c..a4837ca0a 100644 --- a/src/optimagic/__init__.py +++ b/src/optimagic/__init__.py @@ -1,11 +1,9 @@ from __future__ import annotations -import contextlib - try: import pdbp # noqa: F401 except ImportError: - contextlib.suppress(Exception) + pass from optimagic import constraints, mark, utilities from optimagic.algorithms import algos diff --git a/src/optimagic/optimizers/pygmo_optimizers.py b/src/optimagic/optimizers/pygmo_optimizers.py index e8be80ff6..44e6c7339 100644 --- a/src/optimagic/optimizers/pygmo_optimizers.py +++ b/src/optimagic/optimizers/pygmo_optimizers.py @@ -19,7 +19,6 @@ from __future__ import annotations -import contextlib import warnings from dataclasses import dataclass from typing import Any, List, Literal @@ -48,8 +47,10 @@ STOPPING_MAX_ITERATIONS_GENETIC = 250 -with contextlib.suppress(ImportError): +try: import pygmo as pg +except ImportError: + pass @mark.minimizer( diff --git a/src/optimagic/optimizers/tao_optimizers.py b/src/optimagic/optimizers/tao_optimizers.py index 508c7f444..8ea2401b5 100644 --- a/src/optimagic/optimizers/tao_optimizers.py +++ b/src/optimagic/optimizers/tao_optimizers.py @@ -1,6 +1,5 @@ """This module implements the POUNDERs algorithm.""" -import contextlib import functools from dataclasses import dataclass @@ -23,8 +22,10 @@ from optimagic.typing import AggregationLevel, NonNegativeFloat, PositiveInt from optimagic.utilities import calculate_trustregion_initial_radius -with contextlib.suppress(ImportError): +try: from petsc4py import PETSc +except ImportError: + pass @mark.minimizer( From f1276f299bd3c788195306aa3241fefa4598758e Mon Sep 17 00:00:00 2001 From: Tim Mensinger Date: Wed, 13 Nov 2024 17:37:10 +0100 Subject: [PATCH 7/8] Remove pdbp import from init files --- src/estimagic/__init__.py | 5 ----- src/optimagic/__init__.py | 5 ----- 2 files changed, 10 deletions(-) diff --git a/src/estimagic/__init__.py b/src/estimagic/__init__.py index a987f8082..44a640486 100644 --- a/src/estimagic/__init__.py +++ b/src/estimagic/__init__.py @@ -1,11 +1,6 @@ import warnings from dataclasses import dataclass -try: - import pdbp # noqa: F401 -except ImportError: - pass - from estimagic import utilities from estimagic.bootstrap import BootstrapResult, bootstrap from estimagic.estimate_ml import LikelihoodResult, estimate_ml diff --git a/src/optimagic/__init__.py b/src/optimagic/__init__.py index a4837ca0a..28e912234 100644 --- a/src/optimagic/__init__.py +++ b/src/optimagic/__init__.py @@ -1,10 +1,5 @@ from __future__ import annotations -try: - import pdbp # noqa: F401 -except ImportError: - pass - from optimagic import constraints, mark, utilities from optimagic.algorithms import algos from optimagic.benchmarking.benchmark_reports import ( From 63a4e93305fb68875a9efa9495caa98798efeef9 Mon Sep 17 00:00:00 2001 From: Tim Mensinger Date: Wed, 13 Nov 2024 17:45:10 +0100 Subject: [PATCH 8/8] Add test case for history plot --- tests/optimagic/visualization/test_history_plots.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/optimagic/visualization/test_history_plots.py b/tests/optimagic/visualization/test_history_plots.py index 32bab8358..62e0b0ef5 100644 --- a/tests/optimagic/visualization/test_history_plots.py +++ b/tests/optimagic/visualization/test_history_plots.py @@ -157,8 +157,10 @@ def test_harmonize_inputs_to_dict_list_results(): def test_harmonize_inputs_to_dict_dict_input(): res = minimize(fun=lambda x: x @ x, params=np.arange(5), algorithm="scipy_lbfgsb") - results = {"bla": res, "blub": res} - assert _harmonize_inputs_to_dict(results=results, names=None) == results + results = {"bla": res, om.algos.scipy_lbfgsb(): res, om.algos.scipy_neldermead: res} + got = _harmonize_inputs_to_dict(results=results, names=None) + expected = {"bla": res, "scipy_lbfgsb": res, "scipy_neldermead": res} + assert got == expected def test_harmonize_inputs_to_dict_dict_input_with_names():