Skip to content

Commit 88ee601

Browse files
authored
Merge branch 'main' into separate_data_from_plot
2 parents d4a1355 + 2418032 commit 88ee601

File tree

13 files changed

+111
-54
lines changed

13 files changed

+111
-54
lines changed

.pre-commit-config.yaml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ repos:
6767
- id: yamllint
6868
exclude: tests/optimagic/optimizers/_pounders/fixtures
6969
- repo: https://github.com/PyCQA/docformatter
70-
rev: eb1df34
70+
rev: v1.7.7
7171
hooks:
7272
- id: docformatter
7373
args:
@@ -79,7 +79,7 @@ repos:
7979
- --blank
8080
exclude: src/optimagic/optimization/algo_options.py
8181
- repo: https://github.com/astral-sh/ruff-pre-commit
82-
rev: v0.11.8
82+
rev: v0.12.2
8383
hooks:
8484
# Run the linter.
8585
- id: ruff
@@ -131,7 +131,7 @@ repos:
131131
args:
132132
- --drop-empty-cells
133133
- repo: https://github.com/pre-commit/mirrors-mypy
134-
rev: v1.15.0
134+
rev: v1.14.1
135135
hooks:
136136
- id: mypy
137137
files: src|tests
@@ -143,8 +143,6 @@ repos:
143143
- types-cffi
144144
- types-openpyxl
145145
- types-jinja2
146-
args:
147-
- --config=pyproject.toml
148146
ci:
149147
autoupdate_schedule: monthly
150148
skip:

CHANGES.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,47 @@ chronological order. We follow [semantic versioning](https://semver.org/) and al
55
releases are available on [Anaconda.org](https://anaconda.org/optimagic-dev/optimagic).
66

77

8+
## 0.5.2
9+
10+
This minor release includes several bug fixes and small improvements. Many contributions
11+
in this release were made by Google Summer of Code (GSoC) 2025 applicants, with
12+
@gauravmanmode and @spline2hg being the accepted contributors.
13+
14+
- {gh}`605` Enhances batch evaluator checking and processing, introduces the internal
15+
`BatchEvaluatorLiteral` literal, and updates CHANGES.md ({ghuser}`janosg`,
16+
{ghuser}`timmens`).
17+
- {gh}`598` Fixes and adds links to GitHub in the documentation ({ghuser}`hamogu`).
18+
- {gh}`594` Refines newly added optimizer wrappers ({ghuser}`janosg`).
19+
- {gh}`589` Rewrites the algorithm selection pre-commit hook in pure Python to address
20+
issues with bash scripts on Windows ({ghuser}`timmens`).
21+
- {gh}`586` and {gh}`592` Ensure the SciPy `disp` parameter is exposed for the following
22+
SciPy algorithms: slsqp, neldermead, powell, conjugate_gradient, newton_cg, cobyla,
23+
truncated_newton, trust_constr ({ghuser}`sefmef`, {ghuser}`TimBerti`).
24+
- {gh}`585` Exposes all parameters of [SciPy's
25+
BFGS](https://docs.scipy.org/doc/scipy/reference/optimize.minimize-bfgs.html)
26+
optimizer in optimagic ({ghuser}`TimBerti`).
27+
- {gh}`582` Adds support for handling infinite gradients during optimization
28+
({ghuser}`Aziz-Shameem`).
29+
- {gh}`579` Implements a wrapper for the PSO optimizer from the
30+
[nevergrad](https://github.com/facebookresearch/nevergrad) package ({ghuser}`r3kste`).
31+
- {gh}`578` Integrates the `intersphinx-registry` package into the documentation for
32+
automatic linking to up-to-date external documentation
33+
({ghuser}`Schefflera-Arboricola`).
34+
- {gh}`572` and {gh}`573` Fix bugs in error handling for parameter selector processing
35+
and constraints checking ({ghuser}`hmgaudecker`).
36+
- {gh}`570` Adds a how-to guide for adding algorithms to optimagic and improves internal
37+
documentation ({ghuser}`janosg`).
38+
- {gh}`569` Implements a threading batch evaluator ({ghuser}`spline2hg`).
39+
- {gh}`568` Introduces an initial wrapper for the migrad optimizer from the
40+
[iminuit](https://github.com/scikit-hep/iminuit) package ({ghuser}`spline2hg`).
41+
- {gh}`567` Makes the `fun` argument optional when `fun_and_jac` is provided
42+
({ghuser}`gauravmanmode`).
43+
- {gh}`563` Fixes a bug in input harmonization for history plotting
44+
({ghuser}`gauravmanmode`).
45+
- {gh}`552` Refactors and extends the `History` class, removing the internal
46+
`HistoryArrays` class ({ghuser}`timmens`).
47+
48+
849
## 0.5.1
950

1051
This is a minor release that introduces the new algorithm selection tool and several

src/optimagic/batch_evaluators.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717
import threading
1818
from typing import Any, Callable, Literal, TypeVar, cast
1919

20+
from optimagic import deprecations
2021
from optimagic.config import DEFAULT_N_CORES as N_CORES
2122
from optimagic.decorators import catch, unpack
22-
from optimagic.typing import BatchEvaluator, ErrorHandling
23+
from optimagic.typing import BatchEvaluator, BatchEvaluatorLiteral, ErrorHandling
2324

2425
T = TypeVar("T")
2526

@@ -260,10 +261,12 @@ def _check_inputs(
260261

261262

262263
def process_batch_evaluator(
263-
batch_evaluator: Literal["joblib", "pathos", "threading"]
264-
| BatchEvaluator = "joblib",
264+
batch_evaluator: BatchEvaluatorLiteral | BatchEvaluator = "joblib",
265265
) -> BatchEvaluator:
266-
batch_evaluator = "joblib" if batch_evaluator is None else batch_evaluator
266+
if batch_evaluator is None:
267+
deprecations.throw_none_valued_batch_evaluator_warning()
268+
batch_evaluator = "joblib"
269+
267270
if callable(batch_evaluator):
268271
out = batch_evaluator
269272
elif isinstance(batch_evaluator, str):
@@ -275,8 +278,8 @@ def process_batch_evaluator(
275278
out = cast(BatchEvaluator, threading_batch_evaluator)
276279
else:
277280
raise ValueError(
278-
"Invalid batch evaluator requested. Currently only 'pathos' and "
279-
"'joblib' are supported."
281+
"Invalid batch evaluator requested. Currently only 'pathos', 'joblib', "
282+
"and 'threading' are supported."
280283
)
281284
else:
282285
raise TypeError("batch_evaluator must be a callable or string.")

src/optimagic/deprecations.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,15 @@ def throw_dict_access_future_warning(attribute, obj_name):
183183
warnings.warn(msg, FutureWarning)
184184

185185

186+
def throw_none_valued_batch_evaluator_warning():
187+
msg = (
188+
"Passing `None` as the `batch_evaluator` is deprecated and will be "
189+
"removed in optimagic version 0.6.0. Please use the string 'joblib' instead to "
190+
"use the joblib batch evaluator by default."
191+
)
192+
warnings.warn(msg, FutureWarning)
193+
194+
186195
def replace_and_warn_about_deprecated_algo_options(algo_options):
187196
if not isinstance(algo_options, dict):
188197
return algo_options
@@ -362,11 +371,11 @@ def throw_dict_constraints_future_warning_if_required(
362371
if not isinstance(constraints, list):
363372
constraints = [constraints]
364373

365-
types = [
374+
types_or_none = [
366375
constraint.get("type", None) if isinstance(constraint, dict) else None
367376
for constraint in constraints
368377
]
369-
types = list(set(types) - {None})
378+
types = [t for t in types_or_none if t is not None]
370379

371380
if types:
372381
msg = (

src/optimagic/differentiation/derivatives.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from optimagic.parameters.block_trees import hessian_to_block_tree, matrix_to_block_tree
2424
from optimagic.parameters.bounds import Bounds, get_internal_bounds, pre_process_bounds
2525
from optimagic.parameters.tree_registry import get_registry
26-
from optimagic.typing import PyTree
26+
from optimagic.typing import BatchEvaluatorLiteral, PyTree
2727

2828

2929
@dataclass(frozen=True)
@@ -95,7 +95,7 @@ def first_derivative(
9595
f0: PyTree | None = None,
9696
n_cores: int = DEFAULT_N_CORES,
9797
error_handling: Literal["continue", "raise", "raise_strict"] = "continue",
98-
batch_evaluator: Literal["joblib", "pathos"] | Callable = "joblib",
98+
batch_evaluator: BatchEvaluatorLiteral | Callable = "joblib",
9999
unpacker: Callable[[Any], PyTree] | None = None,
100100
# deprecated
101101
lower_bounds: PyTree | None = None,
@@ -400,7 +400,7 @@ def second_derivative(
400400
f0: PyTree | None = None,
401401
n_cores: int = DEFAULT_N_CORES,
402402
error_handling: Literal["continue", "raise", "raise_strict"] = "continue",
403-
batch_evaluator: Literal["joblib", "pathos"] | Callable = "joblib",
403+
batch_evaluator: BatchEvaluatorLiteral | Callable = "joblib",
404404
unpacker: Callable[[Any], PyTree] | None = None,
405405
# deprecated
406406
lower_bounds: PyTree | None = None,

src/optimagic/differentiation/numdiff_options.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
from typing_extensions import NotRequired
66

7+
from optimagic.batch_evaluators import process_batch_evaluator
78
from optimagic.config import DEFAULT_N_CORES
89
from optimagic.exceptions import InvalidNumdiffOptionsError
10+
from optimagic.typing import BatchEvaluatorLiteral
911

1012

1113
@dataclass(frozen=True)
@@ -21,8 +23,8 @@ class NumdiffOptions:
2123
min_steps: The minimum step size to use for numerical differentiation. If None,
2224
the default minimum step size will be used.
2325
n_cores: The number of cores to use for numerical differentiation.
24-
batch_evaluator: The batch evaluator to use for numerical differentiation. Can
25-
be "joblib" or "pathos", or a custom function.
26+
batch_evaluator: The evaluator to use for batch evaluation. Allowed are
27+
"joblib", "pathos", and "threading", or a custom callable.
2628
2729
Raises:
2830
InvalidNumdiffError: If the numdiff options cannot be processed, e.g. because
@@ -37,7 +39,7 @@ class NumdiffOptions:
3739
scaling_factor: float = 1
3840
min_steps: float | None = None
3941
n_cores: int = DEFAULT_N_CORES
40-
batch_evaluator: Literal["joblib", "pathos"] | Callable = "joblib" # type: ignore
42+
batch_evaluator: BatchEvaluatorLiteral | Callable = "joblib" # type: ignore
4143

4244
def __post_init__(self) -> None:
4345
_validate_attribute_types_and_values(self)
@@ -51,7 +53,7 @@ class NumdiffOptionsDict(TypedDict):
5153
scaling_factor: NotRequired[float]
5254
min_steps: NotRequired[float | None]
5355
n_cores: NotRequired[int]
54-
batch_evaluator: NotRequired[Literal["joblib", "pathos"] | Callable] # type: ignore
56+
batch_evaluator: NotRequired[BatchEvaluatorLiteral | Callable] # type: ignore
5557

5658

5759
def pre_process_numdiff_options(
@@ -139,14 +141,12 @@ def _validate_attribute_types_and_values(options: NumdiffOptions) -> None:
139141
"must be an integer greater than 0."
140142
)
141143

142-
if not callable(options.batch_evaluator) and options.batch_evaluator not in {
143-
"joblib",
144-
"pathos",
145-
}:
144+
try:
145+
process_batch_evaluator(options.batch_evaluator)
146+
except Exception as e:
146147
raise InvalidNumdiffOptionsError(
147-
f"Invalid numdiff `batch_evaluator`: {options.batch_evaluator}. Batch "
148-
"evaluator must be a callable or one of 'joblib', 'pathos'."
149-
)
148+
f"Invalid batch evaluator: {options.batch_evaluator}."
149+
) from e
150150

151151

152152
class NumdiffPurpose(str, Enum):

src/optimagic/examples/criterion_functions.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,11 @@ def rosenbrock_scalar(params: PyTree) -> float:
105105
def rosenbrock_gradient(params: PyTree) -> PyTree:
106106
"""Calculate gradient of rosenbrock function."""
107107
x = _get_x(params)
108-
l1 = np.delete(x, [-1])
109-
l1 = np.append(l1, 0)
110-
l2 = np.insert(x, 0, 0)
111-
l2 = np.delete(l2, [1])
112-
l3 = np.insert(x, 0, 0)
113-
l3 = np.delete(l3, [-1])
114-
l4 = np.delete(x, [0])
115-
l4 = np.append(l4, 0)
116-
l5 = np.full((len(x) - 1), 2)
117-
l5 = np.append(l5, 0) # type: ignore[assignment]
108+
l1 = np.append(np.delete(x, [-1]), 0)
109+
l2 = np.delete(np.insert(x, 0, 0), [1])
110+
l3 = np.delete(np.insert(x, 0, 0), [-1])
111+
l4 = np.append(np.delete(x, [0]), 0)
112+
l5 = np.append(np.full((len(x) - 1), 2), 0)
118113
flat = 100 * (4 * (l1**3) + 2 * l2 - 2 * (l3**2) - 4 * (l4 * x)) + 2 * l1 - l5
119114
return _unflatten_gradient(flat, params)
120115

src/optimagic/logging/logger.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ def _extract_best_history(
242242
best_idx, level="step"
243243
)
244244

245-
def _to_dict(pandas_obj: pd.DataFrame | pd.Series) -> dict[str, Any]: # type:ignore
245+
def _to_dict(pandas_obj: pd.DataFrame | pd.Series) -> dict[str, Any]:
246246
if isinstance(pandas_obj, pd.DataFrame):
247247
result = pandas_obj.to_dict(orient="list")
248248
else:

src/optimagic/optimization/multistart_options.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from optimagic.batch_evaluators import process_batch_evaluator
1010
from optimagic.deprecations import replace_and_warn_about_deprecated_multistart_options
1111
from optimagic.exceptions import InvalidMultistartError
12-
from optimagic.typing import BatchEvaluator, PyTree
12+
from optimagic.typing import BatchEvaluator, BatchEvaluatorLiteral, PyTree
1313

1414
# ======================================================================================
1515
# Public Options
@@ -45,8 +45,8 @@ class MultistartOptions:
4545
for convergence. Determines the maximum relative distance two parameter
4646
vecctors can have to be considered equal. Defaults to 0.01.
4747
n_cores: The number of cores to use for parallelization. Defaults to 1.
48-
batch_evaluator: The evaluator to use for batch evaluation. Allowed are "joblib"
49-
and "pathos", or a custom callable.
48+
batch_evaluator: The evaluator to use for batch evaluation. Allowed are
49+
"joblib", "pathos", and "threading", or a custom callable.
5050
batch_size: The batch size for batch evaluation. Must be larger than n_cores
5151
or None.
5252
seed: The seed for the random number generator.
@@ -71,7 +71,7 @@ class MultistartOptions:
7171
convergence_xtol_rel: float | None = None
7272
convergence_max_discoveries: int = 2
7373
n_cores: int = 1
74-
batch_evaluator: Literal["joblib", "pathos"] | BatchEvaluator = "joblib"
74+
batch_evaluator: BatchEvaluatorLiteral | BatchEvaluator = "joblib"
7575
batch_size: int | None = None
7676
seed: int | np.random.Generator | None = None
7777
error_handling: Literal["raise", "continue"] | None = None
@@ -100,7 +100,7 @@ class MultistartOptionsDict(TypedDict):
100100
convergence_xtol_rel: NotRequired[float | None]
101101
convergence_max_discoveries: NotRequired[int]
102102
n_cores: NotRequired[int]
103-
batch_evaluator: NotRequired[Literal["joblib", "pathos"] | BatchEvaluator]
103+
batch_evaluator: NotRequired[BatchEvaluatorLiteral | BatchEvaluator]
104104
batch_size: NotRequired[int | None]
105105
seed: NotRequired[int | np.random.Generator | None]
106106
error_handling: NotRequired[Literal["raise", "continue"] | None]
@@ -247,14 +247,12 @@ def _validate_attribute_types_and_values(options: MultistartOptions) -> None:
247247
"must be a positive integer."
248248
)
249249

250-
if not callable(options.batch_evaluator) and options.batch_evaluator not in (
251-
"joblib",
252-
"pathos",
253-
):
250+
try:
251+
process_batch_evaluator(options.batch_evaluator)
252+
except Exception as e:
254253
raise InvalidMultistartError(
255-
f"Invalid batch evaluator: {options.batch_evaluator}. Batch evaluator "
256-
"must be a Callable or one of 'joblib' or 'pathos'."
257-
)
254+
f"Invalid batch evaluator: {options.batch_evaluator}."
255+
) from e
258256

259257
if options.batch_size is not None and (
260258
not isinstance(options.batch_size, int) or options.batch_size < options.n_cores

src/optimagic/optimizers/scipy_optimizers.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
from optimagic.typing import (
7575
AggregationLevel,
7676
BatchEvaluator,
77+
BatchEvaluatorLiteral,
7778
NegativeFloat,
7879
NonNegativeFloat,
7980
NonNegativeInt,
@@ -810,7 +811,7 @@ class ScipyBrute(Algorithm):
810811
n_grid_points: PositiveInt = 20
811812
polishing_function: Callable | None = None
812813
n_cores: PositiveInt = 1
813-
batch_evaluator: Literal["joblib", "pathos"] | BatchEvaluator = "joblib"
814+
batch_evaluator: BatchEvaluatorLiteral | BatchEvaluator = "joblib"
814815

815816
def _solve_internal_problem(
816817
self, problem: InternalOptimizationProblem, x0: NDArray[np.float64]
@@ -890,7 +891,7 @@ class ScipyDifferentialEvolution(Algorithm):
890891
) = "latinhypercube"
891892
convergence_ftol_abs: NonNegativeFloat = CONVERGENCE_SECOND_BEST_FTOL_ABS
892893
n_cores: PositiveInt = 1
893-
batch_evaluator: Literal["joblib", "pathos"] | BatchEvaluator = "joblib"
894+
batch_evaluator: BatchEvaluatorLiteral | BatchEvaluator = "joblib"
894895

895896
def _solve_internal_problem(
896897
self, problem: InternalOptimizationProblem, x0: NDArray[np.float64]

0 commit comments

Comments
 (0)