From 53174589b3427489beefd09b4e0da0b7c032d60c Mon Sep 17 00:00:00 2001 From: Olivier Cots Date: Sun, 15 Jun 2025 23:06:24 +0200 Subject: [PATCH 1/7] start ocp man --- docs/Project.toml | 1 + docs/make.jl | 4 ++- docs/src/api-ctbase.md | 2 +- docs/src/api-ctdirect.md | 2 +- docs/src/api-ctflows.md | 2 +- docs/src/api-ctmodels.md | 2 +- docs/src/api-ctparser.md | 2 +- docs/src/api-optimalcontrol-user.md | 14 ++------ docs/src/assets/Manifest.toml | 38 +++++++++++++++++++- docs/src/assets/Project.toml | 4 +++ docs/src/manual-solve.md | 2 +- src/OptimalControl.jl | 56 ++++++++++++++++------------- 12 files changed, 85 insertions(+), 44 deletions(-) diff --git a/docs/Project.toml b/docs/Project.toml index 34c79aa18..cb8b7f63f 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -6,6 +6,7 @@ CTFlows = "1c39547c-7794-42f7-af83-d98194f657c2" CTModels = "34c4fa32-2049-4079-8329-de33c2a22e2d" CTParser = "32681960-a1b1-40db-9bff-a1ca817385d1" CommonSolve = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2" +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" DocumenterInterLinks = "d12716ef-a0f6-4df4-a9f1-a5a34e75c656" DocumenterMermaid = "a078cd44-4d9c-4618-b545-3ab9d77f9177" diff --git a/docs/make.jl b/docs/make.jl index 9b1bffec3..041e3b3c4 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -101,7 +101,7 @@ cp("./docs/Project.toml", "./docs/src/assets/Project.toml"; force=true) repo_url = "github.com/control-toolbox/OptimalControl.jl" makedocs(; - draft=false, # if draft is true, then the julia code from .md is not executed + draft=true, # if draft is true, then the julia code from .md is not executed # to disable the draft mode in a specific markdown file, use the following: # ```@meta # Draft = false @@ -125,8 +125,10 @@ makedocs(; ], "Manual" => [ "Define a problem" => "manual-abstract.md", + "Problem characteristics" => "manual-model.md", "Set an initial guess" => "manual-initial-guess.md", "Solve a problem" => "manual-solve.md", + "Solution characteristics" => "manual-solution.md", "Plot a solution" => "manual-plot.md", "Compute flows" => [ "Getting started" => "manual-flow-api.md", diff --git a/docs/src/api-ctbase.md b/docs/src/api-ctbase.md index d7bfe0418..399cfb3c6 100644 --- a/docs/src/api-ctbase.md +++ b/docs/src/api-ctbase.md @@ -29,4 +29,4 @@ M --> B style B fill:#FBF275 ``` -OptimalControl heavily relies on CTBase. Refer to the [CTBase API documentation](@extref CTBase index) for more details. \ No newline at end of file +OptimalControl heavily relies on CTBase. We refer to [CTBase API](@extref CTBase index) for more details. \ No newline at end of file diff --git a/docs/src/api-ctdirect.md b/docs/src/api-ctdirect.md index 2e07a13f4..35faa7710 100644 --- a/docs/src/api-ctdirect.md +++ b/docs/src/api-ctdirect.md @@ -29,4 +29,4 @@ M --> B style D fill:#FBF275 ``` -OptimalControl heavily relies on CTDirect. Refer to the [CTDirect API documentation](@extref CTDirect index) for more details. \ No newline at end of file +OptimalControl heavily relies on CTDirect. We refer to [CTDirect API](@extref CTDirect index) for more details. \ No newline at end of file diff --git a/docs/src/api-ctflows.md b/docs/src/api-ctflows.md index 4bb34184d..6ab4f4b39 100644 --- a/docs/src/api-ctflows.md +++ b/docs/src/api-ctflows.md @@ -29,4 +29,4 @@ M --> B style F fill:#FBF275 ``` -OptimalControl heavily relies on CTFlows. Refer to the [CTFlows API documentation](@extref CTFlows index) for more details. \ No newline at end of file +OptimalControl heavily relies on CTFlows. We refer to [CTFlows API](@extref CTFlows index) for more details. \ No newline at end of file diff --git a/docs/src/api-ctmodels.md b/docs/src/api-ctmodels.md index 1b8dde5bc..e82990794 100644 --- a/docs/src/api-ctmodels.md +++ b/docs/src/api-ctmodels.md @@ -29,4 +29,4 @@ M --> B style M fill:#FBF275 ``` -OptimalControl heavily relies on CTModels. Refer to the [CTModels API documentation](@extref CTModels index) for more details. \ No newline at end of file +OptimalControl heavily relies on CTModels. We refer to [CTModels API](@extref CTModels index) for more details. \ No newline at end of file diff --git a/docs/src/api-ctparser.md b/docs/src/api-ctparser.md index 04ec4916a..d98d243c0 100644 --- a/docs/src/api-ctparser.md +++ b/docs/src/api-ctparser.md @@ -29,4 +29,4 @@ M --> B style P fill:#FBF275 ``` -OptimalControl heavily relies on CTParser. Refer to the [CTParser API documentation](@extref CTParser index) for more details. \ No newline at end of file +OptimalControl heavily relies on CTParser. We refer to [CTParser API](@extref CTParser index) for more details. \ No newline at end of file diff --git a/docs/src/api-optimalcontrol-user.md b/docs/src/api-optimalcontrol-user.md index 10f894f6d..0178eb7f2 100644 --- a/docs/src/api-optimalcontrol-user.md +++ b/docs/src/api-optimalcontrol-user.md @@ -70,15 +70,12 @@ Poisson Solution VectorField available_methods -boundary_constraints_dual build_OCP_solution constraint +constraints constraints_violation control control_components -control_constraints_box -control_constraints_lb_dual -control_constraints_ub_dual control_dimension control_name costate @@ -91,6 +88,7 @@ dynamics export_ocp_solution final_time final_time_name +get_build_examodel has_fixed_final_time has_fixed_initial_time has_free_final_time @@ -107,26 +105,20 @@ lagrange mayer message objective -path_constraints_dual plot(::Solution, ::Symbol...) plot!(::Plots.Plot, ::Solution, ::Symbol...) set_initial_guess solve(::Model, ::Symbol...) state state_components -state_constraints_box -state_constraints_lb_dual -state_constraints_ub_dual state_dimension state_name stopping time_grid time_name +times variable variable_components -variable_constraints_box -variable_constraints_lb_dual -variable_constraints_ub_dual variable_dimension variable_name ⋅ diff --git a/docs/src/assets/Manifest.toml b/docs/src/assets/Manifest.toml index 277dcd17a..587477a79 100644 --- a/docs/src/assets/Manifest.toml +++ b/docs/src/assets/Manifest.toml @@ -2,7 +2,7 @@ julia_version = "1.11.5" manifest_format = "2.0" -project_hash = "619f0fb19f859094e1399a5b2427138fe211e67a" +project_hash = "aedb19159ec43d1fc78401e06abaeeb58fd73c61" [[deps.ADNLPModels]] deps = ["ADTypes", "ForwardDiff", "LinearAlgebra", "NLPModels", "Requires", "ReverseDiff", "SparseArrays", "SparseConnectivityTracer", "SparseMatrixColorings"] @@ -371,6 +371,12 @@ git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe" uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" version = "1.16.0" +[[deps.DataFrames]] +deps = ["Compat", "DataAPI", "DataStructures", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrecompileTools", "PrettyTables", "Printf", "Random", "Reexport", "SentinelArrays", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"] +git-tree-sha1 = "fb61b4812c49343d7ef0b533ba982c46021938a6" +uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +version = "1.7.0" + [[deps.DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] git-tree-sha1 = "4e1fe97fdaed23e9dc21d4d664bea76b65fc50a0" @@ -857,6 +863,19 @@ git-tree-sha1 = "debdd00ffef04665ccbb3e150747a77560e8fad1" uuid = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" version = "0.1.1" +[[deps.InlineStrings]] +git-tree-sha1 = "8594fac023c5ce1ef78260f24d1ad18b4327b420" +uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" +version = "1.4.4" + + [deps.InlineStrings.extensions] + ArrowTypesExt = "ArrowTypes" + ParsersExt = "Parsers" + + [deps.InlineStrings.weakdeps] + ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd" + Parsers = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" + [[deps.IntelOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] git-tree-sha1 = "0f14a5456bdc6b9731a5682f439a672750a09e48" @@ -889,6 +908,11 @@ weakdeps = ["Dates", "Test"] InverseFunctionsDatesExt = "Dates" InverseFunctionsTestExt = "Test" +[[deps.InvertedIndices]] +git-tree-sha1 = "6da3c4316095de0f5ee2ebd875df8721e7e0bdbe" +uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f" +version = "1.3.1" + [[deps.Ipopt]] deps = ["Ipopt_jll", "LinearAlgebra", "OpenBLAS32_jll", "PrecompileTools"] git-tree-sha1 = "4ad0d2dea51e5d49866b40a2d2521da6a1be7097" @@ -1829,6 +1853,12 @@ git-tree-sha1 = "645bed98cd47f72f67316fd42fc47dee771aefcd" uuid = "1d0040c9-8b98-4ee7-8388-3f51789ca0ad" version = "0.2.2" +[[deps.PooledArrays]] +deps = ["DataAPI", "Future"] +git-tree-sha1 = "36d8b4b899628fb92c2749eb488d884a926614d3" +uuid = "2dfb63ee-cc39-5dd5-95bd-886bf059d720" +version = "1.4.3" + [[deps.PreallocationTools]] deps = ["Adapt", "ArrayInterface", "ForwardDiff"] git-tree-sha1 = "6d98eace73d82e47f5b16c393de198836d9f790a" @@ -2064,6 +2094,12 @@ git-tree-sha1 = "3bac05bc7e74a75fd9cba4295cde4045d9fe2386" uuid = "6c6a2e73-6563-6170-7368-637461726353" version = "1.2.1" +[[deps.SentinelArrays]] +deps = ["Dates", "Random"] +git-tree-sha1 = "712fb0231ee6f9120e005ccd56297abbc053e7e0" +uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c" +version = "1.4.8" + [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" version = "1.11.0" diff --git a/docs/src/assets/Project.toml b/docs/src/assets/Project.toml index e671a18a1..20693bfa9 100644 --- a/docs/src/assets/Project.toml +++ b/docs/src/assets/Project.toml @@ -6,6 +6,7 @@ CTFlows = "1c39547c-7794-42f7-af83-d98194f657c2" CTModels = "34c4fa32-2049-4079-8329-de33c2a22e2d" CTParser = "32681960-a1b1-40db-9bff-a1ca817385d1" CommonSolve = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2" +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" DocumenterInterLinks = "d12716ef-a0f6-4df4-a9f1-a5a34e75c656" DocumenterMermaid = "a078cd44-4d9c-4618-b545-3ab9d77f9177" @@ -21,6 +22,7 @@ Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" [compat] +ADNLPModels = "0.8" CTBase = "0.16" CTDirect = "0.14" CTFlows = "0.8" @@ -28,12 +30,14 @@ CTModels = "0.5" CTParser = "0.4" CommonSolve = "0.2" Documenter = "1.8" +DocumenterInterLinks = "1" DocumenterMermaid = "0.2" JLD2 = "0.5" JSON3 = "1.14" LinearAlgebra = "1" MadNLP = "0.8" NLPModelsIpopt = "0.10" +NLPModelsKnitro = "0.9" OrdinaryDiffEq = "6.93" Plots = "1.40" Suppressor = "0.2" diff --git a/docs/src/manual-solve.md b/docs/src/manual-solve.md index 8b49f4f7b..e67a3d797 100644 --- a/docs/src/manual-solve.md +++ b/docs/src/manual-solve.md @@ -122,7 +122,7 @@ The main options for the direct method, with their [default] values, are: For advanced usage, see: - [discrete continuation tutorial](https://control-toolbox.org/Tutorials.jl/stable/tutorial-continuation.html), -- [NLP direct handling tutorial](https://control-toolbox.org/Tutorials.jl/stable/tutorial-nlp.html). +- [NLP manipulation tutorial](https://control-toolbox.org/Tutorials.jl/stable/tutorial-nlp.html). !!! note diff --git a/src/OptimalControl.jl b/src/OptimalControl.jl index 47a1e658e..9e4bf93e1 100644 --- a/src/OptimalControl.jl +++ b/src/OptimalControl.jl @@ -32,6 +32,9 @@ import CTModels: PreModel, Solution, # getters + constraints, + get_build_examodel, + times, definition, dual, initial_time, @@ -39,15 +42,15 @@ import CTModels: final_time, final_time_name, time_name, - variable_constraints_box, + #variable_constraints_box, variable_dimension, variable_components, variable_name, - state_constraints_box, + #state_constraints_box, state_dimension, state_components, state_name, - control_constraints_box, + #control_constraints_box, control_dimension, control_components, control_name, @@ -68,40 +71,43 @@ import CTModels: constraint, time_grid, control, - control_constraints_lb_dual, - control_constraints_ub_dual, + #control_constraints_lb_dual, + #control_constraints_ub_dual, state, - state_constraints_lb_dual, - state_constraints_ub_dual, + #state_constraints_lb_dual, + #state_constraints_ub_dual, variable, - variable_constraints_lb_dual, - variable_constraints_ub_dual, + #variable_constraints_lb_dual, + #variable_constraints_ub_dual, costate, constraints_violation, objective, iterations, stopping, message, - infos, - boundary_constraints_dual, - path_constraints_dual + infos + #boundary_constraints_dual, + #path_constraints_dual export Model, Solution -export definition, +export constraints, + get_build_examodel, + times, + definition, dual, initial_time, initial_time_name, final_time, final_time_name, time_name, - variable_constraints_box, + #variable_constraints_box, variable_dimension, variable_components, variable_name, - state_constraints_box, + #state_constraints_box, state_dimension, state_components, state_name, - control_constraints_box, + #control_constraints_box, control_dimension, control_components, control_name, @@ -122,23 +128,23 @@ export definition, constraint, time_grid, control, - control_constraints_lb_dual, - control_constraints_ub_dual, + #control_constraints_lb_dual, + #control_constraints_ub_dual, state, - state_constraints_lb_dual, - state_constraints_ub_dual, + #state_constraints_lb_dual, + #state_constraints_ub_dual, variable, - variable_constraints_lb_dual, - variable_constraints_ub_dual, + #variable_constraints_lb_dual, + #variable_constraints_ub_dual, costate, constraints_violation, objective, iterations, stopping, message, - infos, - boundary_constraints_dual, - path_constraints_dual + infos + #boundary_constraints_dual, + #path_constraints_dual # CTParser import CTParser: CTParser, @def From aea1de94e99534e2041cc05bc1863c798a66ab02 Mon Sep 17 00:00:00 2001 From: Olivier Cots Date: Sun, 15 Jun 2025 23:07:34 +0200 Subject: [PATCH 2/7] remove exported fun useless --- src/OptimalControl.jl | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/OptimalControl.jl b/src/OptimalControl.jl index 9e4bf93e1..4e01f3435 100644 --- a/src/OptimalControl.jl +++ b/src/OptimalControl.jl @@ -42,15 +42,12 @@ import CTModels: final_time, final_time_name, time_name, - #variable_constraints_box, variable_dimension, variable_components, variable_name, - #state_constraints_box, state_dimension, state_components, state_name, - #control_constraints_box, control_dimension, control_components, control_name, @@ -71,14 +68,8 @@ import CTModels: constraint, time_grid, control, - #control_constraints_lb_dual, - #control_constraints_ub_dual, state, - #state_constraints_lb_dual, - #state_constraints_ub_dual, variable, - #variable_constraints_lb_dual, - #variable_constraints_ub_dual, costate, constraints_violation, objective, @@ -86,8 +77,6 @@ import CTModels: stopping, message, infos - #boundary_constraints_dual, - #path_constraints_dual export Model, Solution export constraints, get_build_examodel, @@ -99,15 +88,12 @@ export constraints, final_time, final_time_name, time_name, - #variable_constraints_box, variable_dimension, variable_components, variable_name, - #state_constraints_box, state_dimension, state_components, state_name, - #control_constraints_box, control_dimension, control_components, control_name, @@ -128,14 +114,8 @@ export constraints, constraint, time_grid, control, - #control_constraints_lb_dual, - #control_constraints_ub_dual, state, - #state_constraints_lb_dual, - #state_constraints_ub_dual, variable, - #variable_constraints_lb_dual, - #variable_constraints_ub_dual, costate, constraints_violation, objective, @@ -143,8 +123,6 @@ export constraints, stopping, message, infos - #boundary_constraints_dual, - #path_constraints_dual # CTParser import CTParser: CTParser, @def From 0ac979a29981924e3f07b7bc49c07473dd344b4f Mon Sep 17 00:00:00 2001 From: Olivier Cots Date: Sun, 15 Jun 2025 23:29:39 +0200 Subject: [PATCH 3/7] add constraints part --- docs/src/manual-model.md | 257 ++++++++++++++++++++++++++++++++++++ docs/src/manual-solution.md | 7 + 2 files changed, 264 insertions(+) create mode 100644 docs/src/manual-model.md create mode 100644 docs/src/manual-solution.md diff --git a/docs/src/manual-model.md b/docs/src/manual-model.md new file mode 100644 index 000000000..03353a7e3 --- /dev/null +++ b/docs/src/manual-model.md @@ -0,0 +1,257 @@ +# [Understanding optimal control problems: functionalities and properties](@id manual-model) + +```@meta +CollapsedDocStrings = false +Draft = false +``` + +In this manual, we'll first recall the **main functionalities** you can use when working with an optimal control problem (OCP). This includes essential operations like: + +* **Solving an OCP**: How to find the optimal solution for your defined problem. +* **Computing flows from an OCP**: Understanding the dynamics and trajectories derived from the optimal solution. +* **Printing an OCP**: How to display a summary of your problem's definition. + +After covering these core functionalities, we'll delve into the **structure of an OCP**. Since an OCP is structured as a [`Model`](@ref) struct, we'll first explain how to **access its underlying attributes**, such as the problem's dynamics, costs, and constraints. Following this, we'll shift our focus to the **simple properties** inherent to an OCP, learning how to determine aspects like whether the problem: + +* **Is autonomous**: Does its dynamics depend explicitly on time? +* **Has a fixed final time**: Is the duration of the control problem predetermined? + +This structured approach will give you a comprehensive understanding of both what you can *do* with an OCP and what an OCP *is*. + +--- + +**Content** + +- [Main functionalities](@ref manual-model-main-functionalities) +- [Model struct](@ref manual-model-struct) +- [Attributes](@ref manual-model-attributes) +- [Properties](@ref manual-model-properties) + +--- + +## [Main functionalities](@id manual-model-main-functionalities) + +Let us define a basic optimal control problem. + +```@example main +using OptimalControl + +t0 = 0 +tf = 1 +x0 = [-1, 0] + +ocp = @def begin + t ∈ [ t0, tf ], time + x = (q, v) ∈ R², state + u ∈ R, control + x(t0) == x0 + x(tf) == [0, 0] + ẋ(t) == [v(t), u(t)] + ∫( 0.5u(t)^2 ) → min +end +nothing # hide +``` + +To print it, simply: + +```@example main +ocp +``` + +We can now solve the problem (for more details, visit the [solve manual](@ref manual-solve)): + +```@example main +using NLPModelsIpopt +#solve(ocp) +nothing # hide +``` + +You can also compute flows (for more details, see the [flow manual](@ref manual-flow-ocp)) from the optimal control problem, providing a control law in feedback form. The **pseudo-Hamiltonian** of this problem is + +```math + H(x, p, u) = p_q\, q + p_v\, v + p^0 u^2 /2, +``` + +where $p^0 = -1$ since we are in the normal case. From the Pontryagin maximum principle, the maximising control is given in feedback form by + +```math +u(x, p) = p_v +``` + +since $\partial^2_{uu} H = p^0 = - 1 < 0$. + +```@example main +u = (x, p) -> p[2] # control law in feedback form + +using OrdinaryDiffEq # needed to import numerical integrators +f = Flow(ocp, u) # compute the Hamiltonian flow function + +p0 = [12, 6] # initial covector solution +xf, pf = f(t0, x0, p0, tf) # flow from (x0, p0) at time t0 to tf +xf # should be (0, 0) +``` + +!!! note + + A more advanced feature allows for the discretization of the optimal control problem. From the discretized version, you can obtain a Nonlinear Programming problem (or optimization problem) and solve it using any appropriate NLP solver. For more details, visit the [NLP manipulation tutorial](https://control-toolbox.org/Tutorials.jl/stable/tutorial-nlp.html). + +## [Model struct](@id manual-model-struct) + +The optimal control problem `ocp` is a [`Model`](@ref) struct. + +```@docs; canonical=false +Model +``` + +Each field can be access directly (`ocp.times`, etc) or by a getter: + +- [`times`](@ref) +- [`state`](@ref) +- [`control`](@ref) +- [`variable`](@ref) +- [`dynamics`](@ref) +- [`objective`](@ref) +- [`constraints`](@ref) +- [`definition`](@ref) +- [`get_build_examodel`](@ref) + +For instance, we can retrieve the `times` and `definition` values. + +```@example main +times(ocp) +``` + +```@example main +definition(ocp) +``` + +The dynamics stored in `ocp` is an inplace function of the form `f!(dx, t, x, u, v)` where `t` is the time, `x` the state, `u` the control and `v` the variable, and where `dx` is the output value. + +```@example main +f! = dynamics(ocp) +t = 0 +x = [0., 1] +u = 2 +v = [] +dx = similar(x) +f!(dx, t, x, u, v) +dx +``` + +We refer to [CTModels API](@extref CTModels Types) for more details about this struct and its fields. + +## [Attributes](@id manual-model-attributes) + +More attributes can be retrived. To illustrate this, we define a more complex optimal control problem. + +```@example main +ocp = @def begin + tf ∈ R, variable + t ∈ [0, tf], time + q = (x, y) ∈ R², state + u ∈ R, control + 0 ≤ tf ≤ 2, (1) + u(t) ≥ 0, (cons_u) + x(t) + u(t) ≤ 10, (cons_mixed) + x(0) == -1 + y(0) - tf == 0, (cons_bound) + q(tf) == [0, 0] + q̇(t) == [y(t), u(t)] + 0.5∫( u(t)^2 ) → min +end +nothing # hide +``` + +### Control, state and variable + +You can get access to the name of the control, the names of the components, its dimension and the box constraints `lb ≤ u(t) ≤ ub`, where `lb` stands for lower bound and `ub` for upper bound. + +```@example main +using DataFrames +data = DataFrame( + Data=Vector{Symbol}(), + Name=Vector{String}(), + Components=Vector{Vector{String}}(), + Dimension=Vector{Int}(), +) + +# control +push!(data,( + :control, + control_name(ocp), + control_components(ocp), + control_dimension(ocp), +)) + +# state +push!(data,( + :state, + state_name(ocp), + state_components(ocp), + state_dimension(ocp), +)) + +# variable +push!(data,( + :variable, + variable_name(ocp), + variable_components(ocp), + variable_dimension(ocp), +)) +``` + +### Constraints + +The labelled constraints may be retrieved with the [`constraint`](@ref) function. The method `constraint(ocp, label)` returns a tuple of the form `(type, f, lb, ub)`. +The signature of the function `f` depends on the type. For boundary constraints (that is at initial and/or final time) and variable constraints, the signature is `f(x0, xf, v)` where `x0` stands for the initial state, `xf` the final state and `v` the variable. For other constraints, the signature is `f(t, x, u, v)`. + +```@example main +(type, f, lb, ub) = constraint(ocp, :eq1) +println("type: ", type) +x0 = [0, 1] +xf = [2, 3] +v = 4 +println("val: ", f(x0, xf, v)) +println("lb: ", lb) +println("ub: ", ub) +``` + +```@example main +(type, f, lb, ub) = constraint(ocp, :cons_bound) +println("type: ", type) +println("val: ", f(x0, xf, v)) +println("lb: ", lb) +println("ub: ", ub) +``` + +```@example main +(type, f, lb, ub) = constraint(ocp, :cons_u) +println("type: ", type) +t = 0 +x = [1, 2] +u = [3, 4] +println("val: ", f(t, x, u, v)) +println("lb: ", lb) +println("ub: ", ub) +``` + +```@example main +(type, f, lb, ub) = constraint(ocp, :cons_mixed) +println("type: ", type) +println("val: ", f(t, x, u, v)) +println("lb: ", lb) +println("ub: ", ub) +``` + +### Dynamics + + +### Objective + + +### Times + + + +## [Properties](@id manual-model-properties) + diff --git a/docs/src/manual-solution.md b/docs/src/manual-solution.md new file mode 100644 index 000000000..2b9602939 --- /dev/null +++ b/docs/src/manual-solution.md @@ -0,0 +1,7 @@ +# [Properties of an optimal control solution](@id manual-solution) + +```@meta +CollapsedDocStrings = false +Draft = false +``` + From 258f756b9fa4cacc3265ee39fbf8fd0d41f5ce71 Mon Sep 17 00:00:00 2001 From: Olivier Cots Date: Mon, 16 Jun 2025 23:25:54 +0200 Subject: [PATCH 4/7] first complete version of manual model --- docs/make.jl | 2 +- docs/src/api-optimalcontrol-user.md | 25 ---- docs/src/manual-model.md | 197 +++++++++++++++++++++++----- docs/src/manual-solution.md | 2 +- 4 files changed, 166 insertions(+), 60 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 041e3b3c4..409ef26c4 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -131,7 +131,7 @@ makedocs(; "Solution characteristics" => "manual-solution.md", "Plot a solution" => "manual-plot.md", "Compute flows" => [ - "Getting started" => "manual-flow-api.md", + "Flow API" => "manual-flow-api.md", "From optimal control problems" => "manual-flow-ocp.md", "From Hamiltonian and others" => "manual-flow-others.md", ], diff --git a/docs/src/api-optimalcontrol-user.md b/docs/src/api-optimalcontrol-user.md index 0178eb7f2..a19300e2f 100644 --- a/docs/src/api-optimalcontrol-user.md +++ b/docs/src/api-optimalcontrol-user.md @@ -28,31 +28,6 @@ Order = [:module] Private = false ``` -## Index - -```@index -Pages = ["api-optimalcontrol-user.md"] -Modules = [ - OptimalControl, - CommonSolve, - RecipesBase, - CTBase, - CTDirect, - CTFlows, - CTModels, - CTParser, - CTFlowsODE, - CTModelsPlots, - CTModelsJSON, - CTModelsJLD, - CTSolveExtIpopt, - CTSolveExtKnitro, - CTSolveExtMadNLP, -] - -Order = [:module, :constant, :type, :function, :macro] -``` - ## Documentation ```@docs; canonical=true diff --git a/docs/src/manual-model.md b/docs/src/manual-model.md index 03353a7e3..f5f1f7fde 100644 --- a/docs/src/manual-model.md +++ b/docs/src/manual-model.md @@ -1,8 +1,7 @@ -# [Understanding optimal control problems: functionalities and properties](@id manual-model) +# [The optimal control problem object: structure and usage](@id manual-model) ```@meta CollapsedDocStrings = false -Draft = false ``` In this manual, we'll first recall the **main functionalities** you can use when working with an optimal control problem (OCP). This includes essential operations like: @@ -14,7 +13,7 @@ In this manual, we'll first recall the **main functionalities** you can use when After covering these core functionalities, we'll delve into the **structure of an OCP**. Since an OCP is structured as a [`Model`](@ref) struct, we'll first explain how to **access its underlying attributes**, such as the problem's dynamics, costs, and constraints. Following this, we'll shift our focus to the **simple properties** inherent to an OCP, learning how to determine aspects like whether the problem: * **Is autonomous**: Does its dynamics depend explicitly on time? -* **Has a fixed final time**: Is the duration of the control problem predetermined? +* **Has a fixed or free initial/final time**: Is the duration of the control problem predetermined or not? This structured approach will give you a comprehensive understanding of both what you can *do* with an OCP and what an OCP *is*. @@ -24,8 +23,7 @@ This structured approach will give you a comprehensive understanding of both wha - [Main functionalities](@ref manual-model-main-functionalities) - [Model struct](@ref manual-model-struct) -- [Attributes](@ref manual-model-attributes) -- [Properties](@ref manual-model-properties) +- [Attributes and properties](@ref manual-model-attributes) --- @@ -47,7 +45,7 @@ ocp = @def begin x(t0) == x0 x(tf) == [0, 0] ẋ(t) == [v(t), u(t)] - ∫( 0.5u(t)^2 ) → min + 0.5∫( u(t)^2 ) → min end nothing # hide ``` @@ -81,7 +79,7 @@ u(x, p) = p_v since $\partial^2_{uu} H = p^0 = - 1 < 0$. ```@example main -u = (x, p) -> p[2] # control law in feedback form +u = (x, p) -> p[2] # control law in feedback form using OrdinaryDiffEq # needed to import numerical integrators f = Flow(ocp, u) # compute the Hamiltonian flow function @@ -125,46 +123,36 @@ times(ocp) definition(ocp) ``` -The dynamics stored in `ocp` is an inplace function of the form `f!(dx, t, x, u, v)` where `t` is the time, `x` the state, `u` the control and `v` the variable, and where `dx` is the output value. - -```@example main -f! = dynamics(ocp) -t = 0 -x = [0., 1] -u = 2 -v = [] -dx = similar(x) -f!(dx, t, x, u, v) -dx -``` +!!! note -We refer to [CTModels API](@extref CTModels Types) for more details about this struct and its fields. + We refer to [CTModels API](@extref CTModels Types) for more details about this struct and its fields. -## [Attributes](@id manual-model-attributes) +## [Attributes and properties](@id manual-model-attributes) More attributes can be retrived. To illustrate this, we define a more complex optimal control problem. ```@example main ocp = @def begin - tf ∈ R, variable - t ∈ [0, tf], time + v = (w, tf) ∈ R², variable + s ∈ [0, tf], time q = (x, y) ∈ R², state u ∈ R, control 0 ≤ tf ≤ 2, (1) - u(t) ≥ 0, (cons_u) - x(t) + u(t) ≤ 10, (cons_mixed) + u(s) ≥ 0, (cons_u) + x(s) + u(s) ≤ 10, (cons_mixed) + w == 0 x(0) == -1 y(0) - tf == 0, (cons_bound) q(tf) == [0, 0] - q̇(t) == [y(t), u(t)] - 0.5∫( u(t)^2 ) → min + q̇(s) == [y(s)+w, u(s)] + 0.5∫( u(s)^2 ) → min end nothing # hide ``` ### Control, state and variable -You can get access to the name of the control, the names of the components, its dimension and the box constraints `lb ≤ u(t) ≤ ub`, where `lb` stands for lower bound and `ub` for upper bound. +You can get access to the name of the control, state and variable, the names of the components and their dimensions. ```@example main using DataFrames @@ -200,17 +188,21 @@ push!(data,( )) ``` +!!! note + + The names of the components are used for instance when plotting the solution. See the [plot manual](@ref manual-plot). + ### Constraints The labelled constraints may be retrieved with the [`constraint`](@ref) function. The method `constraint(ocp, label)` returns a tuple of the form `(type, f, lb, ub)`. -The signature of the function `f` depends on the type. For boundary constraints (that is at initial and/or final time) and variable constraints, the signature is `f(x0, xf, v)` where `x0` stands for the initial state, `xf` the final state and `v` the variable. For other constraints, the signature is `f(t, x, u, v)`. +The signature of the function `f` depends on the symbol `type`. For `:boundary` and `:variable` constraints, the signature is `f(x0, xf, v)` where `x0` is the initial state, `xf` the final state and `v` the variable. For other constraints, the signature is `f(t, x, u, v)`. Here, `t` represents time, `x` the state, `u` the control, and `v` the variable. ```@example main (type, f, lb, ub) = constraint(ocp, :eq1) println("type: ", type) x0 = [0, 1] xf = [2, 3] -v = 4 +v = [1, 4] println("val: ", f(x0, xf, v)) println("lb: ", lb) println("ub: ", ub) @@ -229,7 +221,7 @@ println("ub: ", ub) println("type: ", type) t = 0 x = [1, 2] -u = [3, 4] +u = 3 println("val: ", f(t, x, u, v)) println("lb: ", lb) println("ub: ", ub) @@ -245,13 +237,152 @@ println("ub: ", ub) ### Dynamics +The dynamics stored in `ocp` are an [in-place function](https://docs.julialang.org/en/v1/manual/functions/#man-argument-passing) (the first argument is mutated upon call) of the form `f!(dx, t, x, u, v)`. Here, `t` represents time, `x` the state, `u` the control, and `v` the variable, with `dx` being the output value. + +```@example main +f! = dynamics(ocp) +t = 0 +x = [0., 1] +u = 2 +v = [1, 4] +dx = similar(x) +f!(dx, t, x, u, v) +dx +``` + +### Criterion and objective + +The criterion can be `:min` or `:max`. + +```@example main +criterion(ocp) +``` + +The objective function is either in Mayer, Lagrange or Bolza form. + +- Mayer: +```math +g(x(t_0), x(t_f), v) \to \min +``` +- Lagrange: +```math +\int_{t_0}^{t_f} f^0(t, x(t), u(t), v)\, \mathrm{d}t \to \min +``` +- Bolza: +```math +g(x(t_0), x(t_f), v) + \int_{t_0}^{t_f} f^0(t, x(t), u(t), v)\, \mathrm{d}t \to \min +``` + +The objective of problem `ocp` is `0.5∫( u(t)^2 ) → min`, hence, in Lagrange form. The signature of the Mayer part of the objective is `g(x0, xf, v)` but in our case, the method `mayer` will return an error. + +```@repl main +g = mayer(ocp) +``` + +The signature of the Lagrange part of the objective is `f⁰(t, x, u, v)`. + +```@example main +f⁰ = lagrange(ocp) +f⁰(t, x, u, v) +``` -### Objective +To avoid the necessity to capture exceptions, you can check the form of the objective: +```@example main +println("Mayer: ", has_mayer_cost(ocp)) +println("Lagrange: ", has_lagrange_cost(ocp)) +``` ### Times +The time variable is not named `t` but `s` in `ocp`. +```@example main +time_name(ocp) +``` + +The initial time is `0`. + +```@example main +initial_time(ocp) +``` + +Since the initial time has the value `0`, its name is `string(0)`. + +```@example main +initial_time_name(ocp) +``` + +In contrast, the final time is `tf`, since in `ocp` we have `s ∈ [0, tf]`. + +```@example main +final_time_name(ocp) +``` + +To get the value of the final time, since it is part of the variable `v = (w, tf)` of `ocp`, we need to provide a variable to the function `final_time`. + +```@example main +v = [1, 2] +tf = final_time(ocp, v) +``` + +```@repl main +final_time(ocp) +``` + +To check whether the initial or final time is fixed or free (i.e., part of the variable), you can use the following functions: + +```@example main +println("Fixed initial time: ", has_fixed_initial_time(ocp)) +println("Fixed final time: ", has_fixed_final_time(ocp)) +``` -## [Properties](@id manual-model-properties) +Or, similarly: +```@example main +println("Free initial time: ", has_free_initial_time(ocp)) +println("Free final time: ", has_free_final_time(ocp)) +``` + +### Time dependence + +Optimal control problems can be **autonomous** or **non-autonomous**. In an autonomous problem, neither the dynamics nor the Lagrange cost explicitly depends on the time variable. + +The following problem is autonomous. + +```@example main +ocp = @def begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + ẋ(t) == u(t) # no explicit dependence on t + x(1) + 0.5∫( u(t)^2 ) → min # no explicit dependence on t +end +is_autonomous(ocp) +``` + +The following problem is non-autonomous since the dynamics depends on `t`. + +```@example main +ocp = @def begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + ẋ(t) == u(t) + t # explicit dependence on t + x(1) + 0.5∫( u(t)^2 ) → min +end +is_autonomous(ocp) +``` + +Finally, this last problem is non-autonomous because the Lagrange part of the cost depends on `t`. + +```@example main +ocp = @def begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + ẋ(t) == u(t) + x(1) + 0.5∫( t + u(t)^2 ) → min # explicit dependence on t +end +is_autonomous(ocp) +``` \ No newline at end of file diff --git a/docs/src/manual-solution.md b/docs/src/manual-solution.md index 2b9602939..e44533d33 100644 --- a/docs/src/manual-solution.md +++ b/docs/src/manual-solution.md @@ -1,4 +1,4 @@ -# [Properties of an optimal control solution](@id manual-solution) +# [The optimal control solution object: structure and usage](@id manual-solution) ```@meta CollapsedDocStrings = false From 2e80c9b793c188f6835e9a19d162cc178722d06f Mon Sep 17 00:00:00 2001 From: Olivier Cots Date: Mon, 16 Jun 2025 23:30:53 +0200 Subject: [PATCH 5/7] first complete version of manual model --- docs/src/manual-model.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/manual-model.md b/docs/src/manual-model.md index f5f1f7fde..720857b24 100644 --- a/docs/src/manual-model.md +++ b/docs/src/manual-model.md @@ -129,7 +129,7 @@ definition(ocp) ## [Attributes and properties](@id manual-model-attributes) -More attributes can be retrived. To illustrate this, we define a more complex optimal control problem. +Numerous attributes can be retrieved. To illustrate this, a more complex optimal control problem is defined. ```@example main ocp = @def begin From 3f4c96b61e519d68c0738a09a053f6618b4d84cd Mon Sep 17 00:00:00 2001 From: Olivier Cots Date: Mon, 16 Jun 2025 23:34:55 +0200 Subject: [PATCH 6/7] correct english --- docs/src/manual-model.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/src/manual-model.md b/docs/src/manual-model.md index 720857b24..0f138e951 100644 --- a/docs/src/manual-model.md +++ b/docs/src/manual-model.md @@ -29,7 +29,7 @@ This structured approach will give you a comprehensive understanding of both wha ## [Main functionalities](@id manual-model-main-functionalities) -Let us define a basic optimal control problem. +Let's define a basic optimal control problem. ```@example main using OptimalControl @@ -101,7 +101,7 @@ The optimal control problem `ocp` is a [`Model`](@ref) struct. Model ``` -Each field can be access directly (`ocp.times`, etc) or by a getter: +Each field can be accessed directly (`ocp.times`, etc) or by a getter: - [`times`](@ref) - [`state`](@ref) @@ -152,7 +152,7 @@ nothing # hide ### Control, state and variable -You can get access to the name of the control, state and variable, the names of the components and their dimensions. +You can access the name of the control, state, and variable, along with the names of their components and their dimensions.. ```@example main using DataFrames @@ -194,7 +194,7 @@ push!(data,( ### Constraints -The labelled constraints may be retrieved with the [`constraint`](@ref) function. The method `constraint(ocp, label)` returns a tuple of the form `(type, f, lb, ub)`. +You can retrieve labelled constraints with the [`constraint`](@ref) function. The `constraint(ocp, label)` method returns a tuple of the form `(type, f, lb, ub)`. The signature of the function `f` depends on the symbol `type`. For `:boundary` and `:variable` constraints, the signature is `f(x0, xf, v)` where `x0` is the initial state, `xf` the final state and `v` the variable. For other constraints, the signature is `f(t, x, u, v)`. Here, `t` represents time, `x` the state, `u` the control, and `v` the variable. ```@example main @@ -286,7 +286,7 @@ f⁰ = lagrange(ocp) f⁰(t, x, u, v) ``` -To avoid the necessity to capture exceptions, you can check the form of the objective: +To avoid having to capture exceptions, you can check the form of the objective: ```@example main println("Mayer: ", has_mayer_cost(ocp)) From 5d60c00c7bd2e9d8ce371341b19dc7a35506fc5d Mon Sep 17 00:00:00 2001 From: Olivier Cots Date: Tue, 17 Jun 2025 11:14:31 +0200 Subject: [PATCH 7/7] add doc solution --- docs/src/assets/Manifest.toml | 12 +- docs/src/manual-flow-ocp.md | 16 +++ docs/src/manual-model.md | 8 +- docs/src/manual-solution.md | 210 ++++++++++++++++++++++++++++++++++ src/OptimalControl.jl | 2 +- 5 files changed, 238 insertions(+), 10 deletions(-) diff --git a/docs/src/assets/Manifest.toml b/docs/src/assets/Manifest.toml index 587477a79..d70310ee5 100644 --- a/docs/src/assets/Manifest.toml +++ b/docs/src/assets/Manifest.toml @@ -180,9 +180,9 @@ version = "0.2.6" [[deps.CTBase]] deps = ["DocStringExtensions"] -git-tree-sha1 = "1e0fb19f883cda373412fd40f2ccad71758cb876" +git-tree-sha1 = "ebc7d07d0bf4db7a841c5e7d51b4271bcf1e921c" uuid = "54762871-cc72-4466-b8e8-f6c8b58076cd" -version = "0.16.1" +version = "0.16.2" [[deps.CTDirect]] deps = ["ADNLPModels", "CTBase", "CTModels", "CTParser", "DocStringExtensions", "HSL", "MKL", "NLPModelsIpopt", "SparseArrays"] @@ -208,9 +208,9 @@ weakdeps = ["OrdinaryDiffEq"] [[deps.CTModels]] deps = ["CTBase", "DocStringExtensions", "Interpolations", "LinearAlgebra", "MLStyle", "MacroTools", "OrderedCollections", "Parameters", "PrettyTables", "RecipesBase"] -git-tree-sha1 = "dec8ae920442bbb4deedb9ca40619c58702b94a5" +git-tree-sha1 = "73b95a01af8369b5b08d1ecf3edf6b0bb79ce2c3" uuid = "34c4fa32-2049-4079-8329-de33c2a22e2d" -version = "0.5.3" +version = "0.5.4" weakdeps = ["JLD2", "JSON3", "Plots"] [deps.CTModels.extensions] @@ -1823,9 +1823,9 @@ version = "1.4.3" [[deps.Plots]] deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "JLFzf", "JSON", "LaTeXStrings", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "Pkg", "PlotThemes", "PlotUtils", "PrecompileTools", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "RelocatableFolders", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "TOML", "UUIDs", "UnicodeFun", "UnitfulLatexify", "Unzip"] -git-tree-sha1 = "809ba625a00c605f8d00cd2a9ae19ce34fc24d68" +git-tree-sha1 = "28ea788b78009c695eb0d637587c81d26bdf0e36" uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" -version = "1.40.13" +version = "1.40.14" [deps.Plots.extensions] FileIOExt = "FileIO" diff --git a/docs/src/manual-flow-ocp.md b/docs/src/manual-flow-ocp.md index 4b713fb5d..99ef748b1 100644 --- a/docs/src/manual-flow-ocp.md +++ b/docs/src/manual-flow-ocp.md @@ -468,6 +468,22 @@ From the maximizing condition, along a boundary arc, we have $p(t) = 0$. Differe \mu(x) = 2x. ``` +!!! note + + Within OptimalControl.jl, the constraint must be given in the form: + ```julia + c([t, ]x, u[, v]) + ``` + the control law in feedback form must be given as: + ```julia + u([t, ]x, p[, v]) + ``` + and the dual variable: + ```julia + μ([t, ]x, p[, v]) + ``` + The time `t` must be provided when the problem is [non-autonomous](@ref manual-model-time-dependence) and the variable `v` must be given when the optimal control problem contains a [variable](@ref manual-abstract-variable) to optimise. + The optimal control is a concatenation of 3 arcs: a negative bang arc followed by a boundary arc, followed by a positive bang arc. The initial covector is approximately $p(0)=-0.982237546583301$, the first switching time is $t_1 = 0.9$, and the exit time of the boundary is $t_2 = 1.6$. Let us check this by concatenating the three flows. ```@example main diff --git a/docs/src/manual-model.md b/docs/src/manual-model.md index 0f138e951..cd2b503ad 100644 --- a/docs/src/manual-model.md +++ b/docs/src/manual-model.md @@ -15,8 +15,6 @@ After covering these core functionalities, we'll delve into the **structure of a * **Is autonomous**: Does its dynamics depend explicitly on time? * **Has a fixed or free initial/final time**: Is the duration of the control problem predetermined or not? -This structured approach will give you a comprehensive understanding of both what you can *do* with an OCP and what an OCP *is*. - --- **Content** @@ -235,6 +233,10 @@ println("lb: ", lb) println("ub: ", ub) ``` +!!! note + + To get the dual variable (or Lagrange multiplier) associated to the constraint, use the [`dual`](@ref) method. + ### Dynamics The dynamics stored in `ocp` are an [in-place function](https://docs.julialang.org/en/v1/manual/functions/#man-argument-passing) (the first argument is mutated upon call) of the form `f!(dx, t, x, u, v)`. Here, `t` represents time, `x` the state, `u` the control, and `v` the variable, with `dx` being the output value. @@ -344,7 +346,7 @@ println("Free initial time: ", has_free_initial_time(ocp)) println("Free final time: ", has_free_final_time(ocp)) ``` -### Time dependence +### [Time dependence](@id manual-model-time-dependence) Optimal control problems can be **autonomous** or **non-autonomous**. In an autonomous problem, neither the dynamics nor the Lagrange cost explicitly depends on the time variable. diff --git a/docs/src/manual-solution.md b/docs/src/manual-solution.md index e44533d33..f87285c62 100644 --- a/docs/src/manual-solution.md +++ b/docs/src/manual-solution.md @@ -5,3 +5,213 @@ CollapsedDocStrings = false Draft = false ``` +In this manual, we'll first recall the **main functionalities** you can use when working with a solution of an optimal control problem (SOL). This includes essential operations like: + +* **Plotting a SOL**: How to plot the optimal solution for your defined problem. +* **Printing a SOL**: How to display a summary of your solution. + +After covering these core functionalities, we'll delve into the **structure of a SOL**. Since a SOL is structured as a [`Solution`](@ref) struct, we'll first explain how to **access its underlying attributes**. Following this, we'll shift our focus to the **simple properties** inherent to a SOL. + +--- + +**Content** + +- [Main functionalities](@ref manual-solution-main-functionalities) +- [Model struct](@ref manual-solution-struct) +- [Attributes and properties](@ref manual-solution-attributes) + +--- + +## [Main functionalities](@id manual-solution-main-functionalities) + +Let's define a basic optimal control problem. + +```@example main +using OptimalControl + +t0 = 0 +tf = 1 +x0 = [-1, 0] + +ocp = @def begin + t ∈ [ t0, tf ], time + x = (q, v) ∈ R², state + u ∈ R, control + x(t0) == x0 + x(tf) == [0, 0] + ẋ(t) == [v(t), u(t)] + 0.5∫( u(t)^2 ) → min +end +nothing # hide +``` + +We can now solve the problem (for more details, visit the [solve manual](@ref manual-solve)): + +```@example main +using NLPModelsIpopt +sol = solve(ocp) +nothing # hide +``` + +!!! note + + You can export (or save) the solution in a Julia `.jld2` data file and reload it later, and also export a discretised version of the solution in a more portable [JSON](https://en.wikipedia.org/wiki/JSON) format. Note that the optimal control problem is needed when loading a solution. + + See the two functions: + + - [`import_ocp_solution`](@ref), + - [`export_ocp_solution`](@ref). + +To print `sol`, simply: + +```@example main +sol +``` + +For complementary information, you can plot the solution: + +```@example main +using Plots +plot(sol) +``` + +!!! note + + For more details about plotting a solution, visit the [plot manual](@ref manual-plot). + +## [Model struct](@id manual-solution-struct) + +The solution `sol` is a [`Solution`](@ref) struct. + +```@docs; canonical=false +Solution +``` + +Each field can be accessed directly (`ocp.times`, etc) but we recommend to use the sophisticated getters we proveide: the `state(sol::Solution)` method does not return `sol.state` but a function of time that can be called at any time, not only on the grid `time_grid`. + +```@example main +0.25 ∈ time_grid(sol) +``` + +```@example main +x = state(sol) +x(0.25) +``` + +## [Attributes and properties](@id manual-solution-attributes) + +### State, costate, control, variable and objective value + +You can access the values of the state, costate, control and variable by eponymous functions. The returned values are functions of time for the state, costate and control and a scalar or a vector for the variable. + +```@example main +t = 0.25 +x = state(sol) +p = costate(sol) +u = control(sol) +nothing # hide +``` + +Since the state is of dimension 2, evaluating `x(t)` returns a vector: +```@example main +x(t) +``` + +It is the same for the costate: +```@example main +p(t) +``` + +But the control is one-dimensional: +```@example main +u(t) +``` + +There is no variable, hence, an empty vector is returned: +```@example main +v = variable(sol) +``` + +The objective value is accessed by: +```@example main +objective(sol) +``` + +### Infos from the solver + +The problem `ocp` is solved via a direct method (see [solve manual](@ref manual-solve) for details). The solver stores data in `sol`, including the success of the optimization, the iteration count, the time grid used for **discretisation**, and other specific details within the `solver_infos` field. + +```@example main +time_grid(sol) +``` + +```@example main +constraints_violation(sol) +``` + +```@example main +infos(sol) +``` + +```@example main +iterations(sol) +``` + +```@example main +message(sol) +``` + +```@example main +stopping(sol) +``` + +### Dual variables + +You can retrieved dual variables (or Lagrange multipliers) associated to labelled constraint. To illustrate this, we define a problem with constraints: + +```@example main +ocp = @def begin + + tf ∈ R, variable + t ∈ [0, tf], time + x = (q, v) ∈ R², state + u ∈ R, control + + tf ≥ 0, (eq_tf) + -1 ≤ u(t) ≤ 1, (eq_u) + v(t) ≤ 0.75, (eq_v) + + x(0) == [-1, 0], (eq_x0) + q(tf) == 0 + v(tf) == 0 + + ẋ(t) == [v(t), u(t)] + + tf → min + +end +sol = solve(ocp; display=false) +nothing # hide +``` + +Dual variables corresponding to variable and boundary constraints are given as scalar or vectors. + +```@example main +dual(sol, ocp, :eq_tf) +``` + +```@example main +dual(sol, ocp, :eq_x0) +``` + +The other type of constraints are associated to dual variables given as functions of time. + +```@example main +μ_u = dual(sol, ocp, :eq_u) +plot(time_grid(sol), μ_u) +``` + +```@example main +μ_v = dual(sol, ocp, :eq_v) +plot(time_grid(sol), μ_v) +``` \ No newline at end of file diff --git a/src/OptimalControl.jl b/src/OptimalControl.jl index 4e01f3435..c680f9ea9 100644 --- a/src/OptimalControl.jl +++ b/src/OptimalControl.jl @@ -164,4 +164,4 @@ export solve export available_methods include("solve.jl") -end +end \ No newline at end of file