Skip to content

Commit 0c08663

Browse files
authored
Add Extension for MathOptAI (#390)
* add support for MathOptAI * Add JuMP prefixes * fix coverage * fix doc versioning * Rework docs and naming * fix doctest * doc fix
1 parent fa69b01 commit 0c08663

File tree

13 files changed

+198
-74
lines changed

13 files changed

+198
-74
lines changed

Project.toml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "InfiniteOpt"
22
uuid = "20393b10-9daf-11e9-18c9-8db751c92c57"
3-
authors = ["Joshua Pulsipher and Weiqi Zhang"]
4-
version = "0.5.9"
3+
authors = ["Joshua Pulsipher and colleagues"]
4+
version = "0.6.0"
55

66
[deps]
77
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
@@ -14,9 +14,11 @@ Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
1414

1515
[weakdeps]
1616
Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
17+
MathOptAI = "e52c2cb8-508e-4e12-9dd2-9c4755b60e73"
1718

1819
[extensions]
19-
InfiniteInterpolate = "Interpolations"
20+
InfiniteInterpolations = "Interpolations"
21+
InfiniteMathOptAI = "MathOptAI"
2022

2123
[compat]
2224
DataStructures = "0.14.2 - 0.18, 0.19"
@@ -27,13 +29,16 @@ MutableArithmetics = "1"
2729
Reexport = "0.2, 1"
2830
julia = "^1.6"
2931
Interpolations = "0.16"
32+
MathOptAI = "0.1.15"
3033

3134
[extras]
3235
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
3336
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
3437
Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb"
3538
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
3639
Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
40+
MathOptAI = "e52c2cb8-508e-4e12-9dd2-9c4755b60e73"
41+
Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9"
3742

3843
[targets]
39-
test = ["Pkg", "Test", "Random", "Suppressor", "Interpolations"]
44+
test = ["Pkg", "Test", "Random", "Suppressor", "Interpolations", "MathOptAI", "Ipopt"]

docs/Project.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,20 @@ SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
1212
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
1313
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
1414
Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
15+
MathOptAI = "e52c2cb8-508e-4e12-9dd2-9c4755b60e73"
16+
Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c"
1517

1618
[compat]
1719
Distributions = "0.25"
1820
Documenter = "1.5"
19-
InfiniteOpt = "0.5"
21+
InfiniteOpt = "0.6"
2022
Ipopt = "1.6"
2123
HiGHS = "1"
2224
julia = "1.10"
2325
JuMP = "1.23"
2426
Literate = "2.18"
2527
Plots = "1"
2628
SpecialFunctions = "2"
27-
Interpolations = "0.16"
29+
Interpolations = "0.16"
30+
MathOptAI = "0.1.15"
31+
Flux = "0.16"

docs/make.jl

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Documenter, InfiniteOpt, Distributions, Literate, Random, Interpolations
1+
using Documenter, InfiniteOpt, Distributions, Literate, Random, Interpolations, MathOptAI
22

33
if !@isdefined(EXAMPLE_DIR)
44
const EXAMPLE_DIR = joinpath(@__DIR__, "src", "examples")
@@ -71,7 +71,8 @@ makedocs(;
7171
"Constraints" => "guide/constraint.md",
7272
"Model Transcription" => "guide/transcribe.md",
7373
"Optimization" => "guide/optimize.md",
74-
"Results" => "guide/result.md"
74+
"Results" => "guide/result.md",
75+
"Extensions" => "guide/extensions.md"
7576
],
7677
"API Manual" => [
7778
"Infinite Models" => "manual/model.md",
@@ -86,7 +87,8 @@ makedocs(;
8687
"Constraints" => "manual/constraint.md",
8788
"Backends" => "manual/backend.md",
8889
"TranscriptionOpt" => "manual/transcribe.md",
89-
"Results" => "manual/result.md"
90+
"Results" => "manual/result.md",
91+
"Extensions" => "manual/extensions.md"
9092
],
9193
"Development" => [
9294
"Extensions" => "develop/extensions.md",
@@ -99,8 +101,11 @@ makedocs(;
99101
modules = [
100102
InfiniteOpt,
101103
isdefined(Base, :get_extension) ?
102-
Base.get_extension(InfiniteOpt, :InfiniteInterpolate) :
103-
InfiniteOpt.InfiniteInterpolate
104+
Base.get_extension(InfiniteOpt, :InfiniteInterpolations) :
105+
InfiniteOpt.InfiniteInterpolations,
106+
isdefined(Base, :get_extension) ?
107+
Base.get_extension(InfiniteOpt, :InfiniteMathOptAI) :
108+
InfiniteOpt.InfiniteMathOptAI,
104109
],
105110
checkdocs = :none,
106111
linkcheck = true,

docs/src/examples/Optimal Control/hovercraft.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ end)
6161
# Optimize the model:
6262
optimize!(m)
6363

64-
# Extract the results. The [`InfiniteInterpolate`](@ref infiniteInterpolate)
64+
# Extract the results. The [`InfiniteInterpolations`](@ref interpolate)
6565
# extension can be used to get a smooth interpolated function for x, which is
6666
# invoked when both the `Interpolations` and `InfiniteOpt` packages are imported.
6767
# Here, cubic splines are used as the interpolation method for both x1 and x2:

docs/src/guide/extensions.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# [Extension Packages](@id ext_docs)
2+
Here, we document extension packages that extend the base capabilities of InfiniteOpt.
3+
4+
## [InfiniteInterpolations](@id interpolate)
5+
This extension uses [Interpolations.jl](https://github.com/JuliaMath/Interpolations.jl)
6+
to automatically convert discretized solution values into continuous functions. This
7+
extension is enabled by importing both InfiniteOpt and Interpolations:
8+
```jldoctest interpolate
9+
julia> using InfiniteOpt, Interpolations
10+
```
11+
Now let's solve a model:
12+
```jldoctest interpolate
13+
julia> using HiGHS; model = InfiniteModel(HiGHS.Optimizer);
14+
15+
julia> @infinite_parameter(model, t ∈ [0, 1], num_supports = 5);
16+
17+
julia> @variable(model, y >= 0, Infinite(t));
18+
19+
julia> @objective(model, Min, ∫(y, t));
20+
21+
julia> @constraint(model, y >= 2t);
22+
23+
julia> set_silent(model); optimize!(model)
24+
25+
julia> discrete_y = value(y)
26+
5-element Vector{Float64}:
27+
0.0
28+
0.5
29+
1.0
30+
1.5
31+
2.0
32+
```
33+
where we get vector of five values for `y` since 5 supports are used to discretize ``y(t)``. To obtain a continuous representation, we can use interpolation by specifying one of three supported kinds:
34+
- `constant_interpolation` or `Constant()`
35+
- `linear_interpolation` or `Linear()`
36+
- `cubic_spline_interpolation` or `Cubic()`
37+
38+
For our example, let's choose a linear interpolation:
39+
```jldoctest interpolate
40+
julia> continuous_y = value(y, linear_interpolation);
41+
42+
julia> continuous_y(0.1)
43+
0.2
44+
45+
julia> continuous_y(0.25)
46+
0.5
47+
```
48+
Note we could have equivalently used `value(y, Linear())`.
49+
50+
!!! warning
51+
There is a type piracy conflict between JuMP and OffsetArrays
52+
(a dependency of Interpolations.jl). As a result, type piracy issues may arise
53+
when Interpolations is loaded in (typically when `JuMP.DenseAxisArray`s are used).
54+
Hence, we recommend using `Array` containers when using this extension.
55+
56+
## InfiniteMathOptAI
57+
This extension allows us to import machine learning models into `InfiniteModels`
58+
via [MathOptAI](https://lanl-ansi.github.io/MathOptAI.jl/stable/). This is enabled by
59+
importing InfiniteOpt and MathOptAI:
60+
```jldoctest mathoptai
61+
julia> using InfiniteOpt, MathOptAI
62+
```
63+
Now we can incorporate any predictor (i.e., machine learning model) supported by MathOptAI
64+
which includes neural networks, Gaussian processes, decision trees, and generalized linear
65+
models. For instance, consider the neural ODE with the right-hand side:
66+
```jldoctest mathoptai
67+
julia> using Flux
68+
69+
julia> NN = Flux.Chain(Flux.Dense(2 => 3, Flux.relu), Flux.Dense(3 => 1));
70+
```
71+
Let's create an `InfiniteModel` those poses a neural operator constraint with `NN`. This
72+
is accomplished by using [`MathOptAI.add_predictor`](https://lanl-ansi.github.io/MathOptAI.jl/stable/api/#add_predictor):
73+
```jldoctest mathoptai
74+
julia> model = InfiniteModel();
75+
76+
julia> @infinite_parameter(model, t ∈ [0, 1]);
77+
78+
julia> @variable(model, y >= 0, Infinite(t));
79+
80+
julia> f, formulation = add_predictor(model, NN, [y, t]);
81+
82+
julia> @constraint(model, ∂(y, t) == only(f))
83+
d/dt[y(t)] - moai_Affine[1](t) = 0, ∀ t ∈ [0, 1]
84+
```
85+
Here, `f` is the vector of output variables (only 1 in this case) and `formulation`
86+
is an object that stores all the variables and constraints created to embed `NN` in
87+
`model`. To learn more about MathOptAI's syntax, please visit
88+
[MathOptAI's documentation](https://lanl-ansi.github.io/MathOptAI.jl/stable/).

docs/src/guide/result.md

Lines changed: 2 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -185,55 +185,8 @@ These again all have a 1-to-1 correspondence.
185185
whose dimensions correspond to the supports of the infinite parameters.
186186

187187
## Interpolation-Based Continuous Values
188-
We can also get a continuous representation of a variable as an interpolations
189-
object from the `Interpolations.jl` package. This is based on the
190-
[`InfiniteInterpolate`](@ref infiniteInterpolate) extension, which is
191-
automatically loaded in when both `InfiniteOpt` and `Interpolations` are imported.
192-
The current supported methods are `linear_interpolation`, `constant_interpolation`
193-
and `cubic_spline_interpolation`.
194-
```jldoctest results
195-
julia> using Interpolations
196-
197-
julia> yFunc = value(y, linear_interpolation)
198-
10-element extrapolate(interpolate((::Vector{Float64},), ::Vector{Float64}, Gridded(Linear())), Throw()) with element type Float64:
199-
42.0
200-
20.999999995627107
201-
20.999999995628606
202-
20.999999995628603
203-
20.999999995628592
204-
20.999999995628603
205-
20.999999995634035
206-
20.999999995620904
207-
20.99999999562204
208-
20.9999999956286
209-
210-
julia> yFunc(5.12)
211-
20.9999999956286
212-
```
213-
Alternatively, we can specify the degree of interpolation with `Linear()`,
214-
`Constant()` or `Cubic()`. This will call the corresponding interpolation method.
215-
```jldoctest results
216-
julia> yFunc = value(y, Linear()) # equivalent to value(y, linear_interpolation)
217-
10-element extrapolate(interpolate((::Vector{Float64},), ::Vector{Float64}, Gridded(Linear())), Throw()) with element type Float64:
218-
42.0
219-
20.999999995627107
220-
20.999999995628606
221-
20.999999995628603
222-
20.999999995628592
223-
20.999999995628603
224-
20.999999995634035
225-
20.999999995620904
226-
20.99999999562204
227-
20.9999999956286
228-
229-
julia> yFunc(5.12)
230-
20.9999999956286
231-
```
232-
!!! warning
233-
There is a type piracy conflict between JuMP and OffsetArrays
234-
(a dependency of Interpolations.jl). As a result, type piracy issues may arise
235-
when Interpolations is loaded in. Please keep this in mind when using the
236-
InfiniteInterpolate extension.
188+
We can use interpolations to obtain continuous functions instead of discretized
189+
value arrays. See the [InfiniteInterpolations guide](@ref interpolate) to learn more.
237190

238191
## Termination Queries
239192
Termination queries are those that question about how the infinite model was

docs/src/manual/extensions.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# [Extensions](@id ext_manual)
2+
A technical manual for extension packages natively hosted by `InfiniteOpt`. See the
3+
respective [guide](@ref ext_docs) for more information.
4+
5+
## InfiniteInterpolations
6+
Enabled via `import InfiniteOpt, Interpolations`.
7+
```@docs
8+
JuMP.value(::GeneralVariableRef, ::Interpolations.InterpolationType)
9+
```
10+
11+
## InfiniteMathOptAI
12+
Enabled via `import InfiniteOpt, MathOptAI`.
13+
```@docs
14+
MathOptAI.add_variables
15+
```

docs/src/manual/result.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# [Results](@id result_manual)
22
A technical manual for querying optimized `InfiniteOpt` models. See the
3-
respective [guide](@ref result_manual) for more information.
3+
respective [guide](@ref result_docs) for more information.
44

55
## Statuses
66
```@docs
@@ -36,11 +36,6 @@ JuMP.reduced_cost(::GeneralVariableRef)
3636
JuMP.optimizer_index(::GeneralVariableRef)
3737
```
3838

39-
## [InfiniteInterpolate](@id infiniteInterpolate)
40-
```@docs
41-
JuMP.value(::GeneralVariableRef, ::Interpolations.InterpolationType)
42-
```
43-
4439
## Constraints
4540
```@docs
4641
JuMP.has_duals(::InfiniteModel)

ext/InfiniteInterpolate.jl renamed to ext/InfiniteInterpolations.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module InfiniteInterpolate
1+
module InfiniteInterpolations
22

33
import JuMP
44
import InfiniteOpt

ext/InfiniteMathOptAI.jl

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
module InfiniteMathOptAI
2+
3+
import InfiniteOpt, JuMP, MathOptAI
4+
5+
"""
6+
function MathOptAI.add_variables(
7+
model::InfiniteModel, x::Vector{GeneralVariableRef},
8+
n::Int,
9+
base_name::String,
10+
)::Vector{GeneralVariableRef}
11+
12+
Extend `MathOptAI.add_variables` to properly support infinite variables (i.e., ensure
13+
the output variables of a predictor have the necessary infinite parameter dependencies).
14+
This method should not be directly used by users, it is used to enable the use of
15+
[`MathOptAI.add_predictor`](https://lanl-ansi.github.io/MathOptAI.jl/stable/api/#add_predictor).
16+
"""
17+
function MathOptAI.add_variables(
18+
model::InfiniteOpt.InfiniteModel,
19+
x::Vector{InfiniteOpt.GeneralVariableRef},
20+
n::Int,
21+
base_name::String,
22+
)
23+
inds = InfiniteOpt.parameter_group_int_indices(x)
24+
if isempty(inds)
25+
return JuMP.@variable(model, [1:n], base_name = base_name)
26+
end
27+
params = InfiniteOpt.parameter_refs(model)[inds]
28+
return JuMP.@variable(
29+
model,
30+
[1:n],
31+
base_name = base_name,
32+
variable_type = InfiniteOpt.Infinite(params...),
33+
)
34+
end
35+
36+
# TODO extend MathOptAI.[get/set]_variable_bounds if/when function bounds are allowed in InfiniteOpt
37+
38+
end # end of module

0 commit comments

Comments
 (0)