Skip to content

Add needs_bounds and supports_infinite_bounds fields in algo info #610

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

Merged
merged 12 commits into from
Jul 16, 2025
Merged
11 changes: 9 additions & 2 deletions docs/source/how_to/how_to_algorithm_selection.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@
"`.Available` instead of `.All`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"An even more fine-grained way of filtering is described in [Filtering Algorithms Using Bounds](filtering_algorithms_using_bounds)."
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down Expand Up @@ -345,7 +352,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"display_name": "optimagic-docs",
"language": "python",
"name": "python3"
},
Expand All @@ -359,7 +366,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.15"
"version": "3.10.16"
}
},
"nbformat": 4,
Expand Down
71 changes: 68 additions & 3 deletions docs/source/how_to/how_to_bounds.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -223,13 +223,78 @@
"cell_type": "markdown",
"id": "17",
"metadata": {},
"source": [
"(filtering_algorithms_using_bounds)=\n",
"\n",
"## Filtering algorithms\n",
"\n",
"It is further possible to filter algorithms based on whether they support bounds, if bounds are required to run, and if infinite bounds are supported. The AlgoInfo class provides all information about the chosen algorithm, which can be accessed with algo.algo_info... . Suppose we are looking for a optimizer that supports bounds and strictly require them for the algorithm to run properly.\n",
"\n",
"To find all algorithms that support bounds and cannot run without bounds, we can simply do:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "18",
"metadata": {},
"outputs": [],
"source": [
"from optimagic.algorithms import AVAILABLE_ALGORITHMS\n",
"\n",
"algos_with_bounds_support = [\n",
" algo\n",
" for name, algo in AVAILABLE_ALGORITHMS.items()\n",
" if algo.algo_info.supports_bounds\n",
"]\n",
"my_selection = [\n",
" algo for algo in algos_with_bounds_support if algo.algo_info.needs_bounds\n",
"]\n",
"my_selection[0:3]"
]
},
{
"cell_type": "markdown",
"id": "19",
"metadata": {},
"source": [
"Similarly, to find all algorithms that support infinite values in bounds , we can do:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "20",
"metadata": {},
"outputs": [],
"source": [
"my_selection2 = [\n",
" algo\n",
" for algo in algos_with_bounds_support\n",
" if algo.algo_info.supports_infinite_bounds\n",
"]\n",
"my_selection2[0:3]"
]
},
{
"cell_type": "markdown",
"id": "21",
"metadata": {},
"source": [
"In case you you forget to specify bounds for a optimizer that strictly requires them or pass infinite values in bounds to a optimizer which does not support them, optimagic will raise an `IncompleteBoundsError`. "
]
},
{
"cell_type": "markdown",
"id": "22",
"metadata": {},
"source": [
"## Coming from scipy"
]
},
{
"cell_type": "markdown",
"id": "18",
"id": "23",
"metadata": {},
"source": [
"If `params` is a flat numpy array, you can also provide bounds in any format that \n",
Expand All @@ -240,7 +305,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"display_name": "optimagic-docs",
"language": "python",
"name": "python3"
},
Expand All @@ -254,7 +319,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.14"
"version": "3.10.16"
}
},
"nbformat": 4,
Expand Down
4 changes: 4 additions & 0 deletions src/optimagic/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ class InvalidBoundsError(OptimagicError):
"""Exception for invalid user provided bounds."""


class IncompleteBoundsError(OptimagicError):
"""Exception when user provided bounds are incomplete."""


class InvalidScalingError(OptimagicError):
"""Exception for invalid user provided scaling."""

Expand Down
9 changes: 9 additions & 0 deletions src/optimagic/mark.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,10 @@ def minimizer(
is_global: bool,
needs_jac: bool,
needs_hess: bool,
needs_bounds: bool,
supports_parallelism: bool,
supports_bounds: bool,
supports_infinite_bounds: bool,
supports_linear_constraints: bool,
supports_nonlinear_constraints: bool,
disable_history: bool = False,
Expand All @@ -95,12 +97,17 @@ def minimizer(
needs_hess: Whether the algorithm needs some kind of second derivative. This
is not yet implemented and will be False for all currently wrapped
algorithms.
needs_bounds: Whether the algorithm needs bounds to run. This is different from
supports_bounds in that algorithms that support bounds can run without
requiring them.
supports_parallelism: Whether the algorithm supports parallelism. This needs to
be True if the algorithm previously took `n_cores` and/or `batch_evaluator`
as arguments.
supports_bounds: Whether the algorithm supports bounds. This needs to be True
if the algorithm previously took `lower_bounds` and/or `upper_bounds` as
arguments.
supports_infinite_bounds: Whether the algorithm supports infinite values in
bounds.
supports_linear_constraints: Whether the algorithm supports linear constraints.
This is not yet implemented and will be False for all currently wrapped
algorithms.
Expand All @@ -119,8 +126,10 @@ def decorator(cls: AlgorithmSubclass) -> AlgorithmSubclass:
is_global=is_global,
needs_jac=needs_jac,
needs_hess=needs_hess,
needs_bounds=needs_bounds,
supports_parallelism=supports_parallelism,
supports_bounds=supports_bounds,
supports_infinite_bounds=supports_infinite_bounds,
supports_linear_constraints=supports_linear_constraints,
supports_nonlinear_constraints=supports_nonlinear_constraints,
disable_history=disable_history,
Expand Down
6 changes: 6 additions & 0 deletions src/optimagic/optimization/algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ class AlgoInfo:
is_global: bool
needs_jac: bool
needs_hess: bool
needs_bounds: bool
supports_parallelism: bool
supports_bounds: bool
supports_infinite_bounds: bool
supports_linear_constraints: bool
supports_nonlinear_constraints: bool
disable_history: bool = False
Expand All @@ -46,10 +48,14 @@ def __post_init__(self) -> None:
report.append("needs_jac must be a bool")
if not isinstance(self.needs_hess, bool):
report.append("needs_hess must be a bool")
if not isinstance(self.needs_bounds, bool):
report.append("needs_bounds must be a bool")
if not isinstance(self.supports_parallelism, bool):
report.append("supports_parallelism must be a bool")
if not isinstance(self.supports_bounds, bool):
report.append("supports_bounds must be a bool")
if not isinstance(self.supports_infinite_bounds, bool):
report.append("supports_infinite_bounds must be a bool")
if not isinstance(self.supports_linear_constraints, bool):
report.append("supports_linear_constraints must be a bool")
if not isinstance(self.supports_nonlinear_constraints, bool):
Expand Down
28 changes: 28 additions & 0 deletions src/optimagic/optimization/optimize.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
from pathlib import Path
from typing import Any, Callable, Sequence, Type, cast

import numpy as np
from scipy.optimize import Bounds as ScipyBounds

from optimagic.batch_evaluators import process_batch_evaluator
from optimagic.constraints import Constraint
from optimagic.differentiation.numdiff_options import NumdiffOptions, NumdiffOptionsDict
from optimagic.exceptions import (
IncompleteBoundsError,
InvalidFunctionError,
)
from optimagic.logging.logger import LogReader, LogStore
Expand Down Expand Up @@ -555,6 +557,31 @@ def _optimize(problem: OptimizationProblem) -> OptimizeResult:
else:
logger = None

# ==================================================================================
# Strict checking if bounds are required and infinite values in bounds
# ==================================================================================
if problem.algorithm.algo_info.supports_bounds:
if problem.algorithm.algo_info.needs_bounds:
if (
internal_params.lower_bounds is None
or internal_params.upper_bounds is None
or np.isinf(internal_params.lower_bounds).all()
or np.isinf(internal_params.upper_bounds).all()
):
raise IncompleteBoundsError(
f"Algorithm {problem.algorithm.name} needs finite bounds "
f"for all parameters. "
)
if not problem.algorithm.algo_info.supports_infinite_bounds:
if (
np.isinf(internal_params.lower_bounds).any()
or np.isinf(internal_params.upper_bounds).any()
):
raise IncompleteBoundsError(
f"Algorithm {problem.algorithm.name} does not support infinite "
f"values in bounds for parameters. "
)

# ==================================================================================
# Do some things that require internal parameters or bounds
# ==================================================================================
Expand Down Expand Up @@ -589,6 +616,7 @@ def _optimize(problem: OptimizationProblem) -> OptimizeResult:
lower=internal_params.lower_bounds,
upper=internal_params.upper_bounds,
)

# ==================================================================================
# Create a batch evaluator
# ==================================================================================
Expand Down
2 changes: 2 additions & 0 deletions src/optimagic/optimizers/bhhh.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
is_global=False,
needs_jac=True,
needs_hess=False,
needs_bounds=False,
supports_parallelism=False,
supports_bounds=False,
supports_infinite_bounds=False,
supports_linear_constraints=False,
supports_nonlinear_constraints=False,
disable_history=False,
Expand Down
2 changes: 2 additions & 0 deletions src/optimagic/optimizers/fides.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@
is_global=False,
needs_jac=True,
needs_hess=False,
needs_bounds=False,
supports_parallelism=False,
supports_bounds=True,
supports_infinite_bounds=True,
supports_linear_constraints=False,
supports_nonlinear_constraints=False,
disable_history=False,
Expand Down
2 changes: 2 additions & 0 deletions src/optimagic/optimizers/iminuit_migrad.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@
is_global=False,
needs_jac=True,
needs_hess=False,
needs_bounds=False,
supports_parallelism=False,
supports_bounds=True,
supports_infinite_bounds=True,
supports_linear_constraints=False,
supports_nonlinear_constraints=False,
disable_history=False,
Expand Down
2 changes: 2 additions & 0 deletions src/optimagic/optimizers/ipopt.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@
is_global=False,
needs_jac=True,
needs_hess=False,
needs_bounds=False,
supports_parallelism=False,
supports_bounds=True,
supports_infinite_bounds=True,
supports_linear_constraints=False,
supports_nonlinear_constraints=True,
disable_history=False,
Expand Down
4 changes: 4 additions & 0 deletions src/optimagic/optimizers/nag_optimizers.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,10 @@
is_global=False,
needs_jac=False,
needs_hess=False,
needs_bounds=False,
supports_parallelism=False,
supports_bounds=True,
supports_infinite_bounds=True,
supports_linear_constraints=False,
supports_nonlinear_constraints=False,
disable_history=False,
Expand Down Expand Up @@ -643,8 +645,10 @@ def nag_dfols_internal(
is_global=False,
needs_jac=False,
needs_hess=False,
needs_bounds=False,
supports_parallelism=False,
supports_bounds=True,
supports_infinite_bounds=True,
supports_linear_constraints=False,
supports_nonlinear_constraints=False,
disable_history=False,
Expand Down
2 changes: 2 additions & 0 deletions src/optimagic/optimizers/neldermead.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
is_global=False,
needs_jac=False,
needs_hess=False,
needs_bounds=False,
supports_parallelism=True,
supports_bounds=False,
supports_infinite_bounds=False,
supports_linear_constraints=False,
supports_nonlinear_constraints=False,
disable_history=True,
Expand Down
2 changes: 2 additions & 0 deletions src/optimagic/optimizers/nevergrad_optimizers.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@
is_global=True,
needs_jac=False,
needs_hess=False,
needs_bounds=False,
supports_parallelism=True,
supports_bounds=True,
supports_infinite_bounds=False,
supports_linear_constraints=False,
supports_nonlinear_constraints=False,
disable_history=False,
Expand Down
Loading
Loading