From 7a47d3c0d923512d77c89ccdefe1764d912c00e6 Mon Sep 17 00:00:00 2001 From: Janos Gabler Date: Sun, 17 Aug 2025 16:51:38 +0200 Subject: [PATCH] Add nlopt bobyqa. --- examples/example.ipynb | 1 + src/optimini/algorithms.py | 40 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/examples/example.ipynb b/examples/example.ipynb index 9563240..6671451 100644 --- a/examples/example.ipynb +++ b/examples/example.ipynb @@ -27,6 +27,7 @@ "results = {\n", " \"L-BFGS-B\": minimize(my_fun, params, method=\"L-BFGS-B\"),\n", " \"Nelder-Mead\": minimize(my_fun, params, method=\"Nelder-Mead\"),\n", + " \"bobyqa\": minimize(my_fun, params, method=\"bobyqa\"),\n", "}\n", "history_plot(results)" ] diff --git a/src/optimini/algorithms.py b/src/optimini/algorithms.py index e962a1d..fd0c954 100644 --- a/src/optimini/algorithms.py +++ b/src/optimini/algorithms.py @@ -1,5 +1,6 @@ from dataclasses import dataclass +import nlopt import numpy as np from numpy.typing import NDArray from scipy.optimize import Bounds as ScipyBounds @@ -104,8 +105,47 @@ def _get_scipy_bounds(lower_bounds, upper_bounds): return ScipyBounds(lower_bounds, upper_bounds) +@mark.minimizer( + name="nlopt_bobyqa", + supports_bounds=True, + needs_bounds=False, +) +@dataclass(frozen=True) +class NloptBobyqa(Algorithm): + stopping_maxfun: int = 100 + convergence_ftol_rel: float = 1e-4 + # more options here ... + + def _solve_internal_problem( + self, problem: InternalProblem, x0: NDArray[np.float64] + ) -> InternalResult: + def func(x, grad): + if grad.size > 0: + fun, jac = problem.fun_and_jac(x) + grad[:] = jac + else: + fun = problem.fun(x) + return fun + + opt = nlopt.opt(nlopt.LN_BOBYQA, x0.shape[0]) + opt.set_min_objective(func) + if self.convergence_ftol_rel is not None: + opt.set_ftol_rel(self.convergence_ftol_rel) + if self.stopping_maxfun is not None: + opt.set_maxeval(self.stopping_maxfun) + if problem.lower_bounds is not None: + opt.set_lower_bounds(problem.lower_bounds) + if problem.upper_bounds is not None: + opt.set_upper_bounds(problem.upper_bounds) + + solution_x = opt.optimize(x0) + + return InternalResult(solution_x, opt.last_optimum_value()) + + OPTIMIZER_REGISTRY = { "L-BFGS-B": SciPyLBFGSB, "CG": SciPyCG, "Nelder-Mead": SciPyNelderMead, + "bobyqa": NloptBobyqa, }