Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 5 additions & 5 deletions docs/problems.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function generate_documentation(
using OptimalControlProblems # to access the Beam model
using OptimalControl # to import the OptimalControl model
using NLPModelsIpopt # to solve the model with Ipopt
using DataFrames # to store data
import DataFrames: DataFrame # to store data
using NLPModels # to retrieve data from the NLP solution
using Plots # to plot the trajectories
using Plots.PlotMeasures # for leftmargin, bottommargin
Expand Down Expand Up @@ -100,7 +100,7 @@ function generate_documentation(
push!(data_pb,
(
Problem=:$PROBLEM,
Grid_Size=OptimalControlProblems.metadata[:$PROBLEM][:N],
Grid_Size=metadata[:$PROBLEM][:N],
Variables=get_nvar(nlp_oc),
Constraints=get_ncon(nlp_oc),
)
Expand All @@ -125,8 +125,8 @@ function generate_documentation(
Visualise states, costates, and controls for the OptimalControl solution:

```@example main
x_vars = OptimalControlProblems.metadata[:$PROBLEM][:state_name]
u_vars = OptimalControlProblems.metadata[:$PROBLEM][:control_name]
x_vars = metadata[:$PROBLEM][:state_name]
u_vars = metadata[:$PROBLEM][:control_name]

n = length(x_vars) # number of states
m = length(u_vars) # number of controls
Expand Down Expand Up @@ -319,7 +319,7 @@ function generate_documentation(
```

```@example main
v_vars = OptimalControlProblems.metadata[:$PROBLEM][:variable_name]
v_vars = metadata[:$PROBLEM][:variable_name]

function L2_norm(T, X)
# T and X are supposed to be one dimensional
Expand Down
20 changes: 11 additions & 9 deletions docs/src/dev-add.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ new_problem_meta = OrderedDict(

For more details about the metadata, see the [MetaData](@ref problems-introduction-metadata) section.

**2.** Define the **OptimalControl** model of the problem in a separate file in the `ext/OptimalControlModels` directory.
**2.** Define the DOCP **OptimalControl** model of the problem in a file named `new_problem.jl` in the `ext/OptimalControlModels` directory.

```julia
"""
Description of the new problem
Documentation of the method
"""
function OptimalControlProblems.new_problem(::OptimalControlBackend; N::Int=steps_number_data(:new_problem))
function OptimalControlProblems.new_problem(::OptimalControlBackend, description::Symbol...; N::Int=steps_number_data(:new_problem), kwargs...)

# if tf is fixed
tf = final_time_data(:new_problem)
Expand All @@ -42,30 +42,32 @@ function OptimalControlProblems.new_problem(::OptimalControlBackend; N::Int=step
init = ()

# DOCP and NLP
docp = direct_transcription(ocp; init=init, grid_size=N, disc_method=:trapeze)
docp = direct_transcription(ocp, description...; init=init, grid_size=N, disc_method=:trapeze, kwargs...)

return docp

end
```

**3.** Define the **JuMP** model of the problem in a new file in the `ext/JuMPModels` directory.
**3.** Define the NLP **JuMP** model of the problem in a file named `new_problem.jl` in the `ext/JuMPModels` directory.

```julia
"""
Description of the new problem
Documentation of the method
"""
function OptimalControlProblems.new_problem(::JuMPBackend; N::Int=steps_number_data(:new_problem))
function OptimalControlProblems.new_problem(::JuMPBackend, args...; N::Int=steps_number_data(:new_problem), kwargs...)

# if tf is fixed
tf = final_time_data(:new_problem)

# model
nlp = JuMP.Model()
model = JuMP.Model(args...; kwargs...)

# Define the problem here
# ...

return nlp
return model
end
```

**3.** Describe the problem in a file named `new_problem.jl` in the `ext/Descriptions` directory.
9 changes: 9 additions & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ using Pkg
Pkg.add("OptimalControlProblems")
```

## Credits (not exhaustive!)

- [Nico77310](https://github.com/Nico77310)
- [0Yassine0](https://github.com/0Yassine0)
- [frapac](https://github.com/frapac)
- [BaptisteCbl](https://github.com/BaptisteCbl)
- [COPS: Large-Scale Optimization Problems](https://www.mcs.anl.gov/~more/cops) and [COPSBenchmark.jl](github.com/MadNLP/COPSBenchmark.jl)
- [BOCOP - A collection of examples](https://project.inria.fr/bocop/files/2017/05/Examples-BOCOP.pdf)

## Reproducibility

```@raw html
Expand Down
11 changes: 5 additions & 6 deletions docs/src/problems-introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,14 @@ Depth = 1
For each problem, additional data is provided in the [MetaData](https://github.com/control-toolbox/OptimalControlProblems.jl/tree/main/ext/MetaData) directory:

```@docs; canonical=false
OptimalControlProblems.metadata
metadata
```

To list all metadata, use `OptimalControlProblems.metadata`.
To access the metadata of a specific problem, for example `chain`, run:
To list all metadata, use `metadata`. To access the metadata of a specific problem, for example `chain`, run:

```@example main
using OptimalControlProblems
OptimalControlProblems.metadata[:chain]
metadata[:chain]
```

## Problems characteristics
Expand All @@ -113,7 +112,7 @@ We detail below the characteristics of the optimal control problems (OCPs) and t

```@example main
using NLPModels # to get the number of variables and constraints
using DataFrames
import DataFrames: DataFrame # to store data
using OptimalControl

data_ocp = DataFrame( # to store data of the OCPs
Expand Down Expand Up @@ -183,7 +182,7 @@ for problem in problems()
)

#
N = OptimalControlProblems.metadata[problem][:N] # get default number of steps
N = metadata[problem][:N] # get default number of steps

push!(data_nlp,
(
Expand Down
26 changes: 17 additions & 9 deletions docs/src/tutorial-get.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,19 @@ The `nlp` model represents the nonlinear programming problem (NLP) obtained afte

!!! note

You also have access to the DOCP model, which corresponds to the discretised optimal control problem. Roughly speaking, the DOCP model is the union of the NLP and OCP models. For more details, see this [tutorial](@extref Tutorials Discretization-and-NLP-problem) or the documentation of [`CTDirect.DOCP`](@extref).
You also have access to the DOCP model, which corresponds to the discretised optimal control problem. Roughly speaking, the DOCP model is the union of the NLP and OCP models. For more details, see this [tutorial](@extref Tutorials Discretization-and-NLP-problem) or the documentation of [`CTDirect.DOCP`](@extref). To get the OCP model:

### OCP
```julia
ocp = ocp_model(docp)
```

You also have access to the OCP model, which corresponds to the optimal control problem.
!!! note

```@example main_oc
ocp = ocp_model(docp)
nothing # hide
```
You can pass any `description` and `kwargs` of [`CTDirect.direct_transcription`](@extref) to the `beam` problem or any other.

```julia
docp = beam(OptimalControlBackend(), :madnlp; grid_size=100, disc_method=:euler)
```

### Number of variables, constraints, and nonzeros

Expand Down Expand Up @@ -72,7 +75,7 @@ println("nnzh = ", nnzh)
The number of steps $N$ is stored in the metadata:

```@example main_oc
OptimalControlProblems.metadata[:beam][:N]
metadata[:beam][:N]
```

!!! note
Expand Down Expand Up @@ -108,4 +111,9 @@ nlp = beam(JuMPBackend())
```

!!! note
For details on how to interact with the JuMP model, see the [JuMP documentation](https://jump.dev/JuMP.jl).
For details on how to interact with the JuMP model, see the [JuMP documentation](https://jump.dev/JuMP.jl). In particular, you can pass any arguments and keyword arguments of [`JuMP.Model`](@extref) to the `beam` problem or any other.

```julia
using Ipopt
nlp = beam(JuMPBackend(), Ipopt.Optimizer; add_bridges=true)
```
4 changes: 2 additions & 2 deletions docs/src/tutorial-solve.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@ println("p(tf) = ", p(tf))
We can add the state, costate, and control to the plot:

```@example main
n = length(OptimalControlProblems.metadata[problem][:state_name]) # dimension of the state
m = length(OptimalControlProblems.metadata[problem][:control_name]) # dimension of the control
n = length(metadata[problem][:state_name]) # dimension of the state
m = length(metadata[problem][:control_name]) # dimension of the control

for i in 1:n # state
plot!(plt[i], t, t -> x(t)[i]; color=2, linestyle=:dash, label=:none)
Expand Down
24 changes: 12 additions & 12 deletions ext/JuMPModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Compute the discretised time grid for a given optimal control problem solved wit

# Arguments

- `problem::Symbol`: The name of the problem as defined in `OptimalControlProblems.metadata`.
- `problem::Symbol`: The name of the problem as defined in `metadata`.
- `model::JuMP.GenericModel`: The JuMP model containing the problem solution.

# Returns
Expand All @@ -41,18 +41,18 @@ julia> tgrid = OptimalControlProblems.time_grid(:my_problem, model)
function OptimalControlProblems.time_grid(problem::Symbol, model::JuMP.GenericModel)

# get N
x_vars = OptimalControlProblems.metadata[problem][:state_name]
x_vars = metadata[problem][:state_name]
x_jp_var = JuMP.value.(model[Symbol(x_vars[1])])
N = length(x_jp_var) - 1

## time grid: we assume that t0 = 0
time_data, time_value_or_index = OptimalControlProblems.metadata[problem][:final_time]
time_data, time_value_or_index = metadata[problem][:final_time]

t0 = 0
tf = if time_data == :fixed
time_value_or_index
elseif time_data == :free
v_vars = OptimalControlProblems.metadata[problem][:variable_name]
v_vars = metadata[problem][:variable_name]
value.(model[Symbol(v_vars[time_value_or_index])])
else
error("the final time must be :fixed or :free, not: ", time_data)
Expand All @@ -69,7 +69,7 @@ Extract and interpolate the state trajectory from a JuMP model of an optimal con

# Arguments

- `problem::Symbol`: The name of the problem as defined in `OptimalControlProblems.metadata`.
- `problem::Symbol`: The name of the problem as defined in `metadata`.
- `model::JuMP.GenericModel`: The JuMP model containing the problem solution.

# Returns
Expand All @@ -92,7 +92,7 @@ function OptimalControlProblems.state(problem::Symbol, model::JuMP.GenericModel)
N = length(T) - 1

# get dimension
state_names = OptimalControlProblems.metadata[problem][:state_name]
state_names = metadata[problem][:state_name]
dim_x = length(state_names)

# get state from the model
Expand Down Expand Up @@ -120,7 +120,7 @@ Extract and interpolate the control trajectory from a JuMP model of an optimal c

# Arguments

- `problem::Symbol`: The name of the problem as defined in `OptimalControlProblems.metadata`.
- `problem::Symbol`: The name of the problem as defined in `metadata`.
- `model::JuMP.GenericModel`: The JuMP model containing the problem solution.

# Returns
Expand All @@ -143,7 +143,7 @@ function OptimalControlProblems.control(problem::Symbol, model::JuMP.GenericMode
N = length(T) - 1

# get dimension
control_names = OptimalControlProblems.metadata[problem][:control_name]
control_names = metadata[problem][:control_name]
dim_u = length(control_names)

# get control from the model
Expand Down Expand Up @@ -171,7 +171,7 @@ Extract and interpolate the costate trajectory (dual variables associated with s

# Arguments

- `problem::Symbol`: The name of the problem as defined in `OptimalControlProblems.metadata`.
- `problem::Symbol`: The name of the problem as defined in `metadata`.
- `model::JuMP.GenericModel`: The JuMP model containing the problem solution.

# Returns
Expand All @@ -194,7 +194,7 @@ function OptimalControlProblems.costate(problem::Symbol, model::JuMP.GenericMode
N = length(T) - 1

# get dimension
costate_names = OptimalControlProblems.metadata[problem][:costate_name]
costate_names = metadata[problem][:costate_name]
dim_x = length(costate_names)

# get costate from the model
Expand Down Expand Up @@ -226,7 +226,7 @@ Extract scalar or vector decision variables (such as final time when free) from

# Arguments

- `problem::Symbol`: The name of the problem as defined in `OptimalControlProblems.metadata`.
- `problem::Symbol`: The name of the problem as defined in `metadata`.
- `model::JuMP.GenericModel`: The JuMP model containing the problem solution.

# Returns
Expand All @@ -244,7 +244,7 @@ julia> v = OptimalControlProblems.variable(:my_problem, model)
```
"""
function OptimalControlProblems.variable(problem::Symbol, model::JuMP.GenericModel)
variable_names = OptimalControlProblems.metadata[problem][:variable_name]
variable_names = metadata[problem][:variable_name]

if isnothing(variable_names)
return nothing
Expand Down
4 changes: 2 additions & 2 deletions ext/JuMPModels/beam.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ julia> model = OptimalControlProblems.beam(JuMPBackend(); N=100)

- Problem formulation available at: https://github.com/control-toolbox/bocop/tree/main/bocop
"""
function OptimalControlProblems.beam(::JuMPBackend; N::Int=steps_number_data(:beam))
function OptimalControlProblems.beam(::JuMPBackend, args...; N::Int=steps_number_data(:beam), kwargs...)

# parameters
tf = final_time_data(:beam)
step = tf / N # t0 = 0

# model
model = JuMP.Model()
model = JuMP.Model(args...; kwargs...)

# variables and initial guess
@variables(
Expand Down
4 changes: 2 additions & 2 deletions ext/JuMPModels/bioreactor.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ julia> model = OptimalControlProblems.bioreactor(JuMPBackend(); N=100)
- [control-toolbox/bocop](https://github.com/control-toolbox/bocop/tree/main/bocop)
"""
function OptimalControlProblems.bioreactor(
::JuMPBackend; N::Int=steps_number_data(:bioreactor)
::JuMPBackend, args...; N::Int=steps_number_data(:bioreactor), kwargs...
)

# parameters
Expand All @@ -46,7 +46,7 @@ function OptimalControlProblems.bioreactor(
T = final_time_data(:bioreactor)

# model
model = JuMP.Model()
model = JuMP.Model(args...; kwargs...)

# variables and initial guess
@variables(
Expand Down
4 changes: 2 additions & 2 deletions ext/JuMPModels/cart_pendulum.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ julia> model = OptimalControlProblems.cart_pendulum(JuMPBackend(); N=200)
- [Cart–Pendulum Optimal Control Problem](https://arxiv.org/pdf/2303.16746)
"""
function OptimalControlProblems.cart_pendulum(
::JuMPBackend; N::Int=steps_number_data(:cart_pendulum)
::JuMPBackend, args...; N::Int=steps_number_data(:cart_pendulum), kwargs...
)

# parameters
Expand All @@ -42,7 +42,7 @@ function OptimalControlProblems.cart_pendulum(
max_v = 2

# model
model = JuMP.Model()
model = JuMP.Model(args...; kwargs...)

# variables and initial guess
@variables(
Expand Down
4 changes: 2 additions & 2 deletions ext/JuMPModels/chain.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ julia> model = OptimalControlProblems.chain(JuMPBackend(); N=300)

- [COPS Benchmark Problems – Hanging Chain](https://www.mcs.anl.gov/~more/cops/)
"""
function OptimalControlProblems.chain(::JuMPBackend; N::Int=steps_number_data(:chain))
function OptimalControlProblems.chain(::JuMPBackend, args...; N::Int=steps_number_data(:chain), kwargs...)

# parameters
L = 4
Expand All @@ -39,7 +39,7 @@ function OptimalControlProblems.chain(::JuMPBackend; N::Int=steps_number_data(:c
tmin = b > a ? 1 / 4 : 3 / 4

# model
model = JuMP.Model()
model = JuMP.Model(args...; kwargs...)

# time
@expressions(
Expand Down
4 changes: 2 additions & 2 deletions ext/JuMPModels/dielectrophoretic_particle.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ julia> model = OptimalControlProblems.dielectrophoretic_particle(JuMPBackend();
IEEE Transactions on Automatic Control, 51(7), 1100–1114.
"""
function OptimalControlProblems.dielectrophoretic_particle(
::JuMPBackend; N::Int=steps_number_data(:dielectrophoretic_particle)
::JuMPBackend, args...; N::Int=steps_number_data(:dielectrophoretic_particle), kwargs...
)

# parameters
Expand All @@ -39,7 +39,7 @@ function OptimalControlProblems.dielectrophoretic_particle(
c = 1

# model
model = JuMP.Model()
model = JuMP.Model(args...; kwargs...)

# state, control and variable (final time)
@variable(model, x[0:N], start = 1)
Expand Down
Loading
Loading