From 4fae1cfc1ef7fe2123135322306da2547fe0bcbb Mon Sep 17 00:00:00 2001 From: gaurav Date: Thu, 26 Jun 2025 02:48:27 +0530 Subject: [PATCH 1/7] add fields --- src/optimagic/mark.py | 2 ++ src/optimagic/optimization/algorithm.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/optimagic/mark.py b/src/optimagic/mark.py index 6f0f08c29..6a4b36bda 100644 --- a/src/optimagic/mark.py +++ b/src/optimagic/mark.py @@ -119,8 +119,10 @@ def decorator(cls: AlgorithmSubclass) -> AlgorithmSubclass: is_global=is_global, needs_jac=needs_jac, needs_hess=needs_hess, + needs_bounds=False, supports_parallelism=supports_parallelism, supports_bounds=supports_bounds, + supports_infinite_bounds=False, supports_linear_constraints=supports_linear_constraints, supports_nonlinear_constraints=supports_nonlinear_constraints, disable_history=disable_history, diff --git a/src/optimagic/optimization/algorithm.py b/src/optimagic/optimization/algorithm.py index 125ad6799..7923d1d82 100644 --- a/src/optimagic/optimization/algorithm.py +++ b/src/optimagic/optimization/algorithm.py @@ -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 From a9d766ecea0c8d2b735bebbec5ed19b1b9ed8223 Mon Sep 17 00:00:00 2001 From: gaurav Date: Mon, 30 Jun 2025 13:19:52 +0530 Subject: [PATCH 2/7] fixes --- src/optimagic/optimization/algorithm.py | 4 ++++ tests/optimagic/optimization/test_algorithm.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/optimagic/optimization/algorithm.py b/src/optimagic/optimization/algorithm.py index 7923d1d82..ac83fe4dd 100644 --- a/src/optimagic/optimization/algorithm.py +++ b/src/optimagic/optimization/algorithm.py @@ -48,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): diff --git a/tests/optimagic/optimization/test_algorithm.py b/tests/optimagic/optimization/test_algorithm.py index 96a531ea9..71bae8a79 100644 --- a/tests/optimagic/optimization/test_algorithm.py +++ b/tests/optimagic/optimization/test_algorithm.py @@ -25,8 +25,10 @@ {"is_global": "no"}, {"needs_jac": "yes"}, {"needs_hess": "no"}, + {"needs_bounds": "no"}, {"supports_parallelism": "yes"}, {"supports_bounds": "no"}, + {"supports_infinite_bounds": "no"}, {"supports_linear_constraints": "yes"}, {"supports_nonlinear_constraints": "no"}, {"disable_history": "no"}, @@ -42,8 +44,10 @@ def test_algo_info_validation(kwargs): "is_global": True, "needs_jac": True, "needs_hess": True, + "needs_bounds": True, "supports_parallelism": True, "supports_bounds": True, + "supports_infinite_bounds": True, "supports_linear_constraints": True, "supports_nonlinear_constraints": True, "disable_history": True, From 53ab5d6c3959c08d0509a074ae5d8f5ae0b70048 Mon Sep 17 00:00:00 2001 From: gaurav Date: Tue, 8 Jul 2025 16:48:48 +0530 Subject: [PATCH 3/7] add minimizer fields and strcit check --- docs/source/how_to/how_to_bounds.ipynb | 77 ++++++++++++++++++- src/optimagic/exceptions.py | 8 ++ src/optimagic/mark.py | 11 ++- src/optimagic/optimization/optimize.py | 49 ++++++++++++ src/optimagic/optimizers/bhhh.py | 2 + src/optimagic/optimizers/fides.py | 2 + src/optimagic/optimizers/iminuit_migrad.py | 2 + src/optimagic/optimizers/ipopt.py | 2 + src/optimagic/optimizers/nag_optimizers.py | 4 + src/optimagic/optimizers/neldermead.py | 2 + .../optimizers/nevergrad_optimizers.py | 2 + src/optimagic/optimizers/nlopt_optimizers.py | 32 ++++++++ src/optimagic/optimizers/pounders.py | 2 + src/optimagic/optimizers/pygmo_optimizers.py | 32 ++++++++ src/optimagic/optimizers/scipy_optimizers.py | 38 +++++++++ src/optimagic/optimizers/tao_optimizers.py | 2 + src/optimagic/optimizers/tranquilo.py | 4 + src/optimagic/parameters/conversion.py | 5 +- .../optimization/test_history_collection.py | 2 + 19 files changed, 269 insertions(+), 9 deletions(-) diff --git a/docs/source/how_to/how_to_bounds.ipynb b/docs/source/how_to/how_to_bounds.ipynb index b87a15be2..5e5873478 100644 --- a/docs/source/how_to/how_to_bounds.ipynb +++ b/docs/source/how_to/how_to_bounds.ipynb @@ -223,13 +223,84 @@ "cell_type": "markdown", "id": "17", "metadata": {}, + "source": [ + "## 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:5]" + ] + }, + { + "cell_type": "markdown", + "id": "19", + "metadata": {}, + "source": [ + "In case, you forget to specify bounds for a optimizer that strictly requires them, optimagic will tell you so with a `IncompleteBoundsError`." + ] + }, + { + "cell_type": "markdown", + "id": "20", + "metadata": {}, + "source": [ + "Similarly, to find all algorithms that do not support infinite values in bounds , we can do:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21", + "metadata": {}, + "outputs": [], + "source": [ + "my_selection2 = [\n", + " algo\n", + " for algo in algos_with_bounds_support\n", + " if not algo.algo_info.supports_infinite_bounds\n", + "]\n", + "my_selection2[0:5]" + ] + }, + { + "cell_type": "markdown", + "id": "22", + "metadata": {}, + "source": [ + "In case you pass infinite values in bounds to a optimizer which does not support infinite bounds, optimagic will raise an `InfiniteBoundsError` exception. " + ] + }, + { + "cell_type": "markdown", + "id": "23", + "metadata": {}, "source": [ "## Coming from scipy" ] }, { "cell_type": "markdown", - "id": "18", + "id": "24", "metadata": {}, "source": [ "If `params` is a flat numpy array, you can also provide bounds in any format that \n", @@ -240,7 +311,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "optimagic-docs", "language": "python", "name": "python3" }, @@ -254,7 +325,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.14" + "version": "3.10.16" } }, "nbformat": 4, diff --git a/src/optimagic/exceptions.py b/src/optimagic/exceptions.py index 69e4757a7..7fa3bdf43 100644 --- a/src/optimagic/exceptions.py +++ b/src/optimagic/exceptions.py @@ -47,6 +47,14 @@ class InvalidBoundsError(OptimagicError): """Exception for invalid user provided bounds.""" +class IncompleteBoundsError(OptimagicError): + """Exception when user provided bounds are incomplete.""" + + +class InfiniteBoundsError(OptimagicError): + """Exception when user provided bounds are incomplete.""" + + class InvalidScalingError(OptimagicError): """Exception for invalid user provided scaling.""" diff --git a/src/optimagic/mark.py b/src/optimagic/mark.py index 6a4b36bda..a3567fcb3 100644 --- a/src/optimagic/mark.py +++ b/src/optimagic/mark.py @@ -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, @@ -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. @@ -119,10 +126,10 @@ def decorator(cls: AlgorithmSubclass) -> AlgorithmSubclass: is_global=is_global, needs_jac=needs_jac, needs_hess=needs_hess, - needs_bounds=False, + needs_bounds=needs_bounds, supports_parallelism=supports_parallelism, supports_bounds=supports_bounds, - supports_infinite_bounds=False, + supports_infinite_bounds=supports_infinite_bounds, supports_linear_constraints=supports_linear_constraints, supports_nonlinear_constraints=supports_nonlinear_constraints, disable_history=disable_history, diff --git a/src/optimagic/optimization/optimize.py b/src/optimagic/optimization/optimize.py index 7935de635..1fba0748a 100644 --- a/src/optimagic/optimization/optimize.py +++ b/src/optimagic/optimization/optimize.py @@ -17,12 +17,15 @@ 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, + InfiniteBoundsError, InvalidFunctionError, ) from optimagic.logging.logger import LogReader, LogStore @@ -529,6 +532,52 @@ def _optimize(problem: OptimizationProblem) -> OptimizeResult: else: used_deriv = 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 ( + problem.bounds is None + or problem.bounds.lower is None + or problem.bounds.upper is None + ): + raise IncompleteBoundsError( + f"Algorithm {problem.algorithm.name} needs finite bounds " + f"for all parameters. " + f"Please provide finite bounds for all parameters" + f"for the optimizer to run properly." + ) + + if not problem.algorithm.algo_info.supports_infinite_bounds: + # Need this logic here until processing of internal bounds converts None + # bounds to np.inf and -np.inf values. + if ( + problem.bounds is None + or problem.bounds.lower is None + or problem.bounds.upper is None + ): + raise IncompleteBoundsError( + f"Algorithm {problem.algorithm.name} needs finite bounds " + f"for all parameters. " + f"Please provide finite bounds for all parameters" + f"for the optimizer to run properly." + ) + # If bounds are not None and not finite + else: + infinite_values_in_bounds = ( + np.isinf(problem.bounds.lower).any() + or np.isinf(problem.bounds.upper).any() + ) + + if infinite_values_in_bounds: + raise InfiniteBoundsError( + f"Algorithm {problem.algorithm.name} does not support infinite " + f"values in bounds for parameters. " + f"Found infinite values in: lower={problem.bounds.lower}, " + f"upper={problem.bounds.upper}" + ) + # ================================================================================== # Get the converter (for tree flattening, constraints and scaling) # ================================================================================== diff --git a/src/optimagic/optimizers/bhhh.py b/src/optimagic/optimizers/bhhh.py index f0528c461..c7e3a1539 100644 --- a/src/optimagic/optimizers/bhhh.py +++ b/src/optimagic/optimizers/bhhh.py @@ -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, diff --git a/src/optimagic/optimizers/fides.py b/src/optimagic/optimizers/fides.py index a5af078d0..4533c6e1c 100644 --- a/src/optimagic/optimizers/fides.py +++ b/src/optimagic/optimizers/fides.py @@ -40,8 +40,10 @@ is_global=False, needs_jac=True, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=True, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, diff --git a/src/optimagic/optimizers/iminuit_migrad.py b/src/optimagic/optimizers/iminuit_migrad.py index 97248635f..3c70194e8 100644 --- a/src/optimagic/optimizers/iminuit_migrad.py +++ b/src/optimagic/optimizers/iminuit_migrad.py @@ -30,8 +30,10 @@ is_global=False, needs_jac=True, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=True, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, diff --git a/src/optimagic/optimizers/ipopt.py b/src/optimagic/optimizers/ipopt.py index 2442ebe78..80fd570c7 100644 --- a/src/optimagic/optimizers/ipopt.py +++ b/src/optimagic/optimizers/ipopt.py @@ -41,8 +41,10 @@ is_global=False, needs_jac=True, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=True, supports_linear_constraints=False, supports_nonlinear_constraints=True, disable_history=False, diff --git a/src/optimagic/optimizers/nag_optimizers.py b/src/optimagic/optimizers/nag_optimizers.py index 8c8d6875a..841fee83b 100644 --- a/src/optimagic/optimizers/nag_optimizers.py +++ b/src/optimagic/optimizers/nag_optimizers.py @@ -342,8 +342,10 @@ is_global=False, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=True, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -643,8 +645,10 @@ def nag_dfols_internal( is_global=False, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=True, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, diff --git a/src/optimagic/optimizers/neldermead.py b/src/optimagic/optimizers/neldermead.py index 233f61370..4e45da13a 100644 --- a/src/optimagic/optimizers/neldermead.py +++ b/src/optimagic/optimizers/neldermead.py @@ -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, diff --git a/src/optimagic/optimizers/nevergrad_optimizers.py b/src/optimagic/optimizers/nevergrad_optimizers.py index 10003a0b7..e8f6c239c 100644 --- a/src/optimagic/optimizers/nevergrad_optimizers.py +++ b/src/optimagic/optimizers/nevergrad_optimizers.py @@ -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, diff --git a/src/optimagic/optimizers/nlopt_optimizers.py b/src/optimagic/optimizers/nlopt_optimizers.py index 0889f6493..ce716fb0f 100644 --- a/src/optimagic/optimizers/nlopt_optimizers.py +++ b/src/optimagic/optimizers/nlopt_optimizers.py @@ -43,8 +43,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, @@ -82,8 +84,10 @@ def _solve_internal_problem( 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, @@ -119,8 +123,10 @@ def _solve_internal_problem( is_global=False, needs_jac=False, 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, @@ -158,8 +164,10 @@ def _solve_internal_problem( 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=True, disable_history=False, @@ -197,8 +205,10 @@ def _solve_internal_problem( 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, @@ -236,8 +246,10 @@ def _solve_internal_problem( 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, @@ -283,8 +295,10 @@ def _solve_internal_problem( 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, @@ -322,8 +336,10 @@ def _solve_internal_problem( 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, @@ -361,8 +377,10 @@ def _solve_internal_problem( 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, @@ -400,8 +418,10 @@ def _solve_internal_problem( 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, @@ -444,8 +464,10 @@ def _solve_internal_problem( 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, @@ -488,8 +510,10 @@ def _solve_internal_problem( 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, @@ -527,8 +551,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -588,8 +614,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -627,8 +655,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=True, disable_history=False, @@ -666,8 +696,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, diff --git a/src/optimagic/optimizers/pounders.py b/src/optimagic/optimizers/pounders.py index 87b652225..0ad500cb8 100644 --- a/src/optimagic/optimizers/pounders.py +++ b/src/optimagic/optimizers/pounders.py @@ -45,8 +45,10 @@ is_global=False, needs_jac=False, needs_hess=False, + needs_bounds=False, supports_parallelism=True, supports_bounds=True, + supports_infinite_bounds=True, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, diff --git a/src/optimagic/optimizers/pygmo_optimizers.py b/src/optimagic/optimizers/pygmo_optimizers.py index 44e6c7339..c8cb0cf67 100644 --- a/src/optimagic/optimizers/pygmo_optimizers.py +++ b/src/optimagic/optimizers/pygmo_optimizers.py @@ -60,8 +60,10 @@ is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=True, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -126,8 +128,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -172,8 +176,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -255,8 +261,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -301,8 +309,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -445,8 +455,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -541,8 +553,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -613,8 +627,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -672,8 +688,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -777,8 +795,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=True, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -882,8 +902,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -933,8 +955,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -1010,8 +1034,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -1052,8 +1078,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -1112,8 +1140,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -1171,8 +1201,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, diff --git a/src/optimagic/optimizers/scipy_optimizers.py b/src/optimagic/optimizers/scipy_optimizers.py index 4db78444b..5edbf7152 100644 --- a/src/optimagic/optimizers/scipy_optimizers.py +++ b/src/optimagic/optimizers/scipy_optimizers.py @@ -90,8 +90,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, @@ -135,8 +137,10 @@ def _solve_internal_problem( 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, @@ -175,8 +179,10 @@ def _solve_internal_problem( 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, @@ -220,8 +226,10 @@ def _solve_internal_problem( 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, @@ -262,8 +270,10 @@ def _solve_internal_problem( 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, @@ -304,8 +314,10 @@ def _solve_internal_problem( 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, @@ -340,8 +352,10 @@ def _solve_internal_problem( 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, @@ -378,8 +392,10 @@ def _solve_internal_problem( is_global=False, needs_jac=False, needs_hess=False, + needs_bounds=False, supports_parallelism=False, supports_bounds=False, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=True, disable_history=False, @@ -430,8 +446,10 @@ def _solve_internal_problem( 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, @@ -478,8 +496,10 @@ def _solve_internal_problem( 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, @@ -526,8 +546,10 @@ def _solve_internal_problem( 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, @@ -564,8 +586,10 @@ def _solve_internal_problem( 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, @@ -621,8 +645,10 @@ def _solve_internal_problem( 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, @@ -719,8 +745,10 @@ def _internal_to_scipy_constraint(c): is_global=True, 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, @@ -799,8 +827,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=True, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=True, @@ -848,8 +878,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=True, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=True, disable_history=True, @@ -924,8 +956,10 @@ def _solve_internal_problem( is_global=True, 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, @@ -1030,8 +1064,10 @@ def _solve_internal_problem( is_global=True, needs_jac=True, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, @@ -1110,8 +1146,10 @@ def _solve_internal_problem( is_global=True, needs_jac=False, needs_hess=False, + needs_bounds=True, supports_parallelism=False, supports_bounds=True, + supports_infinite_bounds=False, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=False, diff --git a/src/optimagic/optimizers/tao_optimizers.py b/src/optimagic/optimizers/tao_optimizers.py index 8ea2401b5..0299d9072 100644 --- a/src/optimagic/optimizers/tao_optimizers.py +++ b/src/optimagic/optimizers/tao_optimizers.py @@ -35,8 +35,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, diff --git a/src/optimagic/optimizers/tranquilo.py b/src/optimagic/optimizers/tranquilo.py index 0e269bc3b..9bbb69ea8 100644 --- a/src/optimagic/optimizers/tranquilo.py +++ b/src/optimagic/optimizers/tranquilo.py @@ -51,8 +51,10 @@ is_global=False, needs_jac=False, needs_hess=False, + needs_bounds=False, supports_parallelism=True, supports_bounds=True, + supports_infinite_bounds=True, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=True, @@ -241,8 +243,10 @@ def _solve_internal_problem( is_global=False, needs_jac=False, needs_hess=False, + needs_bounds=False, supports_parallelism=True, supports_bounds=True, + supports_infinite_bounds=True, supports_linear_constraints=False, supports_nonlinear_constraints=False, disable_history=True, diff --git a/src/optimagic/parameters/conversion.py b/src/optimagic/parameters/conversion.py index 2c8061228..d1d10374a 100644 --- a/src/optimagic/parameters/conversion.py +++ b/src/optimagic/parameters/conversion.py @@ -36,8 +36,7 @@ def get_converter( Args: params (pytree): The user provided parameters. constraints (list): The user provided constraints. - lower_bounds (pytree): The user provided lower_bounds - upper_bounds (pytree): The user provided upper bounds + bounds (Bounds): The user provided bounds. func_eval (float or pytree): An evaluation of ``func`` at ``params``. Used to flatten the derivative output. solver_type: Used to determine how the derivative output has to be @@ -46,8 +45,6 @@ def get_converter( performed. derivative_eval (dict, pytree or None): Evaluation of the derivative of func at params. Used for consistency checks. - soft_lower_bounds (pytree): As lower_bounds - soft_upper_bounds (pytree): As upper_bounds add_soft_bounds (bool): Whether soft bounds should be added to the internal_params diff --git a/tests/optimagic/optimization/test_history_collection.py b/tests/optimagic/optimization/test_history_collection.py index 743b8cf43..3f9bb50f3 100644 --- a/tests/optimagic/optimization/test_history_collection.py +++ b/tests/optimagic/optimization/test_history_collection.py @@ -57,8 +57,10 @@ def test_history_collection_with_parallelization(algorithm, tmp_path): 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=False, From bb0faa533d63f7c581b62d8405cb8a88f602963a Mon Sep 17 00:00:00 2001 From: gaurav Date: Tue, 8 Jul 2025 17:20:47 +0530 Subject: [PATCH 4/7] update comment --- src/optimagic/exceptions.py | 2 +- src/optimagic/optimization/optimize.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/optimagic/exceptions.py b/src/optimagic/exceptions.py index 7fa3bdf43..68fbc6da1 100644 --- a/src/optimagic/exceptions.py +++ b/src/optimagic/exceptions.py @@ -52,7 +52,7 @@ class IncompleteBoundsError(OptimagicError): class InfiniteBoundsError(OptimagicError): - """Exception when user provided bounds are incomplete.""" + """Exception when user provided bounds contain infinite values.""" class InvalidScalingError(OptimagicError): diff --git a/src/optimagic/optimization/optimize.py b/src/optimagic/optimization/optimize.py index 1fba0748a..ea412773c 100644 --- a/src/optimagic/optimization/optimize.py +++ b/src/optimagic/optimization/optimize.py @@ -536,6 +536,7 @@ def _optimize(problem: OptimizationProblem) -> OptimizeResult: # Strict checking if bounds are required and infinite values in bounds # ================================================================================== if problem.algorithm.algo_info.supports_bounds: + # Raise an error if bounds are needed but not provided if problem.algorithm.algo_info.needs_bounds: if ( problem.bounds is None @@ -548,7 +549,7 @@ def _optimize(problem: OptimizationProblem) -> OptimizeResult: f"Please provide finite bounds for all parameters" f"for the optimizer to run properly." ) - + # If algo does not support infinite bounds if not problem.algorithm.algo_info.supports_infinite_bounds: # Need this logic here until processing of internal bounds converts None # bounds to np.inf and -np.inf values. From 0170fb3403549a8668cc555867249e9c7bc24505 Mon Sep 17 00:00:00 2001 From: gaurav Date: Tue, 8 Jul 2025 17:31:27 +0530 Subject: [PATCH 5/7] fix --- src/optimagic/optimizers/fides.py | 2 +- src/optimagic/optimizers/iminuit_migrad.py | 2 +- src/optimagic/optimizers/ipopt.py | 2 +- src/optimagic/optimizers/nag_optimizers.py | 4 ++-- tests/optimagic/test_mark.py | 2 ++ 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/optimagic/optimizers/fides.py b/src/optimagic/optimizers/fides.py index 4533c6e1c..c1a5d40d8 100644 --- a/src/optimagic/optimizers/fides.py +++ b/src/optimagic/optimizers/fides.py @@ -40,7 +40,7 @@ is_global=False, needs_jac=True, needs_hess=False, - needs_bounds=True, + needs_bounds=False, supports_parallelism=False, supports_bounds=True, supports_infinite_bounds=True, diff --git a/src/optimagic/optimizers/iminuit_migrad.py b/src/optimagic/optimizers/iminuit_migrad.py index 3c70194e8..a0b0bb1ad 100644 --- a/src/optimagic/optimizers/iminuit_migrad.py +++ b/src/optimagic/optimizers/iminuit_migrad.py @@ -30,7 +30,7 @@ is_global=False, needs_jac=True, needs_hess=False, - needs_bounds=True, + needs_bounds=False, supports_parallelism=False, supports_bounds=True, supports_infinite_bounds=True, diff --git a/src/optimagic/optimizers/ipopt.py b/src/optimagic/optimizers/ipopt.py index 80fd570c7..8230c5e24 100644 --- a/src/optimagic/optimizers/ipopt.py +++ b/src/optimagic/optimizers/ipopt.py @@ -41,7 +41,7 @@ is_global=False, needs_jac=True, needs_hess=False, - needs_bounds=True, + needs_bounds=False, supports_parallelism=False, supports_bounds=True, supports_infinite_bounds=True, diff --git a/src/optimagic/optimizers/nag_optimizers.py b/src/optimagic/optimizers/nag_optimizers.py index 841fee83b..fd8bf6125 100644 --- a/src/optimagic/optimizers/nag_optimizers.py +++ b/src/optimagic/optimizers/nag_optimizers.py @@ -342,7 +342,7 @@ is_global=False, needs_jac=False, needs_hess=False, - needs_bounds=True, + needs_bounds=False, supports_parallelism=False, supports_bounds=True, supports_infinite_bounds=True, @@ -645,7 +645,7 @@ def nag_dfols_internal( is_global=False, needs_jac=False, needs_hess=False, - needs_bounds=True, + needs_bounds=False, supports_parallelism=False, supports_bounds=True, supports_infinite_bounds=True, diff --git a/tests/optimagic/test_mark.py b/tests/optimagic/test_mark.py index f13b2c1ff..4c183d429 100644 --- a/tests/optimagic/test_mark.py +++ b/tests/optimagic/test_mark.py @@ -57,8 +57,10 @@ def test_mark_minimizer(): is_global=True, needs_jac=True, needs_hess=True, + needs_bounds=True, supports_parallelism=True, supports_bounds=True, + supports_infinite_bounds=True, supports_linear_constraints=True, supports_nonlinear_constraints=True, disable_history=False, From e83b9ce3935c143c400da7bcad595afd095b881f Mon Sep 17 00:00:00 2001 From: gaurav Date: Sat, 12 Jul 2025 11:59:11 +0530 Subject: [PATCH 6/7] move strick cheking on internal bounds --- .../how_to/how_to_algorithm_selection.ipynb | 11 ++- docs/source/how_to/how_to_bounds.ipynb | 28 +++----- src/optimagic/exceptions.py | 4 -- src/optimagic/optimization/optimize.py | 72 +++++++------------ 4 files changed, 44 insertions(+), 71 deletions(-) diff --git a/docs/source/how_to/how_to_algorithm_selection.ipynb b/docs/source/how_to/how_to_algorithm_selection.ipynb index 0dfc58307..936f001a7 100644 --- a/docs/source/how_to/how_to_algorithm_selection.ipynb +++ b/docs/source/how_to/how_to_algorithm_selection.ipynb @@ -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": {}, @@ -345,7 +352,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "optimagic-docs", "language": "python", "name": "python3" }, @@ -359,7 +366,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.15" + "version": "3.10.16" } }, "nbformat": 4, diff --git a/docs/source/how_to/how_to_bounds.ipynb b/docs/source/how_to/how_to_bounds.ipynb index 5e5873478..9e587c06b 100644 --- a/docs/source/how_to/how_to_bounds.ipynb +++ b/docs/source/how_to/how_to_bounds.ipynb @@ -224,6 +224,8 @@ "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", @@ -248,7 +250,7 @@ "my_selection = [\n", " algo for algo in algos_with_bounds_support if algo.algo_info.needs_bounds\n", "]\n", - "my_selection[0:5]" + "my_selection[0:3]" ] }, { @@ -256,43 +258,35 @@ "id": "19", "metadata": {}, "source": [ - "In case, you forget to specify bounds for a optimizer that strictly requires them, optimagic will tell you so with a `IncompleteBoundsError`." - ] - }, - { - "cell_type": "markdown", - "id": "20", - "metadata": {}, - "source": [ - "Similarly, to find all algorithms that do not support infinite values in bounds , we can do:" + "Similarly, to find all algorithms that support infinite values in bounds , we can do:" ] }, { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "20", "metadata": {}, "outputs": [], "source": [ "my_selection2 = [\n", " algo\n", " for algo in algos_with_bounds_support\n", - " if not algo.algo_info.supports_infinite_bounds\n", + " if algo.algo_info.supports_infinite_bounds\n", "]\n", - "my_selection2[0:5]" + "my_selection2[0:3]" ] }, { "cell_type": "markdown", - "id": "22", + "id": "21", "metadata": {}, "source": [ - "In case you pass infinite values in bounds to a optimizer which does not support infinite bounds, optimagic will raise an `InfiniteBoundsError` exception. " + "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": "23", + "id": "22", "metadata": {}, "source": [ "## Coming from scipy" @@ -300,7 +294,7 @@ }, { "cell_type": "markdown", - "id": "24", + "id": "23", "metadata": {}, "source": [ "If `params` is a flat numpy array, you can also provide bounds in any format that \n", diff --git a/src/optimagic/exceptions.py b/src/optimagic/exceptions.py index 68fbc6da1..7a7dfb75d 100644 --- a/src/optimagic/exceptions.py +++ b/src/optimagic/exceptions.py @@ -51,10 +51,6 @@ class IncompleteBoundsError(OptimagicError): """Exception when user provided bounds are incomplete.""" -class InfiniteBoundsError(OptimagicError): - """Exception when user provided bounds contain infinite values.""" - - class InvalidScalingError(OptimagicError): """Exception for invalid user provided scaling.""" diff --git a/src/optimagic/optimization/optimize.py b/src/optimagic/optimization/optimize.py index ea412773c..f1d1e529e 100644 --- a/src/optimagic/optimization/optimize.py +++ b/src/optimagic/optimization/optimize.py @@ -25,7 +25,6 @@ from optimagic.differentiation.numdiff_options import NumdiffOptions, NumdiffOptionsDict from optimagic.exceptions import ( IncompleteBoundsError, - InfiniteBoundsError, InvalidFunctionError, ) from optimagic.logging.logger import LogReader, LogStore @@ -532,53 +531,6 @@ def _optimize(problem: OptimizationProblem) -> OptimizeResult: else: used_deriv = None - # ================================================================================== - # Strict checking if bounds are required and infinite values in bounds - # ================================================================================== - if problem.algorithm.algo_info.supports_bounds: - # Raise an error if bounds are needed but not provided - if problem.algorithm.algo_info.needs_bounds: - if ( - problem.bounds is None - or problem.bounds.lower is None - or problem.bounds.upper is None - ): - raise IncompleteBoundsError( - f"Algorithm {problem.algorithm.name} needs finite bounds " - f"for all parameters. " - f"Please provide finite bounds for all parameters" - f"for the optimizer to run properly." - ) - # If algo does not support infinite bounds - if not problem.algorithm.algo_info.supports_infinite_bounds: - # Need this logic here until processing of internal bounds converts None - # bounds to np.inf and -np.inf values. - if ( - problem.bounds is None - or problem.bounds.lower is None - or problem.bounds.upper is None - ): - raise IncompleteBoundsError( - f"Algorithm {problem.algorithm.name} needs finite bounds " - f"for all parameters. " - f"Please provide finite bounds for all parameters" - f"for the optimizer to run properly." - ) - # If bounds are not None and not finite - else: - infinite_values_in_bounds = ( - np.isinf(problem.bounds.lower).any() - or np.isinf(problem.bounds.upper).any() - ) - - if infinite_values_in_bounds: - raise InfiniteBoundsError( - f"Algorithm {problem.algorithm.name} does not support infinite " - f"values in bounds for parameters. " - f"Found infinite values in: lower={problem.bounds.lower}, " - f"upper={problem.bounds.upper}" - ) - # ================================================================================== # Get the converter (for tree flattening, constraints and scaling) # ================================================================================== @@ -605,6 +557,29 @@ 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 + ): + 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 # ================================================================================== @@ -639,6 +614,7 @@ def _optimize(problem: OptimizationProblem) -> OptimizeResult: lower=internal_params.lower_bounds, upper=internal_params.upper_bounds, ) + # ================================================================================== # Create a batch evaluator # ================================================================================== From 716e49b4ee13bdf178f5044187e8363a9638733d Mon Sep 17 00:00:00 2001 From: gaurav Date: Wed, 16 Jul 2025 13:29:49 +0530 Subject: [PATCH 7/7] add checks --- src/optimagic/optimization/optimize.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/optimagic/optimization/optimize.py b/src/optimagic/optimization/optimize.py index f1d1e529e..188816a6f 100644 --- a/src/optimagic/optimization/optimize.py +++ b/src/optimagic/optimization/optimize.py @@ -565,6 +565,8 @@ def _optimize(problem: OptimizationProblem) -> OptimizeResult: 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 "