-
Notifications
You must be signed in to change notification settings - Fork 14
Objective Sensitivity #282
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 10 commits
608ca25
c96e2c0
e459641
f243eb8
0d2ec33
6d2efcf
f366f2d
1e2ac7c
d4abb62
d0bd2c6
a108599
ad3d72c
3e09271
a15f6f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -117,4 +117,70 @@ DiffOpt.reverse_differentiate!(model) | |
@show MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(p)) == MOI.Parameter(direction_x * 3 / pc_val) | ||
@show abs(MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(pc)).value - | ||
-direction_x * 3 * p_val / pc_val^2) < 1e-5 | ||
``` | ||
|
||
## Calculating objective sensitivity with respect to parameters (currently only supported for Nonlinear Programs) | ||
|
||
Consider a differentiable model with parameters `p` and `pc` as in the previous example: | ||
|
||
```julia | ||
using JuMP, DiffOpt, HiGHS | ||
|
||
model = Model(() -> DiffOpt.diff_optimizer(Ipopt.Optimizer)) | ||
set_silent(model) | ||
|
||
p_val = 4.0 | ||
pc_val = 2.0 | ||
@variable(model, x) | ||
@variable(model, p in Parameter(p_val)) | ||
@variable(model, pc in Parameter(pc_val)) | ||
@constraint(model, cons, pc * x >= 3 * p) | ||
@objective(model, Min, x^4) | ||
optimize!(model) | ||
|
||
direction_p = 3.0 | ||
MOI.set(model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), Parameter(direction_p)) | ||
DiffOpt.forward_differentiate!(model) | ||
|
||
``` | ||
|
||
Using Lagrandian duality we could already calculate the objective sensitivity with respect to parameters that appear in the RHS of the constraints (e.g, `cons` in this case for parameter `p`). | ||
|
||
On the other hand, if the parameter appears in the LHS of the constraints, we can calculate the objective sensitivity with respect to the parameter using: the sensitivities of the variables with respect to the parameter, \( \frac{\partial x}{\partial p} \), and the gradient of the objective with respect to the variables \( \frac{\partial f}{\partial x} \): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would suggest to mention that this is a consequence of the chain-rule |
||
|
||
```math | ||
\frac{\partial f}{\partial p} = \frac{\partial f}{\partial x} \frac{\partial x}{\partial p} | ||
``` | ||
|
||
In order to calculate the objective perturbation with respect to the parameter perturbation vector, we can use the following code: | ||
|
||
```julia | ||
# Always a good practice to clear previously set sensitivities | ||
DiffOpt.empty_input_sensitivities!(model) | ||
|
||
MOI.set(model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), Parameter(3.0)) | ||
MOI.set(model, DiffOpt.ForwardConstraintSet(), ParameterRef(p_c), Parameter(3.0)) | ||
DiffOpt.forward_differentiate!(model) | ||
|
||
MOI.get(model, DiffOpt.ForwardObjectiveSensitivity()) | ||
``` | ||
|
||
In the backward mode, we can calculate the parameter perturbation with respect to the objective perturbation: | ||
|
||
```julia | ||
# Always a good practice to clear previously set sensitivities | ||
DiffOpt.empty_input_sensitivities!(model) | ||
|
||
MOI.set( | ||
model, | ||
DiffOpt.ReverseObjectiveSensitivity(), | ||
0.1, | ||
) | ||
|
||
DiffOpt.reverse_differentiate!(model) | ||
|
||
MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(p)) | ||
``` | ||
|
||
It is important to note that the (reverse) parameter perturbation given an objective perturbation is somewhat equivalent to the perturbation with respect to solution (since one can be calculated from the other). Therefore, one cannot set both the objective sensitivity (`DiffOpt.ReverseObjectiveSensitivity`) and the solution sensitivity (e.g. `DiffOpt.ReverseVariablePrimal`) at the same time. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what happens if we set the reverse parameter perturbation both for the objective and for the solution? Does DiffOpt returns an error, or does it keep the reverse parameter set the latest? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice catch, it returns an error, I will add a note here |
||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ | |
dp::Dict{MOI.ConstraintIndex,Float64} = Dict{MOI.ConstraintIndex,Float64}() # Specifically for NonLinearProgram | ||
dy::Dict{MOI.ConstraintIndex,Float64} = Dict{MOI.ConstraintIndex,Float64}() | ||
# Dual sensitivity currently only works for NonLinearProgram | ||
dobj::Float64 = 0.0 # Objective input sensitivity for reverse differentiation | ||
# ds | ||
# dy #= [d\lambda, d\nu] for QP | ||
# FIXME Would it be possible to have a DoubleDict where the value depends | ||
|
@@ -35,6 +36,7 @@ | |
empty!(cache.dx) | ||
empty!(cache.dp) | ||
empty!(cache.dy) | ||
cache.dobj = 0.0 | ||
empty!(cache.scalar_constraints) | ||
empty!(cache.vector_constraints) | ||
cache.objective = nothing | ||
|
@@ -184,6 +186,20 @@ | |
""" | ||
struct ReverseConstraintDual <: MOI.AbstractConstraintAttribute end | ||
|
||
""" | ||
ReverseObjectiveSensitivity <: MOI.AbstractModelAttribute | ||
|
||
A `MOI.AbstractModelAttribute` to set input data for reverse differentiation. | ||
|
||
For instance, to set the sensitivity of the parameter perturbation with respect to the | ||
objective function perturbation, do the following: | ||
|
||
```julia | ||
MOI.set(model, DiffOpt.ReverseObjectiveSensitivity(), value) | ||
``` | ||
""" | ||
struct ReverseObjectiveSensitivity <: MOI.AbstractModelAttribute end | ||
|
||
""" | ||
ForwardConstraintDual <: MOI.AbstractConstraintAttribute | ||
|
||
|
@@ -199,6 +215,21 @@ | |
|
||
MOI.is_set_by_optimize(::ForwardConstraintDual) = true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. codecov thinks this line is untested, but it is used internally in the tests. |
||
|
||
""" | ||
ForwardObjectiveSensitivity <: MOI.AbstractModelAttribute | ||
|
||
A `MOI.AbstractModelAttribute` to get output objective sensitivity data from forward differentiation. | ||
|
||
For instance, to get the sensitivity of the objective function with respect to the parameter perturbation, do the following: | ||
|
||
```julia | ||
MOI.get(model, DiffOpt.ForwardObjectiveSensitivity()) | ||
``` | ||
""" | ||
struct ForwardObjectiveSensitivity <: MOI.AbstractModelAttribute end | ||
|
||
MOI.is_set_by_optimize(::ForwardObjectiveSensitivity) = true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dito codecov mistake |
||
|
||
""" | ||
ReverseObjectiveFunction <: MOI.AbstractModelAttribute | ||
|
||
|
@@ -403,6 +434,11 @@ | |
return | ||
end | ||
|
||
function MOI.set(model::AbstractModel, ::ReverseObjectiveSensitivity, val) | ||
model.input_cache.dobj = val | ||
return | ||
end | ||
|
||
function MOI.set( | ||
model::AbstractModel, | ||
::ForwardConstraintFunction, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo:
Lagrandian
->Lagrangian
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also mention explicitly that the objective sensitivity w.r.t. a parameter change in the RHS of the constraints is given by the optimal multiplier