Optimization#

This subpackage provides optimization routines for solving OED problems, organized into three categories:

  • SciPy wrappers — general-purpose continuous optimization via SciPy’s minimize interface.

  • Binary/discrete optimization — greedy, robust, and constrained solvers for combinatorial design problems (e.g., sensor placement).

  • Policy gradient (REINFORCE) — stochastic optimization using probabilistic optimization inspired by reinforcement learning. This is suitable generally for any design for which a probability distribution (policy) is defined.

Key Classes at a Glance#

pyoed.optimization.Optimizer

Base class for Optimizers (Optimization Routines).

pyoed.optimization.OptimizerResults

Base class (empty data container) to hold optimization data/results.

pyoed.optimization.ScipyOptimizer

Unified (with PyOED optimziers) interface to the scipy.optimize.minimize optimization routines.

pyoed.optimization.ReinforceOptimizer

Implementation of the REINFORCE algorithm for binary optimization (maximization or minimization).

pyoed.optimization.create_optimizer

Given an optimizer and optimizer_configs return the optimizer with configurations as in the passed object.

Optimization Base Classes#

class OptimizerConfigs(*, debug=False, verbose=False, output_dir='./_PYOED_RESULTS_', size=None, name=None, screen_output_iter=1, file_output_iter=100)[source]#

Bases: PyOEDConfigs

Configurations class for the Optimizer abstract base class. This class inherits functionality from PyOEDConfigs and only adds new class-level variables which can be updated as needed.

See PyOEDConfigs for more details on the functionality of this class along with a few additional fields. Otherwise OptimizerConfigs provides the following fields:

Parameters:
  • verbose (bool) – a boolean flag to control verbosity of the object.

  • debug (bool) – a boolean flag that enables adding extra functionality in a debug mode

  • output_dir (str | Path) – the base directory where the output files will be saved.

  • name (str | None) – name of the optimizer. Default is None.

  • size (int | None) – dimension of the target/solution variable. Not all optimizers use this, but it is added for unification.

  • screen_output_iter (int) – iteration interval for screen output. Default is 1. Note that this should be a positive integer to enforce proper effect.

  • file_output_iter (int) – iteration interval for file output. Default is 1. Note that this should be a positive integer to enforce proper effect.

size: int | None#
name: str | None#
screen_output_iter: int#
file_output_iter: int#
__init__(*, debug=False, verbose=False, output_dir='./_PYOED_RESULTS_', size=None, name=None, screen_output_iter=1, file_output_iter=100)#
class Optimizer(configs=None)[source]#

Bases: ABC, PyOEDObject

Base class for Optimizers (Optimization Routines).

Note

Each class derived from Optimizer should have its own __init__ method in which the constructor just calls super().__init__(configs=configs) and then add any additional initialization as needed.

Parameters:

configs (dict | OptimizerConfigs | None) – (optional) configurations for the optimization object

Raises:

PyOEDConfigsValidationError – if passed invalid configs

__init__(configs=None)[source]#
validate_configurations(configs, raise_for_invalid=True)[source]#

Each simulation optimizer SHOULD implement it’s own function that validates its own configurations. If the validation is self contained (validates all configuations), then that’s it. However, one can just validate the configurations of of the immediate class and call super to validate configurations associated with the parent class.

If one does not wish to do any validation (we strongly advise against that), simply add the signature of this function to the optimizer class.

Note

The purpose of this method is to make sure that the settings in the configurations object self._CONFIGURATIONS are of the right type/values and are conformable with each other. This function is called upon instantiation of the object, and each time a configuration value is updated. Thus, this function need to be inexpensive and should not do heavy computations.

Parameters:

configs (dict | OptimizerConfigs) – configurations to validate. If a OptimizerConfigs object is passed, validation is performed on the entire set of configurations. However, if a dictionary is passed, validation is performed only on the configurations corresponding to the keys in the dictionary.

Raises:
abstractmethod solve(*args, **kwargs)[source]#

Solve the optimization problem.

Actual implementation of this solve() must be provided by each optimization class/object. Implementations should accept at least an initial guess and return an OptimizerResults instance (or a derived subclass) containing the optimal solution and convergence information.

Returns:

an instance of (or derived from) OptimizerResults holding the optimization outcome.

Return type:

OptimizerResults

class OptimizerResults[source]#

Bases: PyOEDData

Base class (empty data container) to hold optimization data/results.

Derived classes should add fields specific to their optimizer (e.g., the optimal solution x, objective value fun, convergence flag success, iteration count nit). The properties below provide a unified interface inspired by scipy.optimize.OptimizeResult; each derived class should override those it supports.

property x: ndarray#

The optimal solution of the optimization problem.

property success: bool#

Whether or not the optimizer exited successfully.

property message: str#

Description of the cause of the termination.

property fun: float#

Value of objective function at the optimal solution x.

property jac: ndarray#

Value of the Jacobian of the objective function at the optimal solution x (if available).

property hess: object#

Value of the Hessian of the objective function at the optimal solution x (if available).

property hess_inv: object#

Value of the inverse of Hessian of the objective function at the optimal solution x (if available).

property nfev: int#

Number of evaluations of the objective functions.

property njev: int#

Number of evaluations of the the Jacobian of the objective function.

property nhev: int#

Number of evaluations of the the Hessian of the objective function.

property nit: int#

Number of iterations performed by the optimizer.

__init__()#
create_optimizer(optimizer=None, optimizer_configs=None, new_configs=None)[source]#

Given an optimizer and optimizer_configs return the optimizer with configurations as in the passed object.

The logic:
  1. The optimizer:

    1. None: If both optimizer_configs and new_configs are None return None. If any of the configurations is not None, throw a warning and also return None. This is added to enable resetting optimizers in objects at instantiation.

    2. instance derived from pyoed.optimization.Optimizer: use it as is

    3. subclass of pyoed.optimization.Optimizer: create an instance

  2. The optimizer_configs:

    1. None: convert to empty dictionary

    2. dict: remove unknown keys and throw warning if unknown keys found

    3. instance derived from pyoed.optimization.OptimizerConfigs: convert to dictionary

    4. subclass of pyoed.optimization.OptimizerConfigs: create an instance and convert to dictionary

  3. new_configs (if None replace with empty dictionary):

    1. dict: update/replace known keys into optimizer_configs and throw warning if unknown keys are passed

The optimizer is instantiated (if a class is passed) with the aggregated configurations. If valid optimizer instance is passed, its configurations are updated with the aggregated configurations.

Raises:

TypeError – if the passed arguments are of invalid types.

Raises:

an error (type varies) if the optimizer failed to instantiate

Reinforcement-Learning Utilities#

The following module provides reinforcement-learning components that bridge the optimization and machine-learning worlds. These are used by ReinforceOptimizer and its binary variants.