Skip to content

Domain transforms

ropt.transforms

Domain Transformation Framework.

This module provides a flexible framework for transforming optimization variables, objectives, and constraints between user-defined domains and the domains used internally by the optimizer. These transformations are essential for:

  • Improving Optimizer Performance: Scaling, shifting, and other transformations can significantly enhance the efficiency, stability, and convergence of optimization algorithms.
  • Implementing Custom Mappings: Beyond simple scaling, this framework supports complex, user-defined mappings between domains, allowing for tailored problem representations.
  • Handling Diverse Units and Scales: Transformations enable the optimizer to work with variables and functions that may have vastly different units or scales, improving numerical stability.

Key Components:

  • Abstract Base Classes: Transform classes derive from abstract base classes that define the specific mapping logic between domains.
    • VariableTransform: Defines the interface for transforming variables between user and optimizer domains.
    • ObjectiveTransform: Defines the interface for transforming objective values between user and optimizer domains.
    • NonLinearConstraintTransform: Defines the interface for transforming non-linear constraint values between user and optimizer domains.
  • OptModelTransforms: A container class for conveniently grouping and passing multiple transformation objects (variable, objective, and nonlinear constraint).

Workflow and Integration:

  1. Configuration: Transformation objects are passed to the EnOptConfig during configuration validation, using an OptModelTransforms instance. This ensures that the entire optimization process is aware of and configured for the transformed space. The trnsformation objects are stored in the configuration object.
  2. Optimization Plan: The same transformation objects are passed to the relevant optimization steps within the Plan via the configuraiton object. (See, for example, the default implementation of an optimizer step in DefaultOptimizerStep.run).
  3. Evaluation: When the optimizer requests an evaluation of a variable vector, the following occurs:
    • Transformation to the User Domain: The variable vector is transformed from the optimizer domain back to the user domain using the from_optimizer method of the VariableTransform.
    • Function Evaluation: Objective and constraint values are calculated in the user domain.
    • Transformation to the Optimizer Domain: The resulting objective and constraint values are transformed to the optimizer domain using the to_optimizer methods of the ObjectiveTransform and NonLinearConstraintTransform.
  4. Optimization: The optimizer proceeds using the transformed values.
  5. Results: The Results objects produced during optimization hold values in the optimizer domain. To obtain results in the user domain, the transform_from_optimizer method is used to create new Results objects with the transformed values. For example, DefaultOptimizerStep.run emits events that include a dictionary with a "results" key That contains Results objects in the optimizer domain. To obtain results in the user domain they must be converted using the transform_from_optimizer method.

Classes:

Name Description
OptModelTransforms

A data class for conveniently grouping and passing multiple transformation objects.

VariableScaler

A concrete implementation of VariableTransform that performs linear scaling and shifting.

ropt.transforms.OptModelTransforms dataclass

A container for optimization model transformers.

variables class-attribute instance-attribute

variables: VariableTransform | None = None

A VariableTransform object that defines the transformation for variables.

If None, no transformation is applied to variables.

objectives class-attribute instance-attribute

objectives: ObjectiveTransform | None = None

An ObjectiveTransform object that defines the transformation for objectives.

If None, no transformation is applied to objectives.

nonlinear_constraints class-attribute instance-attribute

nonlinear_constraints: (
    NonLinearConstraintTransform | None
) = None

A NonLinearConstraintTransform object that defines the transformation for nonlinear constraints.

If None, no transformation is applied to nonlinear constraints.

ropt.transforms.base.VariableTransform

Bases: ABC

Abstract base class for variable transformations.

This class defines the interface for transforming variables between the user-defined domain and the optimizer's internal domain. Concrete implementations of this class handle the specific logic for each type of transformation.

When implementing a variable transformation, the following aspects must be considered:

  • Variable Value Transformation: Mapping variable values between the user and optimizer domains. This is achieved by overriding the to_optimizer and from_optimizer methods.
  • Perturbation Magnitude Transformation: Stochastic gradient-based algorithms use perturbations with specified magnitudes (see perturbation_magnitudes). These magnitudes are typically defined in the user domain and must be transformed to the optimizer domain using the magnitudes_to_optimizer method.
  • Bound Constraint Difference Transformation: To report violations of variable bounds, the differences between variable values and their lower/upper bounds must be transformed from the optimizer domain back to the user domain. This is done using the bound_constraint_diffs_from_optimizer method.
  • Linear Constraint Transformation: Linear constraints are generally defined by coefficients and right-hand-side values in the user domain. These must be transformed to the optimizer domain using the linear_constraints_to_optimizer method.
  • Linear Constraint Difference Transformation: To report violations of linear constraints, the differences between the linear constraint values and their right-hand-side values must be transformed back to the user domain. This is done using the linear_constraints_diffs_from_optimizer method.

to_optimizer abstractmethod

to_optimizer(
    values: NDArray[float64],
) -> NDArray[np.float64]

Transform values from the user domain to the optimizer domain.

This method maps variable values from the user-defined domain to the optimizer's internal domain. This transformation might involve scaling, shifting, or other operations to improve the optimizer's performance.

The input values may be a multi-dimensional array. It is assumed that the last axis of the array represents the variable values. If this is not the case, you must adjust the order of the axes before and after calling this method.

Parameters:

Name Type Description Default
values NDArray[float64]

The variable values in the user domain to be transformed.

required

Returns:

Type Description
NDArray[float64]

The transformed variable values in the optimizer domain.

from_optimizer abstractmethod

from_optimizer(
    values: NDArray[float64],
) -> NDArray[np.float64]

Transform values from the optimizer domain to the user domain.

This method maps variable values from the optimizer's internal domain back to the user-defined domain. This transformation reverses any scaling, shifting, or other operations that were applied to improve the optimizer's performance.

The input values may be a multi-dimensional array. It is assumed that the last axis of the array represents the variable values. If this is not the case, you must adjust the order of the axes before and after calling this method.

Parameters:

Name Type Description Default
values NDArray[float64]

The variable values in the optimizer domain to be transformed.

required

Returns:

Type Description
NDArray[float64]

The transformed variable values in the user domain.

magnitudes_to_optimizer abstractmethod

magnitudes_to_optimizer(
    values: NDArray[float64],
) -> NDArray[np.float64]

Transform perturbation magnitudes to the optimizer domain.

This method transforms perturbation magnitudes, typically used in stochastic gradient-based algorithms, from the user-defined domain to the optimizer's internal domain. The transformation ensures that the perturbations are applied correctly in the optimizer's space, which may have different scaling or units than the user domain.

For example, if variables are scaled down in the optimizer domain, the perturbation magnitudes should also be scaled down proportionally.

Parameters:

Name Type Description Default
values NDArray[float64]

The perturbation magnitudes in the user domain to be transformed.

required

Returns:

Type Description
NDArray[float64]

The transformed perturbation magnitudes in the optimizer domain.

bound_constraint_diffs_from_optimizer abstractmethod

bound_constraint_diffs_from_optimizer(
    lower_diffs: NDArray[float64],
    upper_diffs: NDArray[float64],
) -> tuple[NDArray[np.float64], NDArray[np.float64]]

Transform bound constraint differences to the user domain.

This method transforms the differences between variable values and their lower/upper bounds from the optimizer's internal domain back to the user-defined domain. These differences are used to report constraint violations.

For example, if variables are scaled in the optimizer domain, the differences between the variables and their bounds must be scaled back to the user domain to accurately reflect the constraint violations in the user's original units.

Parameters:

Name Type Description Default
lower_diffs NDArray[float64]

The differences between the variable values and their lower bounds in the optimizer domain.

required
upper_diffs NDArray[float64]

The differences between the variable values and their upper bounds in the optimizer domain.

required

Returns:

Type Description
tuple[NDArray[float64], NDArray[float64]]

A tuple containing the transformed differences.

linear_constraints_to_optimizer

linear_constraints_to_optimizer(
    coefficients: NDArray[float64],
    lower_bounds: NDArray[float64],
    upper_bounds: NDArray[float64],
) -> tuple[
    NDArray[np.float64],
    NDArray[np.float64],
    NDArray[np.float64],
]

Transform linear constraints from the user domain to the optimizer domain.

This method transforms linear constraints, defined by their coefficients and right-hand-side bounds, from the user-defined domain to the optimizer's internal domain. This is essential to maintain the validity of the constraints after variable transformations.

For instance, if variables are scaled or shifted in the optimizer domain, the coefficients and bounds of the linear constraints must be adjusted accordingly to ensure the constraints remain consistent.

The linear constraints are defined by the equation A * x = b, where A is the coefficient matrix, x is the variable vector, and b represents the right-hand-side bounds.

Parameters:

Name Type Description Default
coefficients NDArray[float64]

The coefficient matrix.

required
lower_bounds NDArray[float64]

The lower bounds on the right-hand-side values.

required
upper_bounds NDArray[float64]

The upper bounds on the right-hand-side values.

required

Returns:

Type Description
tuple[NDArray[float64], NDArray[float64], NDArray[float64]]

A tuple containing the transformed coefficient matrix and bounds.

linear_constraints_diffs_from_optimizer

linear_constraints_diffs_from_optimizer(
    lower_diffs: NDArray[float64],
    upper_diffs: NDArray[float64],
) -> tuple[NDArray[np.float64], NDArray[np.float64]]

Transform linear constraint differences to the user domain.

This method transforms the differences between linear constraint values and their lower/upper bounds from the optimizer's internal domain back to the user-defined domain. These differences are used to report constraint violations.

For example, if linear constraints are scaled in the optimizer domain, the differences between the constraint values and their bounds must be scaled back to the user domain to accurately reflect the constraint violations in the user's original units.

Parameters:

Name Type Description Default
lower_diffs NDArray[float64]

The differences between the linear constraint values and their lower bounds.

required
upper_diffs NDArray[float64]

The differences between the linear constraint values and their upper bounds.

required

Returns:

Type Description
tuple[NDArray[float64], NDArray[float64]]

A tuple containing the transformed lower and upper differences.

ropt.transforms.base.ObjectiveTransform

Bases: ABC

Abstract base class for objective transformations.

This class defines the interface for transforming objective values between the user-defined domain and the optimizer's internal domain. Concrete implementations of this class handle the specific logic for each type of objective transformation.

When implementing an objective transformation, the following aspects must be considered:

  • Objective Value Transformation: Mapping objective values between the user and optimizer domains. This is achieved by overriding the to_optimizer and from_optimizer methods.
  • Weighted Objective Transformation: The optimizer works with a single, weighted objective value. If the transformation affects the weighted objective, the weighted_objective_from_optimizer method should be overridden to handle this.

to_optimizer abstractmethod

to_optimizer(
    objectives: NDArray[float64],
) -> NDArray[np.float64]

Transform objective values to the optimizer domain.

This method maps objective values from the user-defined domain to the optimizer's internal domain. This transformation might involve scaling, shifting, or other operations to improve the optimizer's performance.

The input objectives may be a multi-dimensional array. It is assumed that the last axis of the array represents the objective values. If this is not the case, you must adjust the order of the axes before and after calling this method.

Parameters:

Name Type Description Default
objectives NDArray[float64]

The objective values in the user domain to be transformed.

required

Returns:

Type Description
NDArray[float64]

The transformed objective values in the optimizer domain.

from_optimizer abstractmethod

from_optimizer(
    objectives: NDArray[float64],
) -> NDArray[np.float64]

Transform objective values to the user domain.

This method maps objective values from the optimizer's internal domain back to the user-defined domain. This transformation reverses any scaling, shifting, or other operations that were applied to improve the optimizer's performance.

The input objectives may be a multi-dimensional array. It is assumed that the last axis of the array represents the objective values. If this is not the case, you must adjust the order of the axes before and after calling this method.

Parameters:

Name Type Description Default
objectives NDArray[float64]

The objective values in the optimizer domain to be transformed.

required

Returns:

Type Description
NDArray[float64]

The transformed objective values in the user domain.

weighted_objective_from_optimizer

weighted_objective_from_optimizer(
    weighted_objective: NDArray[float64],
) -> NDArray[np.float64]

Transform the weighted objective to the user domain.

The optimizer uses a single, weighted objective value evaluated in the optimizer domain. This method reverses that transformation, mapping the weighted objective back to the user domain.

For example, if the transformation to the optimizer domain involved a sign change to convert a maximization problem into a minimization problem, this method would change the sign back.

Note

This method may be applied to the weighted objective itself or to its gradient. Therefore, the input may be a scalar or a vector of values.

Parameters:

Name Type Description Default
weighted_objective NDArray[float64]

The weighted objective value(s) to transform.

required

Returns:

Type Description
NDArray[float64]

The transformed weighted objective value(s).

ropt.transforms.base.NonLinearConstraintTransform

Bases: ABC

Abstract base class for nonlinear constraint transformations.

This class defines the interface for transforming nonlinear constraint values between the user-defined domain and the optimizer's internal domain. Concrete implementations of this class handle the specific logic for each type of nonlinear constraint transformation.

When implementing a nonlinear constraint transformation, the following aspects must be considered:

  • Constraint Value Transformation: Mapping constraint values between the user and optimizer domains. This is achieved by overriding the to_optimizer and from_optimizer methods.
  • Right-Hand-Side Bound Transformation: Mapping the right-hand-side bounds of the constraints between the user and optimizer domains. This is achieved by overriding the bounds_to_optimizer method.
  • Constraint Difference Transformation: To report violations of nonlinear constraints, the differences between constraint values and their lower/upper bounds must be transformed from the optimizer domain back to the user domain. This is done using the nonlinear_constraint_diffs_from_optimizer method.

to_optimizer abstractmethod

to_optimizer(
    constraints: NDArray[float64],
) -> NDArray[np.float64]

Transform constraint values to the optimizer domain.

This method maps nonlinear constraint values from the user-defined domain to the optimizer's internal domain. This transformation might involve scaling, shifting, or other operations to improve the optimizer's performance.

The input constraints may be a multi-dimensional array. It is assumed that the last axis of the array represents the constraint values. If this is not the case, you must adjust the order of the axes before and after calling this method.

Parameters:

Name Type Description Default
constraints NDArray[float64]

The nonlinear constraint values in the user domain to be transformed.

required

Returns:

Type Description
NDArray[float64]

The transformed nonlinear constraint values in the optimizer domain.

from_optimizer abstractmethod

from_optimizer(
    constraints: NDArray[float64],
) -> NDArray[np.float64]

Transform constraint values to the user domain.

This method maps nonlinear constraint values from the optimizer's internal domain back to the user-defined domain. This transformation reverses any scaling, shifting, or other operations that were applied to improve the optimizer's performance.

The input constraints may be a multi-dimensional array. It is assumed that the last axis of the array represents the constraint values. If this is not the case, you must adjust the order of the axes before and after calling this method.

Parameters:

Name Type Description Default
constraints NDArray[float64]

The nonlinear constraint values in the optimizer domain to be transformed.

required

Returns:

Type Description
NDArray[float64]

The transformed nonlinear constraint values in the user domain.

bounds_to_optimizer abstractmethod

bounds_to_optimizer(
    lower_bounds: NDArray[float64],
    upper_bounds: NDArray[float64],
) -> tuple[NDArray[np.float64], NDArray[np.float64]]

Transform the right-hand-side bounds to the optimizer domain.

This method transforms the lower and upper bounds of the nonlinear constraints from the user-defined domain to the optimizer's internal domain. This transformation is necessary to ensure that the constraints remain valid after the variables have been transformed.

For example, if constraint values are scaled or shifted in the optimizer domain, the bounds must be adjusted accordingly.

Parameters:

Name Type Description Default
lower_bounds NDArray[float64]

The lower bounds on the right-hand-side values in the user domain.

required
upper_bounds NDArray[float64]

The upper bounds on the right-hand-side values in the user domain.

required

Returns:

Type Description
tuple[NDArray[float64], NDArray[float64]]

A tuple containing the transformed bounds.

nonlinear_constraint_diffs_from_optimizer abstractmethod

nonlinear_constraint_diffs_from_optimizer(
    lower_diffs: NDArray[float64],
    upper_diffs: NDArray[float64],
) -> tuple[NDArray[np.float64], NDArray[np.float64]]

Transform nonlinear constraint differences to the user domain.

This method transforms the differences between nonlinear constraint values and their lower/upper bounds from the optimizer's internal domain back to the user-defined domain. These differences are used to report constraint violations.

For example, if constraint values are scaled in the optimizer domain, the differences between the constraint values and their bounds must be scaled back to the user domain to accurately reflect the constraint violations in the user's original units.

Parameters:

Name Type Description Default
lower_diffs NDArray[float64]

The differences between the nonlinear constraint values and their lower bounds.

required
upper_diffs NDArray[float64]

The differences between the nonlinear constraint values and their upper bounds.

required

Returns:

Type Description
tuple[NDArray[float64], NDArray[float64]]

A tuple containing the transformed lower and upper differences.

ropt.transforms.VariableScaler

Bases: VariableTransform

Linearly scales and shifts variables between domains.

This class implements a linear transformation for variables, allowing for scaling and shifting between the user-defined domain and the optimizer's internal domain. The transformation is defined by a scaling factor and an offset for each variable.

The transformation from the user domain to the optimizer domain is given by:

\[x_{opt} = \frac{(x_{\textrm{user}} - \textrm{offset})}{\textrm{scale}}\]

The transformation from the optimizer domain back to the user domain is:

\[x_{user} = x_{\textrm{opt}} * {\textrm{scale}} + {\textrm{offset}}\]

This transformation can be used to improve the performance of the optimizer by working with variables that are scaled to a more suitable range or centered around a specific value.

__init__

__init__(
    scales: NDArray[float64] | None,
    offsets: NDArray[float64] | None,
) -> None

Initialize the variable scaler.

This scaler applies a linear transformation to variables, defined by scaling factors and offset values.

If both scales and offsets are provided, they are broadcasted to ensure they have the same length.

Parameters:

Name Type Description Default
scales NDArray[float64] | None

The scaling factors for each variable.

required
offsets NDArray[float64] | None

The offset values for each variable.

required

to_optimizer

to_optimizer(
    values: NDArray[float64],
) -> NDArray[np.float64]

Transform variable values to the optimizer domain.

This method applies the linear scaling and offset transformation to variable values, mapping them from the user-defined domain to the optimizer's internal domain.

The transformation is defined as: x_opt = (x_user - offset) / scale.

The input values may be a multi-dimensional array. It is assumed that the last axis of the array represents the variable values. If this is not the case, you must adjust the order of the axes before and after calling this method.

Parameters:

Name Type Description Default
values NDArray[float64]

The variable values in the user domain to be transformed.

required

Returns:

Type Description
NDArray[float64]

The transformed variable values in the optimizer domain.

from_optimizer

from_optimizer(
    values: NDArray[float64],
) -> NDArray[np.float64]

Transform variable values to the user domain.

This method applies the inverse linear scaling and offset transformation to variable values, mapping them from the optimizer's internal domain back to the user-defined domain.

The transformation is defined as: x_user = x_opt * scale + offset.

The input values may be a multi-dimensional array. It is assumed that the last axis of the array represents the variable values. If this is not the case, you must adjust the order of the axes before and after calling this method.

Parameters:

Name Type Description Default
values NDArray[float64]

The variable values in the optimizer domain to be transformed.

required

Returns:

Type Description
NDArray[float64]

The transformed variable values in the user domain.

magnitudes_to_optimizer

magnitudes_to_optimizer(
    values: NDArray[float64],
) -> NDArray[np.float64]

Transform perturbation magnitudes to the optimizer domain.

This method transforms perturbation magnitudes, typically used in stochastic gradient-based algorithms, from the user-defined domain to the optimizer's internal domain. The transformation ensures that the perturbations are applied correctly in the optimizer's space, which may have different scaling or units than the user domain.

The transformation is defined as: x_opt = x_user / scale.

Parameters:

Name Type Description Default
values NDArray[float64]

The perturbation magnitudes in the user domain.

required

Returns:

Type Description
NDArray[float64]

The transformed perturbation magnitudes in the optimizer domain.

linear_constraints_to_optimizer

linear_constraints_to_optimizer(
    coefficients: NDArray[float64],
    lower_bounds: NDArray[float64],
    upper_bounds: NDArray[float64],
) -> tuple[
    NDArray[np.float64],
    NDArray[np.float64],
    NDArray[np.float64],
]

Transform linear constraints to the optimizer domain.

This method transforms linear constraints, defined by their coefficients and right-hand-side bounds, from the user-defined domain to the optimizer's internal domain. This transformation accounts for the scaling and shifting applied to the variables and ensures that the constraints remain valid in the optimizer's space.

The set of linear constraints can be represented by a matrix equation: \(\mathbf{A} \mathbf{x} = \mathbf{b}\).

When linearly transforming variables to the optimizer domain, the coefficients (\(\mathbf{A}\)) and right-hand-side values (\(\mathbf{b}\)) must be converted to remain valid (see also the configuration for linear constraints). If the linear transformation of the variables to the optimizer domain is given by:

\[ \hat{\mathbf{x}} = \mathbf{S} \mathbf{x} + \mathbf{o}\]

then the coefficients and right-hand-side values must be transformed as follows:

\[ \begin{align} \hat{\mathbf{A}} &= \mathbf{A} \mathbf{S}^{-1} \\ \hat{\mathbf{b}} &= \mathbf{b} + \mathbf{A}\mathbf{S}^{-1}\mathbf{o} \end{align}\]

where \(S\) is a diagonal matrix with scaling factors on the diagonal and \(o\) are the offsets.

The resulting equations are further scaled by dividing them by maximum of the absolute values of the coefficients in each equation.

Parameters:

Name Type Description Default
coefficients NDArray[float64]

The coefficient matrix of the linear constraints.

required
lower_bounds NDArray[float64]

The lower bounds on the right-hand-side values.

required
upper_bounds NDArray[float64]

The upper bounds on the right-hand-side values.

required

Returns:

Type Description
tuple[NDArray[float64], NDArray[float64], NDArray[float64]]

A tuple containing the transformed coefficient matrix and bounds.

bound_constraint_diffs_from_optimizer

bound_constraint_diffs_from_optimizer(
    lower_diffs: NDArray[float64],
    upper_diffs: NDArray[float64],
) -> tuple[NDArray[np.float64], NDArray[np.float64]]

Transform bound constraint differences to the user domain.

This method transforms the differences between variable values and their lower/upper bounds from the optimizer's internal domain back to the user-defined domain. These differences are used to report constraint violations.

For example, if variables are scaled in the optimizer domain, the differences between the variables and their bounds must be scaled back to the user domain to accurately reflect the constraint violations in the user's original units.

The transformation is defined as: x_user = x_opt * scale.

Parameters:

Name Type Description Default
lower_diffs NDArray[float64]

The differences between the variable values and their lower bounds.

required
upper_diffs NDArray[float64]

The differences between the variable values and their upper bounds.

required

Returns:

Type Description
tuple[NDArray[float64], NDArray[float64]]

A tuple containing the transformed lower and upper differences.

linear_constraints_diffs_from_optimizer

linear_constraints_diffs_from_optimizer(
    lower_diffs: NDArray[float64],
    upper_diffs: NDArray[float64],
) -> tuple[NDArray[np.float64], NDArray[np.float64]]

Transform linear constraint differences to the user domain.

This method transforms the differences between linear constraint values and their lower/upper bounds from the optimizer's internal domain back to the user-defined domain. These differences are used to report constraint violations.

This is implemented by re-scaling the equations with the weights that were determined and stored by the linear_constraints_to_optimizer method.

Parameters:

Name Type Description Default
lower_diffs NDArray[float64]

The differences between the linear constraint values and their lower bounds.

required
upper_diffs NDArray[float64]

The differences between the linear constraint values and their upper bounds.

required

Returns:

Type Description
tuple[NDArray[float64], NDArray[float64]]

A tuple containing the transformed lower and upper differences.