Skip to content

🚀 Add NonLinearProgram Support to DiffOpt.jl #260

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 76 commits into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
0dc8099
NonLinearProgram
andrewrosemberg Dec 6, 2024
4b16e13
index by MOI index
andrewrosemberg Dec 9, 2024
db0c0ce
only cache gradient
andrewrosemberg Dec 11, 2024
d8b38f4
update API
andrewrosemberg Dec 13, 2024
eaa4c69
start reverse mode
andrewrosemberg Dec 13, 2024
e170339
add overloads
andrewrosemberg Dec 13, 2024
b006b57
update MOI wrapper
andrewrosemberg Dec 16, 2024
5ade750
update code for DiffOpt API
andrewrosemberg Dec 17, 2024
ff1052f
working code
andrewrosemberg Dec 17, 2024
24fb230
usage example
andrewrosemberg Dec 17, 2024
56a4d1e
add reverse diff
andrewrosemberg Dec 18, 2024
b9781ba
update code
andrewrosemberg Dec 20, 2024
1e94996
update tests
andrewrosemberg Dec 22, 2024
48e7b19
update tests
andrewrosemberg Dec 23, 2024
501fe83
add forward_differentiate! tests
andrewrosemberg Dec 23, 2024
28ec54f
add reverse_differentiate! tests
andrewrosemberg Dec 23, 2024
d3563dd
update docs
andrewrosemberg Dec 23, 2024
7867ece
format
andrewrosemberg Dec 23, 2024
4868748
update API reference
andrewrosemberg Dec 23, 2024
1d5dd4a
fix typos
andrewrosemberg Jan 6, 2025
89d34ea
update reference
andrewrosemberg Jan 6, 2025
d8a5691
update spdiagm
andrewrosemberg Feb 3, 2025
4074055
Typo "acutal" to "actual" (#258)
mzagorowska Jul 23, 2024
b1f0092
Fix GitHub actions badge in README (#263)
odow Jan 5, 2025
614b026
Implement MOI.Utilities.scalar_type for (Matrix|Sparse)VectorAffineFu…
odow Jan 7, 2025
39adba2
Use SlackBridgePrimalDualStart (#253)
blegat Jan 8, 2025
8526ac4
Integrate with POI to improve UX (#262)
joaquimg Jan 31, 2025
066aef6
Add error for missing starting value (#269)
blegat Feb 1, 2025
61123b9
update API
andrewrosemberg Feb 5, 2025
65d4224
expose kwargs
andrewrosemberg Feb 5, 2025
36b0170
restrict hessian type
andrewrosemberg Feb 5, 2025
859ddea
reverse wrong change
andrewrosemberg Feb 5, 2025
475a02f
update usage
andrewrosemberg Feb 5, 2025
453edf5
Merge branch 'master' into ar/NonLinearProgram
andrewrosemberg Feb 5, 2025
dffdf8d
fix mad merge
andrewrosemberg Feb 5, 2025
bf4ab5d
fix typo
andrewrosemberg Feb 5, 2025
b7ef541
fix typo
andrewrosemberg Feb 6, 2025
19dcda4
fix wrong index
andrewrosemberg Feb 6, 2025
df90d97
reverse index
andrewrosemberg Feb 6, 2025
622732e
allow user to just set relevat sensitivities
andrewrosemberg Feb 7, 2025
bce2303
fix copy reverse sensitivity dual
andrewrosemberg Feb 7, 2025
a3fe85a
format
andrewrosemberg Feb 7, 2025
69dc67a
update tests
andrewrosemberg Feb 15, 2025
543840a
format
andrewrosemberg Feb 15, 2025
9b9ef0f
update docs
andrewrosemberg Feb 16, 2025
c8bbc6c
extend parameter @test_throws tests for NLP
andrewrosemberg Feb 16, 2025
fad6d7c
update comments
andrewrosemberg Feb 17, 2025
c8a4522
update private api: _add_leq_geq
andrewrosemberg Feb 17, 2025
dec13c1
fix typo
andrewrosemberg Feb 17, 2025
06f9110
continue fix typo check asserts
andrewrosemberg Feb 17, 2025
e668506
expose factorization through as MOI.AbstractModelAttribute
andrewrosemberg Feb 17, 2025
c152dfe
Merge branch 'master' into ar/NonLinearProgram
joaquimg Feb 18, 2025
bcff465
add tests factorization
andrewrosemberg Feb 18, 2025
a0b2f3d
add comment
andrewrosemberg Feb 18, 2025
095a5d9
rm rm kwargs
andrewrosemberg Feb 18, 2025
db91862
use correct underscore signature for private funcs
andrewrosemberg Feb 18, 2025
65631b3
format
andrewrosemberg Feb 18, 2025
e2cb3be
change github actions to v3
andrewrosemberg Feb 18, 2025
d508789
reverse checkout version
andrewrosemberg Feb 18, 2025
0797d22
add reference sipopt paper
andrewrosemberg Feb 18, 2025
09fec6b
update factorization routine API
andrewrosemberg Feb 18, 2025
e82060e
format
andrewrosemberg Feb 18, 2025
1bad199
Update ci.yml
andrewrosemberg Feb 20, 2025
660b513
improve coverage
andrewrosemberg Feb 20, 2025
afa0182
add test inertia correction
andrewrosemberg Feb 20, 2025
6cec90b
add test ReverseConstraintDual
andrewrosemberg Feb 20, 2025
5105bc7
format
andrewrosemberg Feb 20, 2025
1da5f71
rm useless checks
andrewrosemberg Feb 20, 2025
200299d
add test get ReverseConstraintDual
andrewrosemberg Feb 20, 2025
9633691
format
andrewrosemberg Feb 20, 2025
9b4cef5
rm unecessary funcs
andrewrosemberg Feb 20, 2025
1847392
rm kwargs
andrewrosemberg Feb 20, 2025
9e78f2e
format
andrewrosemberg Feb 20, 2025
ef100b2
rename factorization attributte
joaquimg Feb 21, 2025
83deef4
add supports
joaquimg Feb 21, 2025
5b26b88
Apply suggestions from code review
joaquimg Feb 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: actions/cache@v1
- uses: actions/cache@v3
env:
cache-name: cache-artifacts
with:
Expand All @@ -37,7 +37,7 @@ jobs:
env:
DATADEPS_ALWAYS_ACCEPT: true # For MLDatasets.MNIST
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v3
- uses: codecov/codecov-action@v4
with:
file: lcov.info
docs:
Expand Down
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "DiffOpt"
uuid = "930fe3bc-9c6b-11ea-2d94-6184641e85e7"
authors = ["Akshay Sharma", "Mathieu Besançon", "Joaquim Dias Garcia", "Benoît Legat", "Oscar Dowson"]
version = "0.4.3"
authors = ["Akshay Sharma", "Mathieu Besançon", "Joaquim Dias Garcia", "Benoît Legat", "Oscar Dowson", "Andrew Rosemberg"]
version = "0.5.0"

[deps]
BlockDiagonals = "0a1fb500-61f7-11e9-3c65-f5ef3456f9f0"
Expand Down
8 changes: 4 additions & 4 deletions docs/src/index.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# DiffOpt.jl

[DiffOpt.jl](https://github.com/jump-dev/DiffOpt.jl) is a package for differentiating convex optimization program ([JuMP.jl](https://github.com/jump-dev/JuMP.jl) or [MathOptInterface.jl](https://github.com/jump-dev/MathOptInterface.jl) models) with respect to program parameters. Note that this package does not contain any solver.
[DiffOpt.jl](https://github.com/jump-dev/DiffOpt.jl) is a package for differentiating convex and non-convex optimization program ([JuMP.jl](https://github.com/jump-dev/JuMP.jl) or [MathOptInterface.jl](https://github.com/jump-dev/MathOptInterface.jl) models) with respect to program parameters. Note that this package does not contain any solver.
This package has two major backends, available via the `reverse_differentiate!` and `forward_differentiate!` methods, to differentiate models (quadratic or conic) with optimal solutions.

!!! note
Currently supports *linear programs* (LP), *convex quadratic programs* (QP) and *convex conic programs* (SDP, SOCP, exponential cone constraints only).
Currently supports *linear programs* (LP), *convex quadratic programs* (QP), *convex conic programs* (SDP, SOCP, exponential cone constraints only), and *general nonlinear programs* (NLP).


## Installation
Expand All @@ -16,8 +16,8 @@ DiffOpt can be installed through the Julia package manager:

## Why are Differentiable optimization problems important?

Differentiable optimization is a promising field of convex optimization and has many potential applications in game theory, control theory and machine learning (specifically deep learning - refer [this video](https://www.youtube.com/watch?v=NrcaNnEXkT8) for more).
Recent work has shown how to differentiate specific subclasses of convex optimization problems. But several applications remain unexplored (refer section 8 of this [really good thesis](https://github.com/bamos/thesis)). With the help of automatic differentiation, differentiable optimization can have a significant impact on creating end-to-end differentiable systems to model neural networks, stochastic processes, or a game.
Differentiable optimization is a promising field of constrained optimization and has many potential applications in game theory, control theory and machine learning (specifically deep learning - refer [this video](https://www.youtube.com/watch?v=NrcaNnEXkT8) for more).
Recent work has shown how to differentiate specific subclasses of constrained optimization problems. But several applications remain unexplored (refer section 8 of this [really good thesis](https://github.com/bamos/thesis)). With the help of automatic differentiation, differentiable optimization can have a significant impact on creating end-to-end differentiable systems to model neural networks, stochastic processes, or a game.


## Contributing
Expand Down
35 changes: 29 additions & 6 deletions docs/src/manual.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
# Manual

!!! note
As of now, this package only works for optimization models that can be written either in convex conic form or convex quadratic form.


## Supported objectives & constraints - `QuadraticProgram` backend
## Supported objectives & constraints - scheme 1

For `QuadraticProgram` backend, the package supports following `Function-in-Set` constraints:

Expand Down Expand Up @@ -52,6 +48,33 @@ and the following objective types:

Other conic sets such as `RotatedSecondOrderCone` and `PositiveSemidefiniteConeSquare` are supported through bridges.

## Supported objectives & constraints - `NonlinearProgram` backend

For the `NonlinearProgram` backend, the package supports following `Function-in-Set` constraints:

| MOI Function | MOI Set |
|:-------|:---------------|
| `VariableIndex` | `GreaterThan` |
| `VariableIndex` | `LessThan` |
| `VariableIndex` | `EqualTo` |
| `ScalarAffineFunction` | `GreaterThan` |
| `ScalarAffineFunction` | `LessThan` |
| `ScalarAffineFunction` | `EqualTo` |
| `ScalarQuadraticFunction` | `GreaterThan` |
| `ScalarQuadraticFunction` | `LessThan` |
| `ScalarQuadraticFunction` | `EqualTo` |
| `ScalarNonlinearFunction` | `GreaterThan` |
| `ScalarNonlinearFunction` | `LessThan` |
| `ScalarNonlinearFunction` | `EqualTo` |

and the following objective types:

| MOI Function |
|:-------:|
| `VariableIndex` |
| `ScalarAffineFunction` |
| `ScalarQuadraticFunction` |
| `ScalarNonlinearFunction` |

## Creating a differentiable MOI optimizer

Expand All @@ -68,7 +91,7 @@ DiffOpt requires taking projections and finding projection gradients of vectors
## Conic problem formulation

!!! note
As of now, the package is using `SCS` geometric form for affine expressions in cones.
As of now, when defining a conic or convex quadratic problem, the package is using `SCS` geometric form for affine expressions in cones.

Consider a convex conic optimization problem in its primal (P) and dual (D) forms:
```math
Expand Down
2 changes: 1 addition & 1 deletion docs/src/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
```

```@autodocs
Modules = [DiffOpt, DiffOpt.QuadraticProgram, DiffOpt.ConicProgram]
Modules = [DiffOpt, DiffOpt.QuadraticProgram, DiffOpt.ConicProgram, DiffOpt.NonLinearProgram]
```
62 changes: 62 additions & 0 deletions docs/src/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,65 @@ MOI.set(model, DiffOpt.ForwardObjectiveFunction(), ones(2) ⋅ x)
DiffOpt.forward_differentiate!(model)
grad_x = MOI.get.(model, DiffOpt.ForwardVariablePrimal(), x)
```

3. To differentiate a general nonlinear program, have to use the API for Parameterized JuMP models. For example, consider the following nonlinear program:

```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)
@show value(x) == 3 * p_val / pc_val

# the function is
# x(p, pc) = 3p / pc
# hence,
# dx/dp = 3 / pc
# dx/dpc = -3p / pc^2

# First, try forward mode AD

# differentiate w.r.t. p
direction_p = 3.0
MOI.set(model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), Parameter(direction_p))
DiffOpt.forward_differentiate!(model)
@show MOI.get(model, DiffOpt.ForwardVariablePrimal(), x) == direction_p * 3 / pc_val

# update p and pc
p_val = 2.0
pc_val = 6.0
set_parameter_value(p, p_val)
set_parameter_value(pc, pc_val)
# re-optimize
optimize!(model)
# check solution
@show value(x) ≈ 3 * p_val / pc_val

# stop differentiating with respect to p
DiffOpt.empty_input_sensitivities!(model)
# differentiate w.r.t. pc
direction_pc = 10.0
MOI.set(model, DiffOpt.ForwardConstraintSet(), ParameterRef(pc), Parameter(direction_pc))
DiffOpt.forward_differentiate!(model)
@show abs(MOI.get(model, DiffOpt.ForwardVariablePrimal(), x) -
-direction_pc * 3 * p_val / pc_val^2) < 1e-5

# always a good practice to clear previously set sensitivities
DiffOpt.empty_input_sensitivities!(model)
# Now, reverse model AD
direction_x = 10.0
MOI.set(model, DiffOpt.ReverseVariablePrimal(), x, direction_x)
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
```
8 changes: 8 additions & 0 deletions src/DiffOpt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ include("bridges.jl")

include("QuadraticProgram/QuadraticProgram.jl")
include("ConicProgram/ConicProgram.jl")
include("NonLinearProgram/NonLinearProgram.jl")

"""
add_all_model_constructors(model)
Expand All @@ -37,6 +38,13 @@ Add all constructors of [`AbstractModel`](@ref) defined in this package to
function add_all_model_constructors(model)
add_model_constructor(model, QuadraticProgram.Model)
add_model_constructor(model, ConicProgram.Model)
add_model_constructor(model, NonLinearProgram.Model)
return
end

function add_default_factorization(model)
model.input_cache.factorization =
NonLinearProgram._lu_with_inertia_correction
return
end

Expand Down
Loading
Loading