Penalty Coefficients
PenaltyCoefficient objects are used to represent the penalty coefficients \(\vc_{\vg}\) and \(\vc_{\vh}\), required by formulations such as the QuadraticPenalty
and AugmentedLagrangian formulations.
In Cooper, penalty coefficients are wrappers around a torch.Tensor. Notably, they do not require gradients, as they are not optimized.
The cooper.penalty_coefficients module provides the following types of penalty coefficients:
DensePenaltyCoefficient: Models each penalty coefficient individually.IndexedPenaltyCoefficient: Similar toDensePenaltyCoefficient, but allows fetching and updating penalty coefficients by index. This is useful when constraints are sampled, so that the required penalty coefficients change at each iteration.
Linking constraints and penalty coefficients
Constraint objects require an associated PenaltyCoefficient object to be passed to the constructor when the problem formulation demands it. You can check this requirement using the expects_penalty_coefficient attribute of a Formulation subclass.
penalty_coefficient = ...
constraint = cooper.Constraint(
penalty_coefficient=penalty_coefficient,
constraint_type=cooper.ConstraintType.INEQUALITY,
formulation_type=cooper.formulations.QuadraticPenalty,
)
Note
The helper methods CMP.penalty_coefficients and CMP.named_penalty_coefficients allow iteration over the penalty coefficients associated with constraints registered in a CMP.
For more details, see Registering constraints in a CMP.
Consider the following Quadratic Penalty formulation of a constrained optimization problem:
where \(\vc_{\vg}\) and \(\vc_{\vh}\) are the penalty coefficients associated with the inequality and equality constraints, respectively.
In Cooper, PenaltyCoefficient objects represent the vectors \(\vc_{\vg}\) and \(\vc_{\vh}\), with one coefficient for each constraint. Alternatively, Cooper also supports scalar-valued penalty coefficients, which apply a shared coefficient across all constraints (i.e., \(\vc_{\vg} = c_g \mathbf{1}\) and \(\vc_{\vh} = c_h \mathbf{1}\)).
Since it is often desirable to increase the penalty coefficient over the optimization process, Cooper provides a scheduler mechanism to do so. For more information, see Penalty Coefficient Updaters.
Initialization
To initialize a PenaltyCoefficient, you can pass either a torch.Tensor of shape (num_constraints, ) or a scalar tensor to the init argument.
penalty_coefficient = cooper.penalty_coefficients.DensePenaltyCoefficient(
init=torch.ones(10, device="cuda", dtype=torch.float32)
)
penalty_coefficient = cooper.penalty_coefficients.IndexedPenaltyCoefficient(
init=torch.tensor(1.0, device="cuda", dtype=torch.float32)
)
Evaluating a PenaltyCoefficient
Similar to multipliers, penalty coefficients can be evaluated using __call__(). For example:
# `DensePenaltyCoefficient`s do not require arguments during evaluation
penalty_coefficient_value = penalty_coefficient()
# `IndexedPenaltyCoefficient`s require indices for evaluation
indices = torch.tensor([1, 2, 4, 6])
penalty_coefficient_value = penalty_coefficient(indices)
- class cooper.penalty_coefficients.PenaltyCoefficient(init)[source]
Abstract class for constant (non-trainable) penalty coefficients.
- Parameters:
init (
Tensor) – Value of the penalty coefficient.- Raises:
ValueError – If
inithas two or more dimensions.
- to(*args, **kwargs)[source]
Move the penalty coefficient to a new
deviceand/or change itsdtype.- Return type:
Self
- sanity_check()[source]
Check that the penalty coefficient is well-formed.
- Raises:
ValueError – If the penalty coefficient contains negative entries.
- Return type:
Dense Penalty Coefficients
The DensePenaltyCoefficient class wraps a tensor of penalty coefficients, ensuring that all coefficients are accessed during each evaluation.
Indexed Penalty Coefficients
Similar to IndexedMultipliers, IndexedPenaltyCoefficients allow fetching and updating the penalty coefficients by index. Given indices idx, the __call__() method of an IndexedPenaltyCoefficient object returns the penalty coefficients corresponding to the
indices in idx.
- class cooper.penalty_coefficients.IndexedPenaltyCoefficient(init)[source]
Constant (non-trainable) penalty coefficients. When called, indexed penalty coefficients accept a tensor of indices and return the value of the penalty for a subset of constraints.
- __call__(indices)[source]
Return the current value of the penalty coefficient at the provided indices.
- Parameters:
indices (
Tensor) – Tensor of indices for which to return the penalty coefficient.- Raises:
ValueError – If
indicesis not of typetorch.long.- Return type:
Checkpointing
To save the current penalty coefficients of a CMP, use the state_dict() method to create a state checkpoint. Later, you can restore this state using load_state_dict(). This process captures the multiplier and penalty coefficient values (see CMP Checkpointing for details).
Penalty Coefficient Updaters
Penalty coefficient updaters are used to adjust penalty coefficients during optimization based on measurements of constraint violations. Cooper provides two feasibility-driven updaters: MultiplicativePenaltyCoefficientUpdater and AdditivePenaltyCoefficientUpdater, both of which increase the penalty coefficients when constraint violations exceed a specified tolerance.
- class cooper.penalty_coefficients.PenaltyCoefficientUpdater[source]
Abstract class for updating the penalty coefficient of a constraint.
- step(observed_constraints)[source]
Trigger updates on the penalty coefficients for each of the
observed_constraints.For each constraint in
observed_constraints, this method determines whether its penalty coefficient should be updated. The decision depends on properties like whether the constraint contributes to primal/dual updates and the availability of strict violation measurements.Primal vs Dual Contributions
For formulations expecting multipliers (e.g., AugmentedLagrangian), updates occur if:
The constraint contributes to the dual update, OR
It contributes to the primal update and has a strict violation measurement.
For primal-only formulations (e.g., QuadraticPenalty), updates occur only if the constraint contributes to the primal update.
- Parameters:
observed_constraints (
dict[Constraint,ConstraintState]) – Dictionary withConstraintinstances as keys andConstraintStateinstances as values (containing tensors \(\vg(\vx_t)\) and \(\vh(\vx_t)\)).- Return type:
Multiplicative Penalty Coefficient Updater
The MultiplicativePenaltyCoefficientUpdater multiplies the penalty coefficient by a growth factor when the corresponding entries in violation exceed a prescribed tolerance.
- class cooper.penalty_coefficients.MultiplicativePenaltyCoefficientUpdater(growth_factor=1.01, violation_tolerance=0.0001, has_restart=True)[source]
Multiplicative updater for
PenaltyCoefficients.The penalty coefficient is updated by multiplying it by
growth_factorwhen the constraint violation is larger thanviolation_tolerance.Based on Algorithm 17.4 in Nocedal and Wright [NW06].
- Parameters:
growth_factor (
float) – The factor by which the penalty coefficient is multiplied when the constraint is violated beyondviolation_tolerance.violation_tolerance (
float) – The tolerance for the constraint violation. If the violation is smaller than this tolerance, the penalty coefficient is not updated. The comparison is done at the constraint-level (i.e., each entry of the violation tensor). For equality constraints, the absolute violation is compared to the tolerance. All constraint types use the strict violation (when available) for the comparison.has_restart (
bool) – Whether to restart the penalty coefficient to its initial value when the inequality constraint is satisfied. This is only applicable to inequality constraints.
- Raises:
ValueError – If the violation tolerance is negative.
Additive Penalty Coefficient Updater
The AdditivePenaltyCoefficientUpdater increases the penalty coefficient by a fixed amount when the corresponding entries in violation exceed a prescribed tolerance.
- class cooper.penalty_coefficients.AdditivePenaltyCoefficientUpdater(increment=1.0, violation_tolerance=0.0001, has_restart=True)[source]
Additive updater for
PenaltyCoefficients.The penalty coefficient is updated by adding
incrementwhen the constraint violation is larger thanviolation_tolerance.- Parameters:
increment (
float) – The constant value by which the penalty coefficient is added when the constraint is violated beyondviolation_tolerance.violation_tolerance (
float) – The tolerance for the constraint violation. If the violation is smaller than this tolerance, the penalty coefficient is not updated. The comparison is done at the constraint-level (i.e., each entry of the violation tensor). For equality constraints, the absolute violation is compared to the tolerance. All constraint types use the strict violation (when available) for the comparison.has_restart (
bool) – Whether to restart the penalty coefficient to its initial value when the inequality constraint is satisfied. This is only applicable to inequality constraints.
- Raises:
ValueError – If the violation tolerance is negative.
Using Penalty Coefficient Updaters
To use a PenaltyCoefficientUpdater, follow these steps:
Instantiate the updater with the desired parameters.
Call
penalty_updater.step(), providing the observed constraints.
Example:
penalty_updater = cooper.penalty_coefficients.MultiplicativePenaltyCoefficientUpdater(
growth_factor=2.0,
violation_tolerance=1e-3,
has_restart=True
)
roll_out = cooper_optimizer.roll(...)
penalty_updater.step(roll_out.cmp_state.observed_constraints)