Robust OED Routines#
We provide modules for robust OED for sensor placement assuming parametric uncertainty models. This package is expanding, but initially we provide implementations for robust OED under error model misspecification. Thus, we provide Uncertainty Parameter models as well as Robust OED functionality.
Uncertain Parameter#
Robust OED#
Approaches to solving the robust sensor placement OED problem assuming a parameterized uncertainty (robustness) model. This is the backbone of the results published in:
Ahmed Attia, Sven Leyffer, and Todd S. Munson. “Robust A-optimal experimental design for Bayesian inverse problems.” arXiv preprint arXiv:2305.03855 (2023).
- class RobustSensorPlacementBayesianInversionOEDConfigs(*, debug=False, verbose=False, output_dir='./_PYOED_RESULTS_', name=None, design=None, optimizer=<class 'pyoed.optimization.binary_optimization.robust_binary_optimization.RobustBinaryReinforceOptimizer'>, optimizer_configs=None, criterion='Robust Bayesian A-opt', inverse_problem=None, problem_is_linear=None, penalty_function=None, penalty_function_gradient=None, penalty_weight=0.0, random_seed=None, uncertain_parameter='observation-variance', uncertain_parameter_sampler='CovarianceSampler', uncertain_parameter_sampler_configs=None, uncertain_parameter_initial_sample_size=10)[source]#
Bases:
RobustInversionOEDConfigs
Configurations class for the
RobustSensorPlacementBayesianInversionOED
class. This class inherits functionality fromInversionOEDConfigs
and only adds the class-level variables below which can be updated as needed:- Parameters:
verbose (bool) – a boolean flag to control verbosity of the object.
debug (bool) – a boolean flag that enables adding extra functionlity in a debug mode
output_dir (str | Path) – the base directory where the output files will be saved.
name (str | None) – name of the OED approach/method.
inverse_problem (InverseProblem | None) –
an instance of an inverse problem
InverseProblem
. This can be a filter or a smoother object with access to underlying properties/methods/attributes of the inverse problems:prior
posterior
solve_inverse_problem()
observation_operator
observation_error_model
problem_is_linear (bool | None) –
a flag that defines how to regard the inverse problem, and consequently the OED problem. The following values are accepted:
If not set (None is passed), it will be detected by testing the linearity of the inverse problem upon instantiation.
If set to True, the underlying forward problem (both simulation model and observation operator) are linear. In this case, the posterior is Gaussian, and the posterior uncertainties (covariances) are independent from the data. Thus, the inverse problem is solved once to find the MAP point (posterior mean/mode), and the posterior covariance is defined/constructed. Note that finding the MAP point is not necessary since posterior covariances are independent from it.
If ‘False’, either the simulation model, the observation operator or both are nonlinear; In this case, the posterior is non-Gaussian, and the posterior uncertainties (covariances) are dependent on the data (through the MAP estimate). To deal with this situation, we follow one of two approaches:
The inverse problem is solved to find the MAP point (posterior mean/mode), approximate the posterior covariance (assuming a Gaussian) around the MAP, find an optimal design, and repeat.
Use KL-divergence between prior and posterior using MC estimate of KL.
criterion (None | str | Criterion) –
The optimality criterion to be optimized when solving the OED problem. This overrides the criterion in the base OED class with additional type. Specifically, this can be:
None; in this case, no criterion will be associated with the inversion OED instance (the object) upon instantiation. Thus, OED criterion association is lazy, and the user is allowed to assign the criterion after creating the inversion OED object by calling
register_optimality_criterion()
.an instance of
Criterion
which provides access to an evaluate method which evaluates the value of the optimality criterion.a string holding the name of the optimality criterion (if widely popular and implemented by PyOED). For more about passing string representation of the criterion, check the documentation of
register_optimality_criterion()
penalty_function (None | Callable) – function to evaluate the regularization/sparsification penalty (soft constraint) in the popular regularized OED optimization formulation.
penalty_function_gradient (None | Callable) – gradient/derivative of the penalty_function with respect to the design. For example, this is used for the relaxation approach where a binary design is allowed to take values in the continuous interval
[0, 1]
.penalty_weight (None | float) –
scalar penalty/regularization parameter multiplied by the penalty_function in the objective function.
Note
The OED optimization objective function is defined as the sum of optimality criterion plus a regularization term. The regularization term is the product of a penalty function with a pnalty weight.
optimizer (str | Optimizer | Type[Optimizer]) –
the optimization routine (optimizer) to register. This can be one of the following:
An optimizer instance (object that inherits :py:class`Optimizer`). In this case, the optimizer is registered as is and is updated with the passed configurations if available.
The class (subclass of
Optimizer
) to be used to instantiate the optimizer. To see available built-in optimization routines, see/callshow_supported_optimizers()
.The name of the optimizer (str). This has to match the name of one of the available optimization routine. To see available built-in optimization routines, see/call
show_supported_optimizers()
.
optimizer_configs (None | dict | OptimizerConfigs | Type[OptimizerConfigs]) –
the configurations of the optimization routine. This can be one of the following:
None, in this case configurations are discarded, and whatever default configurations of the selected/passed optimizer are employed.
A dict holding full/partial configurations of the selected optimizer. These are either used to instantiate or update the optimizer configurations based on the type of the passed optimizer.
A class providing implementations of the configurations (this must be a subclass of
OptimizerConfigs
.An instance of a subclass of
OptimizerConfigs
which is to set/udpate optimizer configurations.
random_seed (int | None) – random seed to be used to globally set seeds of the underlying random number generators. This affects both the optimizer and the uncertain parameter sampler (if accepting random seed configuration).
uncertain_parameter (str) –
name of the uncertain parameter. Currently the following parameters are supported by
RobustSensorPlacementBayesianInversionOED
:’observation-variance’: the uncertain parameter is a hyperparameter that defines the observation error covariance matrix
’prior-variance’: the uncertain parameter is a hyperparameter that defines the prior covariance matrix
uncertain_sampler_sampler – A sampler object (derived from
ParameterSampler
) that enables evaluating and sampling the uncertain parameter given a hyperparameter value.uncertain_parameter_initial_sample_size (int) – The sample size used to create an initial sample of the uncertain parameter which is passed to the robust optimizer.
- random_seed: int | None#
- uncertain_parameter: str#
- uncertain_parameter_sampler: str | ParameterSampler | Type[ParameterSampler]#
- uncertain_parameter_sampler_configs: None | dict | ParameterSamplerConfigs | Type[ParameterSamplerConfigs]#
- uncertain_parameter_initial_sample_size: int#
- __init__(*, debug=False, verbose=False, output_dir='./_PYOED_RESULTS_', name=None, design=None, optimizer=<class 'pyoed.optimization.binary_optimization.robust_binary_optimization.RobustBinaryReinforceOptimizer'>, optimizer_configs=None, criterion='Robust Bayesian A-opt', inverse_problem=None, problem_is_linear=None, penalty_function=None, penalty_function_gradient=None, penalty_weight=0.0, random_seed=None, uncertain_parameter='observation-variance', uncertain_parameter_sampler='CovarianceSampler', uncertain_parameter_sampler_configs=None, uncertain_parameter_initial_sample_size=10)#
- class RobustSensorPlacementBayesianInversionOED(configs=None)[source]#
Bases:
RobustInversionOED
,RandomNumberGenerationMixin
This is the main implementation of researech development on robust OED presented in [1].
This formulation of robust OED considers an optimal design as the one that maximizes (minimizing) a predefined utility function against works case scenario posed by some uncertain parameter. The optimization problem is thus defined as a max-min (or min-max) where the outer optimization problem is optimizes (maximize/minimize) the utility with respect (w.r.t.) to the design, and the inner problem optimizes (minimizes/maximizes) the utility function w.r.t. the uncertain parameter.
The parent class
BinaryOED
is general as it applies to any registered criterion with binary design (regardless of how it affects the inverse problem) or utility function (seepyoed.oed.utility_functions.UtilityFunction
). This implementation, on the other hand, specializes the design to the case of sensor placement, and thus the design is binary by definition, and the criterion (utility function) is created upon instantiation based on the passed configurations. The uncertain parameter can be technically anythin parameter in the inverse problem (prior mean, prior variance, observation noise parameters, etc.). Though, the user must provide (in the utility function implementation) proper gradients (of the utility function with respect to the uncertain parameter).References:
Ahmed Attia, Sven Leyffer, and Todd S. Munson. “Robust A-optimal experimental design for Bayesian inverse problems.” arXiv preprint arXiv:2305.03855 (2023).
- validate_configurations(configs, raise_for_invalid=True)[source]#
Validate passed configurations.
- Parameters:
configs (dict | RobustSensorPlacementBayesianInversionOEDConfigs) – configurations to validate. If a
RobustSensorPlacementBayesianInversionOEDConfigs
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.
- update_configurations(**kwargs)[source]#
Take any set of keyword arguments, and lookup each in the configurations, and update as nessesary/possible/valid
- Raises:
TypeError – if any of the passed keys in kwargs is invalid/unrecognized
- Remarks:
Generally, we don’t want actual implementations in abstract classes, however, this one is provided as good guidance. Derived classes can rewrite it and/or provide additional updates.
- register_optimizer(optimizer='RobustBinaryReinforceOptimizer', optimizer_configs=None)[source]#
Register (and return) an optimization routine, and make sure the objective function in the optimization routine is set to the objctive function of this objective
objective_function_value()
. The objective function of the optimizer (and its derivatives) are set to the objective function and derivatives implemented here unless the objective function is passed explicitly in the configurations. This method sets a reference to the optimizer and update the underlying configurations accordingly.Note
If the optimizer passed is an instance of (derived from)
Optimizer
, and valid configurations optimizer_configs is passed, the optimizer is updated with the passed configurations by calling optimizer.update_configurations(optimizer_configs).Warning
If the optimizer passed is an instance of (derived from)
Optimizer
, the responsibility is on the developer/user to validate the contents of the optimizer.- Parameters:
optimizer (str | Optimizer | Type[Optimizer]) –
the optimization routine (optimizer) to register. This can be one of the following:
An optimizer instance (object that inherits :py:class`Optimizer`). In this case, the optimizer is registered as is and is updated with the passed configurations if available.
The class (subclass of
Optimizer
) to be used to instantiate the optimizer. To see available built-in optimization routines, see/callshow_supported_optimizers()
.The name of the optimizer (str). This has to match the name of one of the available optimization routine. To see available built-in optimization routines, see/call
show_supported_optimizers()
.
optimizer_configs (None | dict | OptimizerConfigs | Type[OptimizerConfigs]) –
the configurations of the optimization routine. This can be one of the following:
None, in this case configurations are discarded, and whatever default configurations of the selected/passed optimizer are employed.
A dict holding full/partial configurations of the selected optimizer. These are either used to instantiate or update the optimizer configurations based on the type of the passed optimizer.
A class providing implementations of the configurations (this must be a subclass of
OptimizerConfigs
.An instance of a subclass of
OptimizerConfigs
which is to set/udpate optimizer configurations.
- Returns:
the registered optimizer.
- Raises:
TypeError – if the type of passed optimizer and/or configurations are/is not supported
- Return type:
- register_uncertain_parameter_sampler(sampler='CovarianceSampler', sampler_configs=None, update_optimizer=True)[source]#
Register (and return ) the uncertain parameter sampler and update the optimizer settings if available.
- update_design(design)[source]#
Update the components of the OED (and inverse) problem with the passed design. This method updates both observation operator and observation error model of the underlying inverse problem with the passed design.
Note
This method need to be replaced with design-specific impelentation for any design different from observational experimental design.
- Parameters:
design – an observational design vector conformable with the observation operator and the observation error covariance matrix operator.
- update_random_number_generators(random_seed)[source]#
Update random seeds of underlying random number gnerators (both local and optimizer if supported)
- register_optimality_criterion(criterion=None)[source]#
Validate and set the robust OED optimality criterion as described by criterion, update the configurations object with the criterion, and return the new criterion.
Note
This method only creates a valid criterion (instance of :py:class`Criterion` if a string is passed (name of the criterion)), and then calls super class (parent) to set the criterion. For additional details see e.g.,
pyoed.oed.InversionOED.register_optimality_criterion
- uncertain_parameter_manager(uncertain_parameter_val=None, uncertain_hyperparameter_val=None)[source]#
Creat an return a context manager that enables updating the uncertain parameter to the passed value, execute needed code and then reset the uncertain parameter.
Assuming obj is this object, and val is the uncertain parameter value one wants to run the method obj.run_code(), the following code can be used
with obj.uncertain_parameter_manager(val) as mngr: mngr.run_code()
- Returns:
a reference to self that enables calling any function under self and then automatically, the uncertain parameter is reset.
- set_uncertain_parameter(uncertain_parameter_val, uncertain_hyperparameter_val=None)[source]#
Update the inverse problem based on the passed value of the uncertain parameter.
Warning
Currently the uncertain parameter (the current uncertain parameter value) is unrecoverable. This means, that the passed uncertain_parameter_val takes place, and the current uncertain parameter value in the inverse problem is lost.
- objective_function_value(design, uncertain_parameter_val, reset_parameter_val=True)[source]#
Evaluate the value of the OED objective function (regularized utility function) given the passed observational design and the value(s) of the uncertain parameter; this is similar to
BinaryOED.objective_function()
, where the uncertainty parameter is updated first, then the objective is calculated, and then the uncertain parameter is reset to the original if needed- Parameters:
design – an observational design vector conformable with the observation operator and the observation error covariance matrix operator. This method accepts a design that is either
binary
orrelaxed
(over the interval [0, 1]), and uses the design to properly relax the OED optimization objective (with the right limits at the boundary).uncertain_parameter_val – value of the uncertain parameter; this must match the registered uncertain parameter and sampler.
reset_parameter_val (bool) – if True, the current values of the uncertain parameter are restored after evaluating the objective function value
- Returns:
value of the registered OED optimality criterion
- Raises:
TypeError
is raised if no uncertain_parameter is registered.- Raises:
TypeError
is raised if no uncertain_parameter_sampler is registered with a valid type. Only instances ofParameterSampler
are accepted.- Raises:
TypeError
is raised if no optimality criterion has been registered yet- Raises:
TypeError
is raised if the either the uncertain parameter or the associated sampler is missing.
- objective_function_grad_parameter(design, uncertain_parameter_val, reset_parameter_val=True)[source]#
Evaluate the gradient of the
objective_function()
(regularized utility/criterion) with respect to the uncertainty hyperparameter. The design is set and is regarded as a constant of course.- Parameters:
design – an observational design vector conformable with the observation operator and the observation error covariance matrix operator
uncertain_parameter_val – value of the uncertain parameter; this must match the registered uncertain parameter and sampler.
reset_parameter_val (bool) – if True, the current values of the uncertain parameter are restored after evaluating the objective function value
verbose (bool)
- Returns:
gradient of the registered OED optimality criterion (penalized based on the registered penalty function and parameter), with respect to the uncertainty parameter
- Raises:
TypeError
is raised if no uncertain_parameter_name is registered.- Raises:
TypeError
is raised if no uncertain_parameter_sampler is registered with a valid type. Only instances ofParameterSampler
are accepted.- Raises:
:py:class`TypeError` is raised if penalty function/term is not properly set.
- Raises:
:py:class`TypeError` is raised if no valid optimality criterion object is set in the configurations dictionary.
- Raises:
:py:class`TypeError` is raised if a criterion is registerd but it does not provide a :py:meth:grad_uncertainty` function.
- objective_function_grad_design(design, uncertain_parameter_val, reset_parameter_val=True)[source]#
Derivative of the objective function (that is the regularized utility/criterion, e.g., trace of FIM for A-opt) with respect to the relaxed design. This is used when the OED optimization problem is solved with relaxation of the design allowing the design to take any value in the interval [0, 1] and thus following a gradient descent/ascent direction for optimization over the design. The gradient is thus \(\nabla_{d} \mathcal{J(d, \lambda)}\) where \(d\) is the design, \(\lambda\) is the uncertain parameter, and \(\mathcal{J}\) is the OED objective (A-opt etc.) evaluated by
objective_function()
.- Parameters:
design – an observational design vector conformable with the observation operator and the observation error covariance matrix operator. This is intended to be a relaxed design (over the interval [0, 1]),
uncertain_parameter_val – value of the uncertain parameter; this must match the registered uncertain parameter and sampler.
reset_parameter_val (bool) – if True, the current values of the uncertain parameter are restored after evaluating the objective function value.
oed_evaluation_method (str) – how to evaluate the criterion; ‘exact’ or ‘randomized’. This is currently used only if the registered optimality criterion is A- or D-optimality. More will be added gradually.
oed_evaluation_randomization_settings (dict) – a dictionary holding settings of the randomization method (to approximate the optimality criterion or the utility function) used only if oed_evaluation_method is set to ‘randomized’.
verbose (bool) – screen verbosity True/False
- Returns:
gradient of the registered OED optimality criterion w.r.t the relaxed design
- Raises:
TypeError
is raised if no uncertain_parameter_name is registered.- Raises:
TypeError
is raised if no uncertain_parameter_sampler is registered with a valid type. Only instances ofParameterSampler
are accepted.- Raises:
:py:class`TypeError` is raised if penalty function/term is not properly set.
- Raises:
:py:class`TypeError` is raised if no valid optimality criterion object is set in the configurations dictionary.
- Raises:
:py:class`TypeError` is raised if a criterion is registerd but it does not provide a :py:meth:grad_design` function.
- solve(init_guess=None)[source]#
Solve the robust OED optimization problem by using the registered optimization procedure.
- Parameters:
init_guess – initial guess (e.g., initial design, initial policy parameters)
- Returns:
an instance of
RobustSensorPlacementBayesianInversionOEDResults
holding results obtianed by solving the robust OED optimization problem.- Raises:
TypeError – if no valid optimizer is registered
- static show_supported_optimizers()[source]#
Print out optimization routines available to choose from. In addition to those shown here, the user can register fully instantiated optimizer (instance of
Optimizer
) by callingregister_optimizer()
.- Returns:
a list of supported optimization routines names
- static show_supported_uncertain_parameters()[source]#
Print out uncertain parameters available to choose from.
- property objective_function_offset#
Numeric offset added to the value of the objective (shifting)
- property objective_function_scaling_factor#
A scaling factor of (to be multiplied by) the objective function self.objective_function
- property uncertain_parameter_name#
Name of the unceratin parameter
- property uncertain_parameter_sampler#
Handle to the sampler of the uncertain parameter problem
- property uncertain_parameter_size#
Handle to the sampler of the uncertain parameter problem
- property uncertain_parameter_bounds#
Handle to the sampler of the uncertain parameter problem
- property supported_optimizers#
- property supported_uncertain_parameters#
- property supported_uncertain_parameter_samplers#
- class RobustSensorPlacementBayesianInversionOEDResults(*, oed_problem=None, optimization_results=None)[source]#
Bases:
RobustInversionOEDResults
Base class for objects holding results of
- Parameters:
optimization_results (Type[OptimizerResults] | None) – the results object returned from calling the optimizer.
oed_problem (Type[InversionOED] | None) – instance of a class derived from
OED
, such asInversionOED
- __init__(*, oed_problem=None, optimization_results=None)#