Skip to content

added ChainRules examples with forward and backward #91

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

Merged
merged 4 commits into from
Jun 1, 2021

Conversation

matbesancon
Copy link
Collaborator

Copying the unit commitment example with ChainRules.

We depend on ChainRulesCore through SetDistances, so I added it as a dependency

@codecov
Copy link

codecov bot commented May 1, 2021

Codecov Report

Merging #91 (9ce17c4) into master (99bd415) will increase coverage by 0.32%.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #91      +/-   ##
==========================================
+ Coverage   81.50%   81.82%   +0.32%     
==========================================
  Files           5        5              
  Lines         865      864       -1     
==========================================
+ Hits          705      707       +2     
+ Misses        160      157       -3     
Impacted Files Coverage Δ
src/MOI_wrapper.jl 79.61% <0.00%> (+0.12%) ⬆️
src/utils.jl 82.14% <0.00%> (+1.19%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 99bd415...9ce17c4. Read the comment docs.

@akshay326
Copy link
Collaborator

this is great. i've been stuck at finding the difference b/t frules and rrules in implementation. now its much clear

@matbesancon
Copy link
Collaborator Author

@oxinabox if you want to take a look

@oxinabox
Copy link
Contributor

oxinabox commented May 6, 2021

Looks good.
It would be good to have more narrative around this to have in the docs.
Like https://jump.dev/DiffOpt.jl/dev/sensitivity-analysis-svm/ has with plots.
Possibly someone on our power systems team can help suggesting things that are interesting to show.

Copy link
Contributor

@raphaelsaavedra raphaelsaavedra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the work, looks really nice. Left some minor comments and a question.

model = Model(() -> diff_optimizer(Clp.Optimizer))
pv = unit_commitment(load1_demand, load2_demand, gen_costs, noload_costs, model=model)
energy_balance_cons = model[:energy_balance_cons]
MOI.set.(model, DiffOpt.ForwardIn{DiffOpt.ConstraintConstant}(), energy_balance_cons, [d1 + d2 for (d1, d2) in zip(Δload1_demand, Δload1_demand)])
Copy link
Contributor

@raphaelsaavedra raphaelsaavedra May 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be a dumb question because I'm not familiar with DiffOpt and AD in general, but what are we doing here exactly and why is it specifically for the energy balance constraint?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it be too much to put a comment before each of these about what it does?
Not something that one would do in normal code, but since this is a tutorial it might be suitable.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are setting the perturbation for the right-hand-side of the energy balance constraints, which is the sum of perturbations of each of the quantities in the RHS (in that case sum of load demands)

matbesancon and others added 2 commits May 9, 2021 16:24
Co-authored-by: Raphael Saavedra <raphael.saavedra93@gmail.com>
- `noload_costs` is the vector of fixed activation costs of the generators,
and returning the optimal output power `p`.
"""
function unit_commitment(load1_demand, load2_demand, gen_costs, noload_costs; model = Model(() -> diff_optimizer(Clp.Optimizer)))
Copy link
Contributor

@oxinabox oxinabox May 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can just do

Suggested change
function unit_commitment(load1_demand, load2_demand, gen_costs, noload_costs; model = Model(() -> diff_optimizer(Clp.Optimizer)))
function unit_commitment(load1_demand, load2_demand, gen_costs, noload_costs; model = Model(Clp.Optimizer))

Comment on lines +80 to +82
function ChainRulesCore.frule((_, Δload1_demand, Δload2_demand, Δgen_costs, Δnoload_costs), ::typeof(unit_commitment), load1_demand, load2_demand, gen_costs, noload_costs)
# creating the UC model with a DiffOpt optimizer wrapper around Clp
model = Model(() -> diff_optimizer(Clp.Optimizer))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to also pass the kwargs in: so probably can do:

Suggested change
function ChainRulesCore.frule((_, Δload1_demand, Δload2_demand, Δgen_costs, Δnoload_costs), ::typeof(unit_commitment), load1_demand, load2_demand, gen_costs, noload_costs)
# creating the UC model with a DiffOpt optimizer wrapper around Clp
model = Model(() -> diff_optimizer(Clp.Optimizer))
function ChainRulesCore.frule((_, Δload1_demand, Δload2_demand, Δgen_costs, Δnoload_costs), ::typeof(unit_commitment), load1_demand, load2_demand, gen_costs, noload_costs; model=Model(Clp.Optimizer))
# creating the UC model with a DiffOpt optimizer wrapper around Clp
model = Model(() -> diff_optimizer(optimizer(model)))

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one issue here is that backend(.) only gives you the top-most backend layer, so you add two layers of CachingOptimizer

# Reverse-mode differentiation of the solution map
# The computed pullback takes a seed for the optimal solution `̄p` and returns
# derivatives wrt each input parameter.
function ChainRulesCore.rrule(::typeof(unit_commitment), load1_demand, load2_demand, gen_costs, noload_costs; model = Model(() -> diff_optimizer(Clp.Optimizer)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should write this assuming the Model they use isn't a diff_optimizer model.
And we should unwrap and rewrap the model

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unwrapping and rewrapping the model results in the same issue as above

@matbesancon matbesancon merged commit be331b5 into master Jun 1, 2021
@matbesancon matbesancon deleted the chainrules branch June 1, 2021 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants