Optimization in Optiland allows users to improve the performance of optical systems by adjusting design parameters to minimize
(or maximize) a merit function. The framework supports a wide range of optimizers and a flexible system for defining operands and
variables. The framework integrates tightly with the Optic class.
The Optiland optimization framework includes the following components:
Operands: Quantitative metrics for evaluating optical system performance or properties (e.g., RMS spot size, wavefront error, etc.).
Variables: System parameters that can be adjusted (e.g., surface curvatures, separations).
Scaling: Methods to scale optimization variables to a (roughly) common range for improved convergence and performance.
Optimization Problem Class: Encapsulates the problem definition, linking operands and variables.
Optimizers: Algorithms for solving optimization problems, such as gradient-based methods or evolutionary strategies.
# Add radius of curvature variable for second surfaceproblem.add_variable(lens,'radius',surface_number=2)
(Optional) Configure Batched Ray Evaluation. Batching is enabled by default. If you need to compare with legacy per-operand behavior, disable it explicitly:
# Squared weighted deltas (used by many optimizers)merit=problem.sum_squared()# Unsquared weighted deltas (useful for least-squares style methods)residuals=problem.residual_vector()# Disable batching to opt out (legacy per-operand evaluation)problem.disable_batching()
Choose an Optimizer. Select an optimizer and run the optimization:
The batching path is implemented by optiland.optimization.batched_evaluator.BatchedRayEvaluator and is integrated into OptimizationProblem by default. You can opt out with disable_batching() and re-enable with enable_batching().
When enabled, the evaluator performs three steps:
Group compatible operands that can share the same trace call.
Execute the minimum required set of trace_generic and trace calls.
Extract per-operand values from shared traced arrays while preserving backend behavior and autograd.
Current batching support includes:
Single-ray (`trace_generic`) operands such as real_x_intercept, real_y_intercept, real_z_intercept, local-coordinate intercept variants, direction cosines (real_L, real_M, real_N), and AOI.
Distribution (`trace`) operands for rms_spot_size when trace parameters match.
Operands that are not currently batchable are evaluated through the standard direct path, so mixed merit functions are fully supported.
For PyTorch workflows, this design keeps gradients valid because values are extracted by tensor indexing from traced data (without detaching). For NumPy workflows, behavior remains numerically equivalent to the standard per-operand evaluation path.
Operands represent individual components of the merit function. To find the inputs required for a specific operand:
Refer to the operand registry in the Operand module, or the API documentation.
Use operand-specific documentation for parameter details. For example, the RMS spot size requires a field as an input, while the focal length does not. All operands require a target value, weight, and an Optic instance. Note that the weight of an operand dictates its contribution relative to other operands in the merit function. For operands evaluated across the pupil or across a spectrum (e.g., standard RMS Spot Size), the merit function also automatically accounts for the intrinsic weight assigned to the Field and Wavelength objects defined in the Optic.
Optiland also includes a specialized optimizer called GlassExpert for handling problems that involve categorical variables, specifically lens materials.
This optimizer is designed to find an optimal combination of real glasses from a catalog while simultaneously optimizing continuous lens parameters.
Architecture
The GlassExpert class inherits from OptimizerGeneric and extends its capabilities to manage material variables defined by their refractive index (n_d) and Abbe number (V_d). The core algorithm operates in phases:
Initialization
The optimization problem is set up with both continuous variables (e.g., radii, thicknesses) and categorical glass variables.
Each glass variable is associated with a list of candidate glasses from a catalog (e.g., Schott, Ohara):
For each glass variable, a broad search is performed across the entire specified glass catalog.
To manage the search space, the glass map (n_d vs. V_d) is often downsampled using K-Means clustering, retaining a diverse subset of materials (controlled by pool_size, which defaults to num_neighbours in the run method).
Each glass in this downsampled pool is temporarily substituted into the design.
Map of the (nd,vd) glass space and selected candidates for global search.
Local exploration
After the global exploration, a focused search is conducted around the current best-performing glasses.
For each glass variable, its num_neighbours nearest materials in the (n_d, V_d) space are identified.
Each of these neighboring glasses is then trialed.
Example of map of the (nd,vd) glass space and selected candidates for local search.
Evaluation and refinement
For every candidate glass tested (whether from global or local exploration), a continuous local optimization is performed on all continuous variables in the system (e.g., radii, thicknesses).
The merit function value achieved after this local optimization determines the performance of that particular glass choice.
If substituting a new glass and re-optimizing continuous variables results in a lower merit function value, the new glass is kept. Otherwise, the system reverts to its previous state.
Final polish
After all glass variables have been processed through global and local exploration passes, a final local optimization is performed using only the continuous variables to fine-tune the design with the selected glass combination.
The merit function value during a GlassExpert run can look as follows (for 7 neighbours):
Evolution in log scale of the merit function during a GlassExpert run.
The error function jumps are normal and correspond to the optic being restored to its previous best state, or the evaluation of glasses far from the current glass.
Also please note that the run duration scales with the number of lenses and the number of glass neighbours.
Key Code Aspects
optiland.optimization.glass_expert.GlassExpert: The main class implementing the algorithm.
Material Representation: Glasses are primarily identified by their names (strings), but their (n_d, V_d) properties are used for neighborhood searches and catalog downsampling. Functions like get_nd_vd and get_neighbour_glasses from optiland.materials are utilized.
Variable Handling: GlassExpert temporarily separates continuous and categorical (glass) variables. Continuous optimizations are run only on the continuous set, while glass variables are iteratively substituted.
run() method: The primary entry point, which orchestrates the global exploration, local exploration, and final optimization passes. It accepts parameters like num_neighbours, maxiter (for local optimizations), and tol.
Use Case for Developers
Developers might interact with or extend the GlassExpert in several ways:
Customizing Search Strategy: While GlassExpert uses a specific greedy nearest-neighbor approach combined with K-Means downsampling, alternative strategies for exploring the categorical glass space could be implemented by modifying or subclassing GlassExpert.
Integrating New Material Properties: If optimization based on other material properties (beyond n_d and v_d) is desired, the underlying material property functions and distance metrics within GlassExpert would need to be adapted.
Performance Tuning: The number of local optimizations can be significant. Developers might explore ways to reduce this, perhaps by more sophisticated candidate selection or by using surrogate models if the optimization landscape is complex.
GlassExpert provides a powerful way to tackle mixed continuous-categorical optimization problems common in lens design, where selecting the right materials is as critical as defining the right shapes and distances.