ScipyOptimizer#

class lumopt2.optimizer.scipy_optimizer.ScipyOptimizer(method: str = 'L-BFGS-B', max_iter: int | None = None, max_feval: int | None = None, ftol: float = 1e-06, gtol: float = 1e-05, max_line_search: int = 8, bounds: list | None = None, options: Dict[str, Any] | None = None, max_eval: int | None = None)#

Wrapper for scipy.optimize.minimize optimizers, particularly L-BFGS-B.

This class provides a clean interface to scipy’s optimization methods with proper handling of gradients, bounds, and optimization parameters commonly used in photonic inverse design.

Parameters:
methodstr, optional

Optimization method to use. Supported methods include ‘L-BFGS-B’, ‘BFGS’, ‘CG’, ‘Nelder-Mead’, ‘Powell’, ‘SLSQP’, etc. See scipy.optimize.minimize documentation for full list (default: ‘L-BFGS-B’).

max_iterint, optional

Maximum number of iterations (default: 100).

max_fevalint, optional

Hard upper bound on the number of objective-function evaluations. A single SciPy iteration may request many evaluations (line search, finite-difference gradients, simplex updates, …), so this knob is independent of max_iter. Translates to maxfun for L-BFGS-B and maxfev for Nelder-Mead / Powell. If the chosen method does not expose a function-evaluation budget, the value is ignored with a warning (default: None).

ftolfloat, optional

Tolerance for termination by the change of the objective function. For L-BFGS-B, this is the factr parameter multiplied by machine precision (default: 1e-6).

gtolfloat, optional

Tolerance for termination by the gradient norm (default: 1e-5).

max_line_searchint, optional

Maximum number of line search steps per iteration for L-BFGS-B (maxls in SciPy). Lower values cap the number of trial function evaluations the optimizer spends recovering from a poor search direction, which keeps iterations with noisy gradients from dominating wall-clock time. Ignored for methods other than L-BFGS-B (default: 8; SciPy’s own default is 20).

boundslist of tuple, optional

Bounds for each parameter as (min, max) pairs. If None, uses unbounded optimization (default: None). When bounds are provided, LumOpt2 requires every lower/upper bound to be finite.

optionsdict, optional

Additional options to pass to scipy.optimize.minimize (default: None).

max_evalint, optional

Backward-compatible alias for max_iter (default: None).

Deprecated since version Use: max_iter to cap iterations or max_feval to cap function evaluations.

Attributes:
methodstr

The optimization method being used.

max_iterint

Maximum number of iterations.

max_fevalint or None

Maximum number of objective-function evaluations (None for no cap).

ftolfloat

Function tolerance.

gtolfloat

Gradient tolerance.

max_line_searchint

Maximum number of line search steps per iteration (only used by L-BFGS-B).

boundslist or None

Parameter bounds.

optionsdict

Additional scipy options.

resultscipy.optimize.OptimizeResult or None

Result object from the last optimization run.

historydict

Dictionary storing optimization history with keys: - ‘params’: list of parameter arrays at each iteration - ‘fom’: list of FOM values at each iteration - ‘gradient’: list of gradient arrays at each iteration

Notes

The L-BFGS-B method is particularly well-suited for inverse design because: - It handles box constraints (bounds) naturally - It’s memory-efficient for high-dimensional problems - It uses gradient information efficiently - It converges quickly for smooth objective functions

The optimizer expects the objective function to be maximized. If you need to minimize, negate your FOM.

Examples

Basic L-BFGS-B optimization:

>>> optimizer = ScipyOptimizer(method='L-BFGS-B', max_iter=50)
>>> result = optimizer.optimize(objective_func, gradient_func, initial_params)

With bounds:

>>> bounds = [(0, 1) for _ in range(n_params)]
>>> optimizer = ScipyOptimizer(method='L-BFGS-B', bounds=bounds)
>>> result = optimizer.optimize(objective_func, gradient_func, initial_params)

Using different method:

>>> optimizer = ScipyOptimizer(method='SLSQP', max_iter=100, ftol=1e-8)
>>> result = optimizer.optimize(objective_func, gradient_func, initial_params)

Methods

ScipyOptimizer.get_history()

Get the optimization history.

ScipyOptimizer.get_result()

Get the full scipy optimization result object.

ScipyOptimizer.optimize(objective_func, ...)

Run the optimization.

Attributes

ScipyOptimizer.is_gradient_free

Return True if the optimizer uses a gradient-free method.