Optimizer Backends
ropt.backend
Public API for optimizer backend implementations.
Backends define how ropt runs an optimization algorithm against the problem
described by an EnOptContext object. A backend
manages the optimizer lifecycle, requests function and gradient evaluations
through the core callback interface, and advances the optimization from an
initial variable vector toward a solution.
Core Interface
All backend implementations inherit from the
Backend base class, which defines the backend
lifecycle (__init__, init, start), validation hook (validate_options),
and capability flags (allow_nan, is_parallel).
Integration with Optimization
Backends are accessed via an
EnOptContext object through its backend
field. A backend is instantiated either directly as an object or via a
BackendConfig object, which is used by the
plugin system to create an instance based on the configured backend method
string.
During execution, a backend uses the
OptimizerCallback interface to request
objective, constraint, and gradient evaluations from the ropt core.
Built-in and Custom Backends
ropt includes two built-in backends:
SciPyBackend: Uses optimization methods provided by SciPy.ExternalBackend: Delegates the optimization loop to an external executable or process.
Users can implement custom backends by subclassing Backend. Those subclasses
can be instantiated directly and passed into an
EnOptContext object through its backend
field. Registering a custom backend with the plugin system is optional and
only required when the backend should be selected and configured via
BackendConfig objects instead of being instantiated explicitly by the user.
Backend
Bases: ABC
Abstract base class for optimizer backend implementations.
All concrete backend implementations must inherit from this class and
implement the required lifecycle and validation methods. A backend is
responsible for configuring a concrete optimization algorithm, interacting
with the ropt evaluation pipeline through an
OptimizerCallback, and executing the main
optimization loop.
During optimization, the backend receives an
EnOptContext object describing the problem
setup and uses the callback interface to request objective, constraint, and
gradient evaluations as needed by the underlying algorithm.
Lifecycle
- Instantiation via
__init__: Called with a backend configuration object. - Setup via
init: Called once per optimization workflow with theEnOptContextand anOptimizerCallback. - Validation via
validate_options: Called to verify that the configured backend options are supported. - Execution via
start: Called with the initial variable vector to run the optimization algorithm.
Subclasses must implement:
__init__: Stores backend configuration and performs lightweight setup.init: Receives the optimization context and callback interface.start: Runs the optimization algorithm.validate_options: Verifies that backend-specific options are valid.
Subclasses may optionally override:
allow_nan: Indicates whether the backend can continue when evaluations produceNaNvalues.is_parallel: Indicates whether the backend may evaluate multiple candidate variable vectors concurrently.
__init__
abstractmethod
Create a new backend instance.
Called during instantiation. Subclasses should store the configuration
and perform any lightweight initialization. Validation and
context-dependent setup should usually be deferred to validate_options
and init.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
backend_config
|
BackendConfig
|
Configuration object specifying the backend method and any method-specific options. |
required |
init
abstractmethod
Finalize initialization after the optimization context is known.
Called once at the start of each optimization workflow, after all configuration is finalized. Use this method to store the optimization context, retain the callback interface, and perform any setup that depends on the full problem definition.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
context
|
EnOptContext
|
The full optimization context, containing all configuration and state for the current workflow. |
required |
optimizer_callback
|
OptimizerCallback
|
Callback interface used to request objective,
constraint, and gradient evaluations from the |
required |
start
abstractmethod
Run the optimization algorithm from the provided initial values.
Starts the backend's main optimization loop using the supplied initial
variable vector. During execution, the implementation is expected to
use the OptimizerCallback provided in
init to request any required objective, constraint, or gradient
evaluations from the ropt core.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
initial_values
|
NDArray[float64]
|
A 1D array of shape |
required |
allow_nan
property
Indicate whether the backend can handle NaN evaluation results.
Backends that can continue after receiving NaN objective or
constraint values should override this property to return True.
This is particularly relevant in ensemble-based optimization where
evaluations might fail for all realizations. When allow_nan is True,
setting realization_min_success to
zero allows the evaluation process to return NaN instead of raising an
error, enabling the optimizer to potentially continue.
Returns:
| Type | Description |
|---|---|
bool
|
|
is_parallel
property
Indicate whether the backend may issue parallel evaluations.
Backends that evaluate multiple candidate variable vectors concurrently
should override this property to return True.
This information can be used by ropt and related components to manage
resources or coordinate parallel execution appropriately.
Returns:
| Type | Description |
|---|---|
bool
|
|
validate_options
abstractmethod
Validate backend-specific options for the configured method.
Checks that the options supplied through the
BackendConfig object have the expected
type, contain only supported keys, and satisfy any method-specific
value constraints.
Concrete backends should implement validation logic for the methods they support, potentially using schema-validation tools such as Pydantic.
The raised exception must be a ValueError, or derive from a ValueError.
Note
Backend options may be represented as a dictionary or list,
depending on the backend. This method should verify that the type
matches what the backend expects and raise a ValueError with a
clear message when it does not.
Method name with prefix
The method string may be prefixed in the form "backend/method".
Implementations should account for this when parsing the method
name.
Handling the default method
The method string may be set to "default", in which case it
should be mapped to the backend's actual default method.
Raises:
| Type | Description |
|---|---|
ValueError
|
If the provided options are invalid. |
ropt.backend.scipy.SciPyBackend
Bases: Backend
Backend implementation using SciPy optimization algorithms.
Implements the Backend interface to expose
optimization algorithms from
scipy.optimize
to ropt.
The algorithm is selected via the method field of the
BackendConfig object. Algorithm-specific
options are passed through the options dictionary. Click on the common
options or the method name for the corresponding
scipy.optimize
documentation:
Common Options:
The keep_feasible option is used to maintain feasibility with
respect to bound, linear and non-linear constraints, by passing
it to the constraint handling code of the underlying SciPy
optimizer. Some algorithms may choose to ignore this option.
Hessian Options:
hess, exception_strategy, min_curvature, min_denominator, init_scale
These options are used to configure the Hessian approximation
method for the optimizer. The hess option specifies the type
of Hessian approximation to use ("BFGS" or "SR1"), while the
other options provide additional parameters for the chosen
method.
Method-specific Options:
| Method | Options |
|---|---|
| Nelder-Mead | maxfev, xatol, fatol, adaptive |
| Powell | maxfev, xtol, ftol |
| CG | gtol, norm, eps, finite_diff_rel_step, c1, c2 |
| BFGS | gtol, norm, eps, finite_diff_rel_step, xrtol, c1, c2 |
| Newton-CG | xtol, eps, c1, c2 |
| L-BFGS-B1 | disp, maxcor, ftol, gtol, eps, maxfun, iprint, maxls, finite_diff_rel_step |
| TNC2 | maxfun, eps, scale, offset, maxCGit, eta, stepmx, accuracy, minfev, ftol, xtol, gtol, rescale, finite_diff_rel_step, |
| COBYLA | rhobeg, tol, catol |
| COBYQA | maxfev, f_target, feasibility_tol, initial_tr_radius, final_tr_radius, scale |
| SLSQP | ftol, eps, finite_diff_rel_step |
| trust-constr | gtol, xtol, barrier_tol, sparse_jacobian, initial_tr_radius, initial_constr_penalty, initial_barrier_parameter, initial_barrier_tolerance, factorization_method, finite_diff_rel_step, verbose |
| differential_evolution | strategy, popsize, tol, mutation, recombination, rng, polish, init, atol, updating |
Notes:
- Options in italics override a common option with a different type or behavior.
- Options with
strikethroughindicate a common option that is not supported.
ropt.backend.external.ExternalBackend
Bases: Backend
Backend implementation that runs an optimizer in a separate process.
Implements the Backend interface by spawning a
child process to run a delegate backend. The child process performs the
optimization independently and communicates back through queues to request
function evaluations, report optimizer states, and propagate errors.
Method naming
Unlike other backends, the method field of
BackendConfig must include both the plugin
and method name in one of these forms:
external/plugin-name/method-nameexternal/method-name
The external/ prefix is stripped before the remainder is forwarded to
the delegate plugin. Standard plugin-name/method-name resolution without
the prefix is not supported by this backend.
Note
The cloudpickle package must be installed. If it is absent,
instantiation raises NotImplementedError.
ropt.backend.utils
Utility functions for use by optimizer backend plugins.
This module provides helpers for constraint validation, linear constraint adjustment based on variable masks, unique output path construction, and normalization of non-linear constraints into a standard form.
validate_supported_constraints
validate_supported_constraints(
context: EnOptContext,
method: str,
supported_constraints: dict[str, set[str]],
required_constraints: dict[str, set[str]],
) -> None
Raise if the context's constraints are incompatible with the chosen method.
Checks bounds, linear, and non-linear constraints in context against the
sets of method names that support or require each constraint type. Constraint
types are identified by the keys "bounds", "linear:eq", "linear:ineq",
"nonlinear:eq", and "nonlinear:ineq".
Raises NotImplementedError if a constraint present in context is not
supported by the method, or if a constraint required by the method is absent
from context.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
context
|
EnOptContext
|
The optimization context to inspect. |
required |
method
|
str
|
The name of the optimization method being used. |
required |
supported_constraints
|
dict[str, set[str]]
|
Maps each constraint type to the supported methods. |
required |
required_constraints
|
dict[str, set[str]]
|
Maps each constraint type to supported methods. |
required |
create_output_path
create_output_path(
base_name: str,
base_dir: Path | None = None,
name: str | None = None,
suffix: str | None = None,
) -> Path
Construct a unique output path, appending an index if necessary.
Builds a path from the provided components. If the resulting path already
exists on disk, a three-digit counter suffix (e.g. -001, -002) is
appended or incremented until a non-existing path is found.
The path is assembled as:
<base_dir>/<base_name>[-<name>][-<index>][<suffix>]
base_dir is created (including parents) if it does not exist.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_name
|
str
|
Base file or directory name. |
required |
base_dir
|
Path | None
|
Parent directory. If |
None
|
name
|
str | None
|
Optional label appended to |
None
|
suffix
|
str | None
|
Optional file extension including the leading dot |
None
|
Returns:
| Type | Description |
|---|---|
Path
|
A |
NormalizedConstraints
Normalizes non-linear constraints into a standard scalar form.
Transforms raw constraints defined by lower and upper bound pairs into one
of the forms C(x) = 0, C(x) > 0, or C(x) < 0 by subtracting the
relevant bound and optionally flipping the sign.
Normalization rules (applied per constraint row):
- If
lower_bound ≈ upper_bound(within 1e-15): equality constraint, normalized asC(x) - lower_bound. - If only
lower_boundis finite: inequality, normalized asC(x) - lower_bound. - If only
upper_boundis finite: inequality, normalized as-(C(x) - upper_bound). - If both bounds are finite: the constraint is split into two rows, one for each bound.
By default inequality constraints are normalized to C(x) > 0. Setting
flip=True produces C(x) < 0 instead.
Usage:
- Initialize with the lower and upper bounds.
- Before each new function/gradient evaluation with a new variable vector,
reset the normalized constraints by calling the
resetmethod. - The constraint values are given by the
constraintsproperty. Before accessing it, call theset_constraintswith the raw constraints. If necessary, this will calculate and cache the normalized values. Since values are cached, calling this method and accessingconstraintsmultiple times is cheap. - Use the same procedure for gradients, using the
gradientsproperty andset_gradients. Raw gradients must be provided as a matrix, where the rows are the gradients of each constraint. - Use the
is_eqproperty to retrieve a vector of boolean flags to check which constraints are equality constraints.
See the scipy optimization backend in the ropt source code for an
example of usage.
Parallel evaluation.
The raw constraints may be a vector of constraints, or may be a matrix
of constraints for multiple variables to support parallel evaluation. In
the latter case, the constraints for different variables are given by
the columns of the matrix. In this case, the constraints property will
have the same structure. Note that this is only supported for the
constraint values, not for the gradients. Hence, parallel evaluation of
multiple gradients is not supported.
__init__
Create a new constraint normalizer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
flip
|
bool
|
Whether to normalize inequality constraints to |
False
|
set_bounds
Set or update the constraint bounds.
Computes the internal normalization mapping from the supplied bound arrays. If the bounds change between calls, the mapping is rebuilt.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lower_bounds
|
NDArray[float64]
|
Lower bound for each raw constraint. |
required |
upper_bounds
|
NDArray[float64]
|
Upper bound for each raw constraint. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the new bounds change which constraints are equalities vs. inequalities, since this would invalidate cached optimizer state. |
is_eq
property
Return flags indicating which normalized rows are equalities.
The returned list corresponds to the normalized constraint rows after any splitting of two-sided bounds into separate lower and upper rows.
Returns:
| Type | Description |
|---|---|
list[bool]
|
A list of booleans where |
reset
Discard cached normalized values and gradients.
Call this before normalizing results for a new variable vector. After
reset, the next calls to set_constraints and set_gradients will
rebuild the cached normalized data.
After calling this method, the constraints and gradients properties
return None until new values are cached.
constraints
property
Return cached normalized constraint values, if available.
These values are produced by
set_constraints
after subtracting the relevant bound and applying any required sign
flip.
Returns None if set_constraints has not been called since the last
reset.
Returns:
| Type | Description |
|---|---|
NDArray[float64] | None
|
A 2D NumPy array of shape |
gradients
property
Return cached normalized constraint gradients, if available.
These gradients are produced by
set_gradients
after applying any required sign flip.
Returns None if set_gradients has not been called since the last
reset.
Returns:
| Type | Description |
|---|---|
NDArray[float64] | None
|
A 2D NumPy array of shape |
set_constraints
Normalize and cache raw constraint values.
Applies the configured bound subtraction and sign convention to raw
constraint values, then stores the result in the constraints cache.
Parallel evaluation is supported: if values is 2D, columns represent
different evaluation points and rows represent raw constraint indices.
A 1D input is treated as a single evaluation point.
If normalized values are already cached, this method returns without
modifying them; call reset first to recompute.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
values
|
NDArray[float64]
|
Raw constraint values with shape |
required |
set_gradients
Normalize and cache raw constraint gradients.
Applies the configured sign convention to raw constraint gradients and
stores the result in the gradients cache.
If normalized gradients are already cached, this method returns without
modifying them; call reset first to recompute.
Note
Unlike set_constraints, this method does not support parallel
evaluation; it expects gradients for a single variable vector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
values
|
NDArray[float64]
|
Raw constraint gradients with shape
|
required |
get_masked_linear_constraints
get_masked_linear_constraints(
context: EnOptContext, initial_values: NDArray[float64]
) -> tuple[
NDArray[np.float64],
NDArray[np.float64],
NDArray[np.float64],
]
Adjust linear constraints based on a variable mask.
When an optimization problem uses a variable mask (context.variables.mask)
to optimize only a subset of variables, the linear constraints need to be
adapted. This function performs that adaptation.
It removes columns from the constraint coefficient matrix
(context.linear_constraints.coefficients) that correspond to the masked
(fixed) variables. The contribution of these fixed variables (using their
initial_values) is then calculated and subtracted from the original lower
and upper bounds (context.linear_constraints.lower_bounds,
context.linear_constraints.upper_bounds) to produce adjusted bounds for the
optimization involving only the active variables.
Additionally, any constraint rows that originally involved only masked variables (i.e., all coefficients for active variables in that row are zero) are removed entirely, as they become trivial constants.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
context
|
EnOptContext
|
The |
required |
initial_values
|
NDArray[float64]
|
The initial values to use. |
required |
Returns:
| Type | Description |
|---|---|
tuple[NDArray[float64], NDArray[float64], NDArray[float64]]
|
A tuple of |