From 74f51151674413097994cd592774462d2fab1a97 Mon Sep 17 00:00:00 2001 From: Youri Date: Tue, 10 Jun 2025 11:20:43 +0200 Subject: [PATCH 01/20] First commit --- docs/src/tutorial-free-times.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/src/tutorial-free-times.md diff --git a/docs/src/tutorial-free-times.md b/docs/src/tutorial-free-times.md new file mode 100644 index 0000000..e69de29 From 7684f0f5c32a687cde6e28ead1c3393e1e014b51 Mon Sep 17 00:00:00 2001 From: Olivier Cots Date: Tue, 10 Jun 2025 11:38:05 +0200 Subject: [PATCH 02/20] add explanation draft=false by file --- docs/make.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/make.jl b/docs/make.jl index 93aeb96..83269af 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -9,6 +9,10 @@ repo_url = "github.com/control-toolbox/Tutorials.jl" makedocs(; draft=false, # 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 + # ``` warnonly=[:cross_references, :autodocs_block], sitename="Tutorials", format=Documenter.HTML(; From ec5e2821140a857199ea7aa379cd4f307783f6b0 Mon Sep 17 00:00:00 2001 From: Olivier Cots Date: Tue, 10 Jun 2025 11:43:22 +0200 Subject: [PATCH 03/20] add free times in menu --- docs/make.jl | 3 ++- docs/src/assets/Manifest.toml | 14 ++++++++++++-- docs/src/assets/Project.toml | 6 +++++- docs/src/tutorial-free-times.md | 11 +++++++++++ 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 83269af..ce35cf6 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -8,7 +8,7 @@ cp("./docs/Project.toml", "./docs/src/assets/Project.toml"; force=true) repo_url = "github.com/control-toolbox/Tutorials.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 @@ -29,6 +29,7 @@ makedocs(; "Tutorials and Advanced Features" => [ "Discrete continuation" => "tutorial-continuation.md", "Discretisation methods" => "tutorial-discretisation.md", + "Free times" => "tutorial-free-times.md", "NLP manipulations" => "tutorial-nlp.md", "Indirect simple shooting" => "tutorial-iss.md", "Goddard: direct, indirect" => "tutorial-goddard.md", diff --git a/docs/src/assets/Manifest.toml b/docs/src/assets/Manifest.toml index f31f319..eb7f009 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 = "a0f7a38986e41d62db81da09b6b12da3c238b679" +project_hash = "e7183c2ad8bb7ad385c3187377b285a34e621395" [[deps.ADNLPModels]] deps = ["ADTypes", "ForwardDiff", "LinearAlgebra", "NLPModels", "Requires", "ReverseDiff", "SparseArrays", "SparseConnectivityTracer", "SparseMatrixColorings"] @@ -150,6 +150,12 @@ version = "1.1.0" uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" version = "1.11.0" +[[deps.BenchmarkTools]] +deps = ["Compat", "JSON", "Logging", "Printf", "Profile", "Statistics", "UUIDs"] +git-tree-sha1 = "e38fbc49a620f5d0b660d7f543db1009fe0f8336" +uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +version = "1.6.0" + [[deps.BitFlags]] git-tree-sha1 = "0691e34b3bb8be9307330f88d1a3c3f25466c24d" uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35" @@ -1843,6 +1849,10 @@ deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" version = "1.11.0" +[[deps.Profile]] +uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" +version = "1.11.0" + [[deps.PtrArrays]] git-tree-sha1 = "1d36ef11a9aaf1e8b74dacc6a731dd1de8fd493d" uuid = "43287f4e-b6f4-7ad1-bb20-aadabca52c3d" @@ -2353,7 +2363,7 @@ version = "1.4.0" [[deps.Tutorials]] path = "/Users/ocots/Research/logiciels/dev/control-toolbox/Tutorials" uuid = "cb10daa6-a5e5-4c25-a171-ae181b8ea3c9" -version = "0.1.3" +version = "0.1.4" [[deps.URIs]] git-tree-sha1 = "cbbebadbcc76c5ca1cc4b4f3b0614b3e603b5000" diff --git a/docs/src/assets/Project.toml b/docs/src/assets/Project.toml index ef12086..0cc8353 100644 --- a/docs/src/assets/Project.toml +++ b/docs/src/assets/Project.toml @@ -1,4 +1,5 @@ [deps] +BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" DifferentiationInterface = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" @@ -12,10 +13,12 @@ NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec" OptimalControl = "5f98b655-cc9a-415a-b60e-744165666948" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" Tutorials = "cb10daa6-a5e5-4c25-a171-ae181b8ea3c9" [compat] +BenchmarkTools = "1.6" DataFrames = "1.7" DifferentiationInterface = "0.7" Documenter = "1.8" @@ -29,5 +32,6 @@ NonlinearSolve = "4.6" OptimalControl = "1.0" OrdinaryDiffEq = "6.93" Plots = "1.40" +Printf = "1" Suppressor = "0.2" -julia = "1" +julia = "1.10" diff --git a/docs/src/tutorial-free-times.md b/docs/src/tutorial-free-times.md index e69de29..665fa5d 100644 --- a/docs/src/tutorial-free-times.md +++ b/docs/src/tutorial-free-times.md @@ -0,0 +1,11 @@ +# [Free times](@id tutorial-free-times) + +```@meta +Draft = false +``` + +Blabla + +```@example main-freetimes +x=1 +``` \ No newline at end of file From ad3c164d2a6fbf16b82d45289829aa4d083a91cb Mon Sep 17 00:00:00 2001 From: yassin moukan Date: Tue, 10 Jun 2025 14:07:32 +0200 Subject: [PATCH 04/20] test first commit --- docs/src/tutorial-free-times.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/tutorial-free-times.md b/docs/src/tutorial-free-times.md index 665fa5d..1cb33d2 100644 --- a/docs/src/tutorial-free-times.md +++ b/docs/src/tutorial-free-times.md @@ -5,6 +5,7 @@ Draft = false ``` Blabla +test commit push ```@example main-freetimes x=1 From 914af705d2d5a7fa858fd40b40403cdc9b7ebfd8 Mon Sep 17 00:00:00 2001 From: yassin moukan Date: Tue, 10 Jun 2025 17:11:34 +0200 Subject: [PATCH 05/20] =?UTF-8?q?tutoriel-free-times=20modifi=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/src/assets/Manifest.toml | 282 +++++++++++++------------------- docs/src/tutorial-free-times.md | 44 ++++- 2 files changed, 153 insertions(+), 173 deletions(-) diff --git a/docs/src/assets/Manifest.toml b/docs/src/assets/Manifest.toml index eb7f009..a5c6501 100644 --- a/docs/src/assets/Manifest.toml +++ b/docs/src/assets/Manifest.toml @@ -1,6 +1,6 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.11.5" +julia_version = "1.11.2" manifest_format = "2.0" project_hash = "e7183c2ad8bb7ad385c3187377b285a34e621395" @@ -88,12 +88,6 @@ version = "1.1.3" uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" version = "1.1.2" -[[deps.ArnoldiMethod]] -deps = ["LinearAlgebra", "Random", "StaticArrays"] -git-tree-sha1 = "d57bd3762d308bded22c3b82d033bff85f6195c6" -uuid = "ec485272-7323-5ecc-a04f-4719b315124d" -version = "0.4.0" - [[deps.ArrayInterface]] deps = ["Adapt", "LinearAlgebra"] git-tree-sha1 = "9606d7832795cbef89e06a550475be300364a8aa" @@ -169,12 +163,13 @@ version = "0.1.6" [[deps.BracketingNonlinearSolve]] deps = ["CommonSolve", "ConcreteStructs", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase"] -git-tree-sha1 = "637ebe439ba587828fd997b7810d8171eed2ea1b" +git-tree-sha1 = "a9014924595b7a2c1dd14aac516e38fa10ada656" uuid = "70df07ce-3d50-431d-a3e7-ca6ddb60ac1e" -version = "1.2.0" -weakdeps = ["ForwardDiff"] +version = "1.3.0" +weakdeps = ["ChainRulesCore", "ForwardDiff"] [deps.BracketingNonlinearSolve.extensions] + BracketingNonlinearSolveChainRulesCoreExt = ["ChainRulesCore", "ForwardDiff"] BracketingNonlinearSolveForwardDiffExt = "ForwardDiff" [[deps.Bzip2_jll]] @@ -191,9 +186,9 @@ version = "0.2.6" [[deps.CTBase]] deps = ["DocStringExtensions"] -git-tree-sha1 = "715a4381eb5e1d11ee92d86759ada1c27689345f" +git-tree-sha1 = "1e0fb19f883cda373412fd40f2ccad71758cb876" uuid = "54762871-cc72-4466-b8e8-f6c8b58076cd" -version = "0.16.0" +version = "0.16.1" [[deps.CTDirect]] deps = ["ADNLPModels", "CTBase", "CTModels", "CTParser", "DocStringExtensions", "HSL", "MKL", "NLPModelsIpopt", "SparseArrays"] @@ -244,9 +239,9 @@ version = "0.2.5" [[deps.Cairo_jll]] deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "2ac646d71d0d24b44f3f8c84da8c9f4d70fb67df" +git-tree-sha1 = "fde3bf89aead2e723284a8ff9cdf5b551ed700e8" uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.18.4+0" +version = "1.18.5+0" [[deps.ChainRulesCore]] deps = ["Compat", "LinearAlgebra"] @@ -298,9 +293,9 @@ weakdeps = ["SpecialFunctions"] [[deps.Colors]] deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] -git-tree-sha1 = "64e15186f0aa277e174aa81798f7eb8598e0157e" +git-tree-sha1 = "37ea44092930b1811e666c3bc38065d7d87fcc74" uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.13.0" +version = "0.13.1" [[deps.CommonSolve]] git-tree-sha1 = "0eee5eb66b1cf62cd6ad1b460238e60e4b09400c" @@ -425,9 +420,9 @@ version = "1.9.1" [[deps.DiffEqBase]] deps = ["ArrayInterface", "ConcreteStructs", "DataStructures", "DocStringExtensions", "EnumX", "EnzymeCore", "FastBroadcast", "FastClosures", "FastPower", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "Parameters", "PrecompileTools", "Printf", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "Setfield", "Static", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "TruncatedStacktraces"] -git-tree-sha1 = "1bcd3a5c585c477e5d0595937ea7b5adcda6c621" +git-tree-sha1 = "a0e5b5669df9465bc3dd32ea4a8ddeefbc0f7b5c" uuid = "2b5f629d-d688-5b77-993f-72d75c75574e" -version = "6.174.0" +version = "6.175.0" [deps.DiffEqBase.extensions] DiffEqBaseCUDAExt = "CUDA" @@ -440,6 +435,7 @@ version = "6.174.0" DiffEqBaseMPIExt = "MPI" DiffEqBaseMeasurementsExt = "Measurements" DiffEqBaseMonteCarloMeasurementsExt = "MonteCarloMeasurements" + DiffEqBaseMooncakeExt = "Mooncake" DiffEqBaseReverseDiffExt = "ReverseDiff" DiffEqBaseSparseArraysExt = "SparseArrays" DiffEqBaseTrackerExt = "Tracker" @@ -456,6 +452,7 @@ version = "6.174.0" MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" @@ -529,15 +526,15 @@ uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" version = "1.11.0" [[deps.DocStringExtensions]] -git-tree-sha1 = "e7b7e6f178525d17c720ab9c081e4ef04429f860" +git-tree-sha1 = "7442a5dfe1ebb773c29cc2962a8980f47221d76c" uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.9.4" +version = "0.9.5" [[deps.Documenter]] deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "CodecZlib", "Dates", "DocStringExtensions", "Downloads", "Git", "IOCapture", "InteractiveUtils", "JSON", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "TOML", "Test", "Unicode"] -git-tree-sha1 = "6c182d0bd94142d7cbc3ae8a1e74668f15d0dd65" +git-tree-sha1 = "0ef76e54dfe9d736350a79334dc66236598c8d38" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "1.11.4" +version = "1.12.0" [[deps.Downloads]] deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] @@ -550,9 +547,9 @@ uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" version = "1.0.5" [[deps.EnzymeCore]] -git-tree-sha1 = "1eb59f40a772d0fbd4cb75e00b3fa7f5f79c975a" +git-tree-sha1 = "7d7822a643c33bbff4eab9c87ca8459d7c688db0" uuid = "f151be2c-9106-41f4-ab19-57ee4f262869" -version = "0.8.9" +version = "0.8.11" weakdeps = ["Adapt"] [deps.EnzymeCore.extensions] @@ -789,9 +786,9 @@ version = "2.49.0+0" [[deps.Glib_jll]] deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "b0036b392358c80d2d2124746c2bf3d48d457938" +git-tree-sha1 = "fee60557e4f19d0fe5cd169211fdda80e494f4e8" uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.82.4+0" +version = "2.84.0+0" [[deps.Graphite2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -799,12 +796,6 @@ git-tree-sha1 = "8a6dbda1fd736d60cc477d99f2e7a042acfa46e8" uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" version = "1.3.15+0" -[[deps.Graphs]] -deps = ["ArnoldiMethod", "Compat", "DataStructures", "Distributed", "Inflate", "LinearAlgebra", "Random", "SharedArrays", "SimpleTraits", "SparseArrays", "Statistics"] -git-tree-sha1 = "3169fd3440a02f35e549728b0890904cfd4ae58a" -uuid = "86223c79-3864-5bf0-83f7-82e725a168b6" -version = "1.12.1" - [[deps.Grisu]] git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe" @@ -830,9 +821,9 @@ version = "1.10.16" [[deps.HarfBuzz_jll]] deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll"] -git-tree-sha1 = "55c53be97790242c29031e5cd45e8ac296dadda3" +git-tree-sha1 = "f923f9a774fcf3f5cb761bfa43aeadd689714813" uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" -version = "8.5.0+0" +version = "8.5.1+0" [[deps.Hwloc_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -851,11 +842,6 @@ git-tree-sha1 = "debdd00ffef04665ccbb3e150747a77560e8fad1" uuid = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" version = "0.1.1" -[[deps.Inflate]] -git-tree-sha1 = "d1b1b796e47d94588b3757fe84fbf65a5ec4a80d" -uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" -version = "0.1.5" - [[deps.InlineStrings]] git-tree-sha1 = "6a9fde685a7ac1eb3495f8e812c5a7c3711c2d5e" uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" @@ -907,9 +893,9 @@ version = "1.3.1" [[deps.Ipopt]] deps = ["Ipopt_jll", "LinearAlgebra", "OpenBLAS32_jll", "PrecompileTools"] -git-tree-sha1 = "100030874c53b61d8c21d1bcb725265555d146ff" +git-tree-sha1 = "4ad0d2dea51e5d49866b40a2d2521da6a1be7097" uuid = "b6b21f68-93f8-5de0-b562-5493be1d77c9" -version = "1.10.3" +version = "1.10.6" [deps.Ipopt.extensions] IpoptMathOptInterfaceExt = "MathOptInterface" @@ -919,9 +905,9 @@ version = "1.10.3" [[deps.Ipopt_jll]] deps = ["ASL_jll", "Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "MUMPS_seq_jll", "SPRAL_jll", "libblastrampoline_jll"] -git-tree-sha1 = "4f55ad688c698a4f77d892a1cb673f7e8a30f178" +git-tree-sha1 = "1bb978524c2837be596aeb2b69951feb6b9822f8" uuid = "9cc047cb-c261-5740-88fc-0cf96f7bdcc7" -version = "300.1400.1700+0" +version = "300.1400.1701+0" [[deps.IrrationalConstants]] git-tree-sha1 = "e2222959fbc6c19554dc15174c81bf7bf3aa691c" @@ -1006,19 +992,21 @@ version = "1.4.0" [[deps.Latexify]] deps = ["Format", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "OrderedCollections", "Requires"] -git-tree-sha1 = "cd10d2cc78d34c0e2a3a36420ab607b611debfbb" +git-tree-sha1 = "4f34eaabe49ecb3fb0d58d6015e32fd31a733199" uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" -version = "0.16.7" +version = "0.16.8" [deps.Latexify.extensions] DataFramesExt = "DataFrames" SparseArraysExt = "SparseArrays" SymEngineExt = "SymEngine" + TectonicExt = "tectonic_jll" [deps.Latexify.weakdeps] DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SymEngine = "123dc426-2d89-5057-bbad-38513e3affd8" + tectonic_jll = "d7dd28d6-a5e6-559c-9131-7eb760cdacc5" [[deps.LayoutPointers]] deps = ["ArrayInterface", "LinearAlgebra", "ManualMemory", "SIMDTypes", "Static", "StaticArrayInterface"] @@ -1084,10 +1072,10 @@ uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" version = "1.11.0" [[deps.Libffi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "27ecae93dd25ee0909666e6835051dd684cc035e" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "c8da7e6a91781c41a863611c7e966098d783c57a" uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" -version = "3.2.2+2" +version = "3.4.7+0" [[deps.Libglvnd_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll", "Xorg_libXext_jll"] @@ -1164,9 +1152,9 @@ version = "2.10.0" [[deps.LinearSolve]] deps = ["ArrayInterface", "ChainRulesCore", "ConcreteStructs", "DocStringExtensions", "EnumX", "GPUArraysCore", "InteractiveUtils", "Krylov", "LazyArrays", "Libdl", "LinearAlgebra", "MKL_jll", "Markdown", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "Setfield", "StaticArraysCore", "UnPack"] -git-tree-sha1 = "6cc433e4e8cf070fc54bf29f620685d21890fe07" +git-tree-sha1 = "c0d1a91a50af6778863d320761f807f641f74935" uuid = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" -version = "3.14.1" +version = "3.17.0" [deps.LinearSolve.extensions] LinearSolveBandedMatricesExt = "BandedMatrices" @@ -1261,9 +1249,9 @@ version = "0.4.17" [[deps.MUMPS_seq_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "METIS_jll", "libblastrampoline_jll"] -git-tree-sha1 = "0eab12f94948ca67908aec14b9f2ebefd17463fe" +git-tree-sha1 = "196f61d99adc06f32c32bc4afe5298d9b1e862c8" uuid = "d7ed1dd3-d0ae-5e8e-bfb4-87a502085b8d" -version = "500.700.301+0" +version = "500.800.0+0" [[deps.MacroTools]] git-tree-sha1 = "1e0228a030642014fe5cfe68c2c0a818f9e3f522" @@ -1362,10 +1350,10 @@ uuid = "f4238b75-b362-5c4c-b852-0801c9a21d71" version = "0.10.4" [[deps.NLSolversBase]] -deps = ["DiffResults", "Distributed", "FiniteDiff", "ForwardDiff"] -git-tree-sha1 = "a0b464d183da839699f4c79e7606d9d186ec172c" +deps = ["ADTypes", "DifferentiationInterface", "Distributed", "FiniteDiff", "ForwardDiff"] +git-tree-sha1 = "25a6638571a902ecfb1ae2a18fc1575f86b1d4df" uuid = "d41bc354-129a-5804-8e4c-c37616107c6c" -version = "7.8.3" +version = "7.10.0" [[deps.NaNMath]] deps = ["OpenLibm_jll"] @@ -1411,9 +1399,9 @@ version = "4.9.0" [[deps.NonlinearSolveBase]] deps = ["ADTypes", "Adapt", "ArrayInterface", "CommonSolve", "Compat", "ConcreteStructs", "DifferentiationInterface", "EnzymeCore", "FastClosures", "LinearAlgebra", "Markdown", "MaybeInplace", "Preferences", "Printf", "RecursiveArrayTools", "SciMLBase", "SciMLJacobianOperators", "SciMLOperators", "StaticArraysCore", "SymbolicIndexingInterface", "TimerOutputs"] -git-tree-sha1 = "1a6f6b161a644beac3c46a46f9bbb830c24abffb" +git-tree-sha1 = "404d71dd057759f4d590191a643113485c4a482a" uuid = "be0214bd-f91f-a760-ac4e-3421ce2b2da0" -version = "1.8.0" +version = "1.12.0" [deps.NonlinearSolveBase.extensions] NonlinearSolveBaseBandedMatricesExt = "BandedMatrices" @@ -1441,9 +1429,9 @@ version = "1.5.0" [[deps.NonlinearSolveQuasiNewton]] deps = ["ArrayInterface", "CommonSolve", "ConcreteStructs", "DiffEqBase", "LinearAlgebra", "LinearSolve", "MaybeInplace", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase", "SciMLOperators", "StaticArraysCore"] -git-tree-sha1 = "b69a68ef3a7bba7ab1d5ef6321ed6d9a613142b0" +git-tree-sha1 = "e3888bdbab6e0bfadbc3164ef4595e40e7b7e954" uuid = "9a2c21bd-3a47-402d-9113-8faf9a0ee114" -version = "1.5.0" +version = "1.6.0" weakdeps = ["ForwardDiff"] [deps.NonlinearSolveQuasiNewton.extensions] @@ -1488,7 +1476,7 @@ version = "0.3.27+1" [[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.5+0" +version = "0.8.1+2" [[deps.OpenSSH_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "OpenSSL_jll", "Zlib_jll"] @@ -1533,9 +1521,9 @@ version = "1.8.1" [[deps.OrdinaryDiffEq]] deps = ["ADTypes", "Adapt", "ArrayInterface", "DataStructures", "DiffEqBase", "DocStringExtensions", "EnumX", "ExponentialUtilities", "FastBroadcast", "FastClosures", "FillArrays", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "InteractiveUtils", "LineSearches", "LinearAlgebra", "LinearSolve", "Logging", "MacroTools", "MuladdMacro", "NonlinearSolve", "OrdinaryDiffEqAdamsBashforthMoulton", "OrdinaryDiffEqBDF", "OrdinaryDiffEqCore", "OrdinaryDiffEqDefault", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqExplicitRK", "OrdinaryDiffEqExponentialRK", "OrdinaryDiffEqExtrapolation", "OrdinaryDiffEqFIRK", "OrdinaryDiffEqFeagin", "OrdinaryDiffEqFunctionMap", "OrdinaryDiffEqHighOrderRK", "OrdinaryDiffEqIMEXMultistep", "OrdinaryDiffEqLinear", "OrdinaryDiffEqLowOrderRK", "OrdinaryDiffEqLowStorageRK", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqNordsieck", "OrdinaryDiffEqPDIRK", "OrdinaryDiffEqPRK", "OrdinaryDiffEqQPRK", "OrdinaryDiffEqRKN", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqSDIRK", "OrdinaryDiffEqSSPRK", "OrdinaryDiffEqStabilizedIRK", "OrdinaryDiffEqStabilizedRK", "OrdinaryDiffEqSymplecticRK", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "Polyester", "PreallocationTools", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleNonlinearSolve", "SimpleUnPack", "SparseArrays", "Static", "StaticArrayInterface", "StaticArrays", "TruncatedStacktraces"] -git-tree-sha1 = "56d5500e9970f0112a4e1ab6474d6fedde61ef64" +git-tree-sha1 = "1c2b2df870944e0dc01454fd87479847c55fa26c" uuid = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" -version = "6.97.0" +version = "6.98.0" [[deps.OrdinaryDiffEqAdamsBashforthMoulton]] deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqLowOrderRK", "Polyester", "RecursiveArrayTools", "Reexport", "Static"] @@ -1545,9 +1533,9 @@ version = "1.2.0" [[deps.OrdinaryDiffEqBDF]] deps = ["ADTypes", "ArrayInterface", "DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqSDIRK", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "StaticArrays", "TruncatedStacktraces"] -git-tree-sha1 = "970ac761ae1c4249fc70d75760c328269a518585" +git-tree-sha1 = "9124a686af119063bb4d3a8f87044a8f312fcad9" uuid = "6ad6398a-0878-4a85-9266-38940aa047c8" -version = "1.3.0" +version = "1.6.0" [[deps.OrdinaryDiffEqCore]] deps = ["ADTypes", "Accessors", "Adapt", "ArrayInterface", "DataStructures", "DiffEqBase", "DocStringExtensions", "EnumX", "FastBroadcast", "FastClosures", "FastPower", "FillArrays", "FunctionWrappersWrappers", "InteractiveUtils", "LinearAlgebra", "Logging", "MacroTools", "MuladdMacro", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleUnPack", "Static", "StaticArrayInterface", "StaticArraysCore", "SymbolicIndexingInterface", "TruncatedStacktraces"] @@ -1566,10 +1554,10 @@ uuid = "50262376-6c5a-4cf5-baba-aaf4f84d72d7" version = "1.4.0" [[deps.OrdinaryDiffEqDifferentiation]] -deps = ["ADTypes", "ArrayInterface", "DiffEqBase", "FastBroadcast", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEqCore", "SciMLBase", "SparseArrays", "SparseDiffTools", "StaticArrayInterface", "StaticArrays"] -git-tree-sha1 = "9a535370247496c1375ba6d08b0493c0d856d25b" +deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "ConstructionBase", "DiffEqBase", "DifferentiationInterface", "FastBroadcast", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEqCore", "SciMLBase", "SciMLOperators", "SparseArrays", "SparseMatrixColorings", "StaticArrayInterface", "StaticArrays"] +git-tree-sha1 = "efecf0c4cc44e16251b0e718f08b0876b2a82b80" uuid = "4302a76b-040a-498a-8c04-15b101fed76b" -version = "1.4.0" +version = "1.10.0" [[deps.OrdinaryDiffEqExplicitRK]] deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "TruncatedStacktraces"] @@ -1591,9 +1579,9 @@ version = "1.5.0" [[deps.OrdinaryDiffEqFIRK]] deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "FastGaussQuadrature", "FastPower", "LinearAlgebra", "LinearSolve", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators"] -git-tree-sha1 = "588f454cc1c48c20a32f03838af4983053a1d3ae" +git-tree-sha1 = "0da8ec3491821262a3d2828e6370e76b51a770a3" uuid = "5960d6e9-dd7a-4743-88e7-cf307b64f125" -version = "1.9.0" +version = "1.12.0" [[deps.OrdinaryDiffEqFeagin]] deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "RecursiveArrayTools", "Reexport", "Static"] @@ -1639,9 +1627,9 @@ version = "1.3.0" [[deps.OrdinaryDiffEqNonlinearSolve]] deps = ["ADTypes", "ArrayInterface", "DiffEqBase", "FastBroadcast", "FastClosures", "ForwardDiff", "LinearAlgebra", "LinearSolve", "MuladdMacro", "NonlinearSolve", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "PreallocationTools", "RecursiveArrayTools", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleNonlinearSolve", "StaticArrays"] -git-tree-sha1 = "1b89e3e84752a3cbd2c94db565e6ea7acb5279b2" +git-tree-sha1 = "ffdb0f5207b0e30f8b1edf99b3b9546d9c48ccaf" uuid = "127b3ac7-2247-4354-8eb6-78cf4e7c58e8" -version = "1.5.0" +version = "1.10.0" [[deps.OrdinaryDiffEqNordsieck]] deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqTsit5", "Polyester", "RecursiveArrayTools", "Reexport", "Static"] @@ -1651,9 +1639,9 @@ version = "1.1.0" [[deps.OrdinaryDiffEqPDIRK]] deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Polyester", "Reexport", "StaticArrays"] -git-tree-sha1 = "f74b27b8b811a83d77a9cad6293e793ab0804cdc" +git-tree-sha1 = "ab9897e4bc8e3cf8e15f1cf61dbdd15d6a2341d7" uuid = "5dd0a6cf-3d4b-4314-aa06-06d4e299bc89" -version = "1.3.0" +version = "1.3.1" [[deps.OrdinaryDiffEqPRK]] deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "Reexport"] @@ -1674,10 +1662,10 @@ uuid = "af6ede74-add8-4cfd-b1df-9a4dbb109d7a" version = "1.1.0" [[deps.OrdinaryDiffEqRosenbrock]] -deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "LinearSolve", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "Static"] -git-tree-sha1 = "bede3226fd485741e364239e23236e07ace77639" +deps = ["ADTypes", "DiffEqBase", "DifferentiationInterface", "FastBroadcast", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "LinearSolve", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "Static"] +git-tree-sha1 = "1ce0096d920e95773220e818f29bf4b37ea2bb78" uuid = "43230ef6-c299-4910-a778-202eb28ce4ce" -version = "1.8.0" +version = "1.11.0" [[deps.OrdinaryDiffEqSDIRK]] deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "RecursiveArrayTools", "Reexport", "SciMLBase", "TruncatedStacktraces"] @@ -1726,17 +1714,11 @@ deps = ["Artifacts", "Libdl"] uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" version = "10.42.0+1" -[[deps.PackageExtensionCompat]] -git-tree-sha1 = "fb28e33b8a95c4cee25ce296c817d89cc2e53518" -uuid = "65ce6f38-6b18-4e1d-a461-8949797d7930" -version = "1.0.2" -weakdeps = ["Requires", "TOML"] - [[deps.Pango_jll]] deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl"] -git-tree-sha1 = "3b31172c032a1def20c98dae3f2cdc9d10e3b561" +git-tree-sha1 = "275a9a6d85dc86c24d03d1837a0010226a96f540" uuid = "36c8627f-9965-5494-a995-c6b170f724f3" -version = "1.56.1+0" +version = "1.56.3+0" [[deps.Parameters]] deps = ["OrderedCollections", "UnPack"] @@ -1799,9 +1781,9 @@ version = "1.40.13" [[deps.Polyester]] deps = ["ArrayInterface", "BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "ManualMemory", "PolyesterWeave", "Static", "StaticArrayInterface", "StrideArraysCore", "ThreadingUtilities"] -git-tree-sha1 = "2082cc4be5e765bd982ed04ea06c068f4f702410" +git-tree-sha1 = "6f7cd22a802094d239824c57d94c8e2d0f7cfc7d" uuid = "f517fe37-dbe3-4b94-8317-1923a5111588" -version = "0.7.17" +version = "0.7.18" [[deps.PolyesterWeave]] deps = ["BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "Static", "ThreadingUtilities"] @@ -1979,9 +1961,9 @@ version = "1.16.1" [[deps.RuntimeGeneratedFunctions]] deps = ["ExprTools", "SHA", "Serialization"] -git-tree-sha1 = "7cb9d10026d630ce2dd2a1fc6006a3d5041b34c0" +git-tree-sha1 = "86a8a8b783481e1ea6b9c91dd949cb32191f8ab4" uuid = "7e49a35a-f44a-4d26-94aa-eba1b4ca6b47" -version = "0.5.14" +version = "0.5.15" [[deps.SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" @@ -1994,15 +1976,15 @@ version = "0.1.0" [[deps.SPRAL_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "Libdl", "METIS_jll", "libblastrampoline_jll"] -git-tree-sha1 = "11f3da4b25efacd1cec8e263421f2a9003a5e8e0" +git-tree-sha1 = "4f9833187a65ead66ed1907b44d5f20606282e3f" uuid = "319450e9-13b8-58e8-aa9f-8fd1420848ab" -version = "2024.5.8+0" +version = "2025.5.20+0" [[deps.SciMLBase]] -deps = ["ADTypes", "Accessors", "ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "Moshi", "PrecompileTools", "Preferences", "Printf", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "SciMLStructures", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface"] -git-tree-sha1 = "2fd047893cb0089b180fcbb7e8434ba15dcc2841" +deps = ["ADTypes", "Accessors", "Adapt", "ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "Moshi", "PrecompileTools", "Preferences", "Printf", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "SciMLStructures", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface"] +git-tree-sha1 = "d7bef263e23c7f5392071e4538b1949cee7ae458" uuid = "0bca4576-84f4-4d90-8ffe-ffa030f20462" -version = "2.87.0" +version = "2.99.0" [deps.SciMLBase.extensions] SciMLBaseChainRulesCoreExt = "ChainRulesCore" @@ -2027,15 +2009,15 @@ version = "2.87.0" [[deps.SciMLJacobianOperators]] deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "ConstructionBase", "DifferentiationInterface", "FastClosures", "LinearAlgebra", "SciMLBase", "SciMLOperators"] -git-tree-sha1 = "d563758f3ce5153810adebc534d88e24d34eeb95" +git-tree-sha1 = "7da1216346ad79499d08d7e2a3dbf297dc80c829" uuid = "19f34311-ddf3-4b8b-af20-060888a46c0e" -version = "0.1.5" +version = "0.1.6" [[deps.SciMLOperators]] deps = ["Accessors", "ArrayInterface", "DocStringExtensions", "LinearAlgebra", "MacroTools"] -git-tree-sha1 = "1c4b7f6c3e14e6de0af66e66b86d525cae10ecb4" +git-tree-sha1 = "d82853c515a8d9d42c1ab493a2687a37f1e26c91" uuid = "c0aeaf25-5076-4817-a8d5-81caf7dfa961" -version = "0.3.13" +version = "0.4.0" weakdeps = ["SparseArrays", "StaticArraysCore"] [deps.SciMLOperators.extensions] @@ -2104,12 +2086,6 @@ version = "2.5.0" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" -[[deps.SimpleTraits]] -deps = ["InteractiveUtils", "MacroTools"] -git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231" -uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d" -version = "0.9.4" - [[deps.SimpleUnPack]] git-tree-sha1 = "58e6353e72cde29b90a69527e56df1b5c3d8c437" uuid = "ce78b400-467f-4804-87d8-8f486da07d0a" @@ -2138,9 +2114,9 @@ version = "1.11.0" [[deps.SparseConnectivityTracer]] deps = ["ADTypes", "DocStringExtensions", "FillArrays", "LinearAlgebra", "Random", "SparseArrays"] -git-tree-sha1 = "fadb2d7010dd92912e5eb31a493613ad4b8c9583" +git-tree-sha1 = "2c3cbb3703f77045d4eb891b2831ca132ef4183c" uuid = "9f842d2f-2579-4b1d-911e-f412cf18a3f5" -version = "0.6.18" +version = "0.6.19" [deps.SparseConnectivityTracer.extensions] SparseConnectivityTracerDataInterpolationsExt = "DataInterpolations" @@ -2156,31 +2132,11 @@ version = "0.6.18" NaNMath = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" -[[deps.SparseDiffTools]] -deps = ["ADTypes", "Adapt", "ArrayInterface", "Compat", "DataStructures", "FiniteDiff", "ForwardDiff", "Graphs", "LinearAlgebra", "PackageExtensionCompat", "Random", "Reexport", "SciMLOperators", "Setfield", "SparseArrays", "StaticArrayInterface", "StaticArrays", "UnPack", "VertexSafeGraphs"] -git-tree-sha1 = "ccbf06a08573200853b1bd06203d8ccce8449578" -uuid = "47a9eef4-7e08-11e9-0b38-333d64bd3804" -version = "2.26.0" - - [deps.SparseDiffTools.extensions] - SparseDiffToolsEnzymeExt = "Enzyme" - SparseDiffToolsPolyesterExt = "Polyester" - SparseDiffToolsPolyesterForwardDiffExt = "PolyesterForwardDiff" - SparseDiffToolsSymbolicsExt = "Symbolics" - SparseDiffToolsZygoteExt = "Zygote" - - [deps.SparseDiffTools.weakdeps] - Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" - Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588" - PolyesterForwardDiff = "98d1487c-24ca-40b6-b7ab-df2af84e126b" - Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" - Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" - [[deps.SparseMatrixColorings]] -deps = ["ADTypes", "DocStringExtensions", "LinearAlgebra", "Random", "SparseArrays"] -git-tree-sha1 = "76e9564f0de0d1d7a46095e758ae13ceba680cfb" +deps = ["ADTypes", "DocStringExtensions", "LinearAlgebra", "PrecompileTools", "Random", "SparseArrays"] +git-tree-sha1 = "ab958b4fec46d1f1d057bb8e2a99bfdb90744646" uuid = "0a514795-09f3-496d-8182-132a7b665d35" -version = "0.4.19" +version = "0.4.20" [deps.SparseMatrixColorings.extensions] SparseMatrixColoringsCliqueTreesExt = "CliqueTrees" @@ -2251,9 +2207,9 @@ weakdeps = ["SparseArrays"] [[deps.StatsAPI]] deps = ["LinearAlgebra"] -git-tree-sha1 = "1ff449ad350c9c4cbc756624d6f8a8c3ef56d3ed" +git-tree-sha1 = "9d72a13a3f4dd3795a195ac5a44d7d6ff5f552ff" uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" -version = "1.7.0" +version = "1.7.1" [[deps.StatsBase]] deps = ["AliasTables", "DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] @@ -2311,9 +2267,9 @@ version = "1.0.1" [[deps.Tables]] deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "OrderedCollections", "TableTraits"] -git-tree-sha1 = "598cd7c1f68d1e205689b1c2fe65a9f85846f297" +git-tree-sha1 = "f2c1efbc8f3a609aadf318094f8fc5204bdaf344" uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.12.0" +version = "1.12.1" [[deps.Tar]] deps = ["ArgTools", "SHA"] @@ -2361,7 +2317,7 @@ uuid = "781d530d-4396-4725-bb49-402e4bee1e77" version = "1.4.0" [[deps.Tutorials]] -path = "/Users/ocots/Research/logiciels/dev/control-toolbox/Tutorials" +path = "/Users/yassinmoukan/Desktop/stage_co/Tutorials.jl" uuid = "cb10daa6-a5e5-4c25-a171-ae181b8ea3c9" version = "0.1.4" @@ -2392,32 +2348,28 @@ version = "0.4.1" [[deps.Unitful]] deps = ["Dates", "LinearAlgebra", "Random"] -git-tree-sha1 = "d62610ec45e4efeabf7032d67de2ffdea8344bed" +git-tree-sha1 = "02c1ac8104c9cf941395db79c611483909c04c7d" uuid = "1986cc42-f94f-5a68-af5c-568840ba703d" -version = "1.22.1" -weakdeps = ["ConstructionBase", "InverseFunctions"] +version = "1.23.0" +weakdeps = ["ConstructionBase", "ForwardDiff", "InverseFunctions", "Printf"] [deps.Unitful.extensions] ConstructionBaseUnitfulExt = "ConstructionBase" + ForwardDiffExt = "ForwardDiff" InverseFunctionsUnitfulExt = "InverseFunctions" + PrintfExt = "Printf" [[deps.UnitfulLatexify]] deps = ["LaTeXStrings", "Latexify", "Unitful"] -git-tree-sha1 = "975c354fcd5f7e1ddcc1f1a23e6e091d99e99bc8" +git-tree-sha1 = "af305cc62419f9bd61b6644d19170a4d258c7967" uuid = "45397f5d-5981-4c77-b2b3-fc36d6e9b728" -version = "1.6.4" +version = "1.7.0" [[deps.Unzip]] git-tree-sha1 = "ca0969166a028236229f63514992fc073799bb78" uuid = "41fe7b60-77ed-43a1-b4f0-825fd5a5650d" version = "0.2.0" -[[deps.VertexSafeGraphs]] -deps = ["Graphs"] -git-tree-sha1 = "8351f8d73d7e880bfc042a8b6922684ebeafb35c" -uuid = "19fa3120-7c27-5ec5-8db8-b0b0aa330d6f" -version = "0.2.0" - [[deps.Vulkan_Loader_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Wayland_jll", "Xorg_libX11_jll", "Xorg_libXrandr_jll", "xkbcommon_jll"] git-tree-sha1 = "2f0486047a07670caad3a81a075d2e518acc5c59" @@ -2425,10 +2377,10 @@ uuid = "a44049a8-05dd-5a78-86c9-5fde0876e88c" version = "1.3.243+0" [[deps.Wayland_jll]] -deps = ["Artifacts", "EpollShim_jll", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "85c7811eddec9e7f22615371c3cc81a504c508ee" +deps = ["Artifacts", "EpollShim_jll", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "XML2_jll"] +git-tree-sha1 = "49be0be57db8f863a902d59c0083d73281ecae8e" uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" -version = "1.21.0+2" +version = "1.23.1+0" [[deps.Wayland_protocols_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -2551,28 +2503,28 @@ uuid = "12413925-8142-5f55-bb0e-6d7ca50bb09b" version = "0.4.0+1" [[deps.Xorg_xcb_util_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll"] -git-tree-sha1 = "e7fd7b2881fa2eaa72717420894d3938177862d1" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll"] +git-tree-sha1 = "68da27247e7d8d8dafd1fcf0c3654ad6506f5f97" uuid = "2def613f-5ad1-5310-b15b-b15d46f528f5" -version = "0.4.0+1" +version = "0.4.1+0" [[deps.Xorg_xcb_util_keysyms_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] -git-tree-sha1 = "d1151e2c45a544f32441a567d1690e701ec89b00" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] +git-tree-sha1 = "44ec54b0e2acd408b0fb361e1e9244c60c9c3dd4" uuid = "975044d2-76e6-5fbe-bf08-97ce7c6574c7" -version = "0.4.0+1" +version = "0.4.1+0" [[deps.Xorg_xcb_util_renderutil_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] -git-tree-sha1 = "dfd7a8f38d4613b6a575253b3174dd991ca6183e" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] +git-tree-sha1 = "5b0263b6d080716a02544c55fdff2c8d7f9a16a0" uuid = "0d47668e-0667-5a69-a72c-f761630bfb7e" -version = "0.3.9+1" +version = "0.3.10+0" [[deps.Xorg_xcb_util_wm_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] -git-tree-sha1 = "e78d10aab01a4a154142c5006ed44fd9e8e31b67" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] +git-tree-sha1 = "f233c83cad1fa0e70b7771e0e21b061a116f2763" uuid = "c22f9ab0-d5fe-5066-847c-f4bb1cd4e361" -version = "0.4.1+1" +version = "0.4.2+0" [[deps.Xorg_xkbcomp_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxkbfile_jll"] @@ -2651,10 +2603,10 @@ uuid = "1183f4f0-6f2a-5f1a-908b-139f9cdfea6f" version = "0.2.2+0" [[deps.libevdev_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "141fe65dc3efabb0b1d5ba74e91f6ad26f84cc22" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "56d643b57b188d30cccc25e331d416d3d358e557" uuid = "2db6ffa8-e38f-5e21-84af-90c45d0032cc" -version = "1.11.0+0" +version = "1.13.4+0" [[deps.libfdk_aac_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -2681,10 +2633,10 @@ uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" version = "1.3.7+2" [[deps.mtdev_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "814e154bdb7be91d78b6802843f76b6ece642f11" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "b4d631fd51f2e9cdd93724ae25b2efc198b059b1" uuid = "009596ad-96f7-51b1-9f1b-5ce2d5e8a71e" -version = "1.1.6+0" +version = "1.1.7+0" [[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] diff --git a/docs/src/tutorial-free-times.md b/docs/src/tutorial-free-times.md index 1cb33d2..c20279a 100644 --- a/docs/src/tutorial-free-times.md +++ b/docs/src/tutorial-free-times.md @@ -1,12 +1,40 @@ -# [Free times](@id tutorial-free-times) -```@meta -Draft = false +# [Free Initial and Final Times](@id tutorial-free-times) + +In this tutorial, we explore optimal control problems with free initial time `t₀` and/or final time `t_f`. These problems require special treatment in both direct and indirect methods, particularly when handling time-varying constraints and objectives. + + +```@example free-time +using OptimalControl +using NLPModelsIpopt +using BenchmarkTools +using DataFrames +using Plots + + +function double_integrator_mintf() + @def ocp begin + tf ∈ R, variable + t ∈ [0, tf], time + x ∈ R², state + u ∈ R, control + -1 ≤ u(t) ≤ 1 + x(0) == [0, 0] + x(tf) == [1, 0] + 0.05 ≤ tf ≤ Inf + ẋ(t) == [x₂(t), u(t)] + tf → min + end + return ocp +end +nothing # hide ``` -Blabla -test commit push +## Pour la résolution directe on obtient . + + +```@example main-disc +sol = solve(ocp; disc_method=:trapeze, display=false) +plot(sol; size=(800, 800)) +``` -```@example main-freetimes -x=1 -``` \ No newline at end of file From 025016103ea2c2f67b98253811d4ec099ee0bb59 Mon Sep 17 00:00:00 2001 From: Youri Date: Wed, 11 Jun 2025 16:24:23 +0200 Subject: [PATCH 06/20] First example + theorical part detailed --- docs/src/assets/Manifest.toml | 6 +- docs/src/tutorial-continuation.md | 3 +- docs/src/tutorial-free-times.md | 144 ++++++++++++++++++++++++++++-- 3 files changed, 143 insertions(+), 10 deletions(-) diff --git a/docs/src/assets/Manifest.toml b/docs/src/assets/Manifest.toml index a5c6501..e323262 100644 --- a/docs/src/assets/Manifest.toml +++ b/docs/src/assets/Manifest.toml @@ -1,6 +1,6 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.11.2" +julia_version = "1.11.5" manifest_format = "2.0" project_hash = "e7183c2ad8bb7ad385c3187377b285a34e621395" @@ -1476,7 +1476,7 @@ version = "0.3.27+1" [[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+2" +version = "0.8.5+0" [[deps.OpenSSH_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "OpenSSL_jll", "Zlib_jll"] @@ -2317,7 +2317,7 @@ uuid = "781d530d-4396-4725-bb49-402e4bee1e77" version = "1.4.0" [[deps.Tutorials]] -path = "/Users/yassinmoukan/Desktop/stage_co/Tutorials.jl" +path = "C:\\Users\\youri\\Desktop\\ENSEEIHT\\Stage\\Tutorials.jl" uuid = "cb10daa6-a5e5-4c25-a171-ae181b8ea3c9" version = "0.1.4" diff --git a/docs/src/tutorial-continuation.md b/docs/src/tutorial-continuation.md index eefd682..5286ba6 100644 --- a/docs/src/tutorial-continuation.md +++ b/docs/src/tutorial-continuation.md @@ -1,3 +1,4 @@ + # Discrete continuation By using the warm start option, it is easy to implement a basic discrete continuation method, in which a sequence of problems is solved by using each solution as the initial guess for the next problem. @@ -53,7 +54,7 @@ Then we perform the continuation with a simple *for* loop, using each solution t init = () data = DataFrame(T=Float64[], Objective=Float64[], Iterations=Int[]) for T ∈ range(1, 2, length=5) - ocp = problem(T) + ocp = problem(T) sol = solve(ocp; init=init, display=false) global init = sol push!(data, (T=T, Objective=objective(sol), Iterations=iterations(sol))) diff --git a/docs/src/tutorial-free-times.md b/docs/src/tutorial-free-times.md index c20279a..f1484ac 100644 --- a/docs/src/tutorial-free-times.md +++ b/docs/src/tutorial-free-times.md @@ -1,16 +1,19 @@ +```@meta +Draft = false +``` # [Free Initial and Final Times](@id tutorial-free-times) -In this tutorial, we explore optimal control problems with free initial time `t₀` and/or final time `t_f`. These problems require special treatment in both direct and indirect methods, particularly when handling time-varying constraints and objectives. +In this tutorial, we explore optimal control problems with free initial time `t₀` and/or final time `t_f`. These problems require special treatment in both direct and indirect methods, particularly when handling time-varying constraints and objectives. -```@example free-time +```@example main-disc using OptimalControl using NLPModelsIpopt using BenchmarkTools using DataFrames using Plots - +using Printf function double_integrator_mintf() @def ocp begin @@ -30,11 +33,140 @@ end nothing # hide ``` -## Pour la résolution directe on obtient . +To allow the use of a free final time, you define t0 and/or tf as variables, and then set the global time t between these two variables. Here is a complete example : +```julia +v=(t0, tf) ∈ R², variable +t ∈ [t0, tf], time +``` + +You may need to add this type of constrain in your problem definition : + +```julia +0.05 ≤ tf ≤ Inf +``` + +It is to ensure and help the convergence of the algorithm depending on your problem and you can also need it with t0. + +## Direct resolution with free final time : + +We now solve the problem using a direct method, with automatic treatment of the free final time. ```@example main-disc -sol = solve(ocp; disc_method=:trapeze, display=false) -plot(sol; size=(800, 800)) +ocp = double_integrator_mintf() +sol = solve(ocp; grid_size=100) +plot(sol; label="direct", size=(800, 800)) +``` + +## Verification of results + +Here the theorical part : +```math +H = p1*x1 + p2*u +``` +Conditions of Pontryagin's theorem : +```math +p1' = 0, \quad p2' = -p1 \\ +p1 = p1(0) = constante = 1\\ +p2 = -p1*t + p2(0) = 1-t\\ +``` +Transversal conditions : +```math +H[tf] = -p° = 1 +``` +We find u : +```math +u(t) = 1 ∈ [0,t1] \\ +u(t) = -1 ∈ [t1, tf] +``` +Where t1 is a constant to determined. + +Now we have to integrate x' : + +On t ∈ [0,t1] : +```math +x1' = x2 \\ +x2'= u = 1 \\ +``` + +```math +x2(t) = t \\ +x1(t) = (1/2)*t^2 +``` + +When t = t1 : +```math +x2(t1) = t1 \\ +x1(t1) = (1/2)*t1^2 +``` + +On t ∈ [t1,tf] +```math +x2(t) = -t + 2*t1 \\ +x1(t) = -(1/2)*t^2 + 2*t1*t + C2 \\ ``` +```math +x1(t1) = -(1/2)*t1^2 + 2*t1^2 + C2 = (1/2)*t1^2 \\ +C2 = -t1^2 \\ +``` + +```math +x1(t) = -(1/2)*t^2 + 2*t1*t - t1^2 +``` + +Finally you can solve the terminal conditions : +```math +x1(tf) = 1, \quad x2(tf) = 0 \\ +``` + +```math +x2(tf) = -tf + 2*t1 = 0 \\ +tf = 2*t1 \\ +``` + +```math +x1(tf) = -(1/2)*tf^2 + 2*t1*tf - t1^2 = 1 \\ +-2*t1^2 + 4*t1 - t1^2 = t1 = 1 \\ +``` + +```math +t1 = 1, \quad t2 = 2 +``` + +To sum up we find the following solutions : +```math +x1(t) = \begin{cases} +(1/2)*t^2 & \text{si } t \in [0, 1) \\ +-(1/2)*t^2 + 2*t - 1 & \text{si } t \in [1, 2] +\end{cases} +\qquad +x2(t) = \begin{cases} +t & \text{si } t \in [0, 1) \\ +2 - t & \text{si } t \in [1, 2] +\end{cases} +``` + +```math +p1(t) = 1, \quad p2(t) = 1-t, \quad p°=-1 +``` + +```math +u(t) = \begin{cases} +1 & \text{si } t \in [0, 1) \\ +-1 & \text{si } t \in [1, 2] +\end{cases} +``` + +We can analyse the influence of using different discretization sizes (grid_size), and observed the following results for the optimal tf: + +```@example main-disc +for N in [20, 50, 100, 200] + solN = solve(ocp; grid_size=N, display=false) + @printf("grid_size = %3d → tf = %.5f\n", N, objective(solN)) +end +``` + +This example shows that problems with a free final time can be sensitive to discretization. A small grid may lead to suboptimal or slightly inaccurate results. + +## From free final time to fixed final time : From 68470e31c268696b92f04ba37a549f8a264ed288 Mon Sep 17 00:00:00 2001 From: yassin moukan Date: Thu, 12 Jun 2025 14:08:58 +0200 Subject: [PATCH 07/20] =?UTF-8?q?free=20initial=20time=20exemple=20sans=20?= =?UTF-8?q?v=C3=A9rification?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/Project.toml | 1 + docs/src/assets/Manifest.toml | 6 ++-- docs/src/tutorial-free-times.md | 49 ++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/docs/Project.toml b/docs/Project.toml index 8fe9d0e..0cc8353 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -15,6 +15,7 @@ OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" +Tutorials = "cb10daa6-a5e5-4c25-a171-ae181b8ea3c9" [compat] BenchmarkTools = "1.6" diff --git a/docs/src/assets/Manifest.toml b/docs/src/assets/Manifest.toml index e323262..a5c6501 100644 --- a/docs/src/assets/Manifest.toml +++ b/docs/src/assets/Manifest.toml @@ -1,6 +1,6 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.11.5" +julia_version = "1.11.2" manifest_format = "2.0" project_hash = "e7183c2ad8bb7ad385c3187377b285a34e621395" @@ -1476,7 +1476,7 @@ version = "0.3.27+1" [[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.5+0" +version = "0.8.1+2" [[deps.OpenSSH_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "OpenSSL_jll", "Zlib_jll"] @@ -2317,7 +2317,7 @@ uuid = "781d530d-4396-4725-bb49-402e4bee1e77" version = "1.4.0" [[deps.Tutorials]] -path = "C:\\Users\\youri\\Desktop\\ENSEEIHT\\Stage\\Tutorials.jl" +path = "/Users/yassinmoukan/Desktop/stage_co/Tutorials.jl" uuid = "cb10daa6-a5e5-4c25-a171-ae181b8ea3c9" version = "0.1.4" diff --git a/docs/src/tutorial-free-times.md b/docs/src/tutorial-free-times.md index f1484ac..f9531cf 100644 --- a/docs/src/tutorial-free-times.md +++ b/docs/src/tutorial-free-times.md @@ -46,7 +46,7 @@ You may need to add this type of constrain in your problem definition : 0.05 ≤ tf ≤ Inf ``` -It is to ensure and help the convergence of the algorithm depending on your problem and you can also need it with t0. +It is to ensure and help the convergence of the algorithm depending on your problem and it can also be needed when dealing with problems having a free initial time, as we will see in the next example. ## Direct resolution with free final time : @@ -169,4 +169,51 @@ end This example shows that problems with a free final time can be sensitive to discretization. A small grid may lead to suboptimal or slightly inaccurate results. + +# Now we will try an example with a free initial time + +```@example initial_time +using OptimalControl +using NLPModelsIpopt +using BenchmarkTools +using DataFrames +using Plots +using Printf + +function double_integrator_mint0() + @def ocp begin + t0 ∈ R, variable + tf=0 + t ∈ [t0, tf], time + x ∈ R², state + u ∈ R, control + -1 ≤ u(t) ≤ 1 + x(t0) == [0, 0] + x(tf) == [1, 0] + 0.05 ≤ -t0 ≤ Inf + ẋ(t) == [x₂(t), u(t)] + -t0 → min + end + return ocp +end +nothing # hide +``` + +# Direct resolution with free initial time : + +We now solve the problem using a direct method, with automatic treatment of the free initial time. + +```@example initial_time +ocp = double_integrator_mint0() +sol = solve(ocp; grid_size=100) +plot(sol; label="direct", size=(800, 800)) +``` + + + + + + + + ## From free final time to fixed final time : From 321edcfb7af4278ebfc9659db3af1bc3645d3265 Mon Sep 17 00:00:00 2001 From: yassin moukan Date: Thu, 12 Jun 2025 15:10:21 +0200 Subject: [PATCH 08/20] tutorial-free-times.md exemple free initian time with verification --- docs/src/tutorial-free-times.md | 65 +++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/docs/src/tutorial-free-times.md b/docs/src/tutorial-free-times.md index f9531cf..4fde2de 100644 --- a/docs/src/tutorial-free-times.md +++ b/docs/src/tutorial-free-times.md @@ -211,9 +211,74 @@ plot(sol; label="direct", size=(800, 800)) +## Verification of results +Here is the theoretical part using Pontryagin's Maximum Principle: +```math +H = p_1 x_2 + p_2 u + 1 +``` +Conditions from Pontryagin’s theorem: +```math +p_1' = 0 \quad \Rightarrow \quad p_1 = c_1 \quad (\text{constant}) \\ +p_2' = -p_1 \quad \Rightarrow \quad p_2 = -c_1 t + c_2 +``` +Switching condition: +```math +p_2(t_s) = 0 \quad \Rightarrow \quad c_2 = c_1 t_s +``` + +Optimal control: +```math +u(t) = 1 \quad \text{on} \quad [t_0, t_s] \\ +u(t) = -1 \quad \text{on} \quad [t_s, 0] +``` + +Now we integrate the system: + +On \( t \in [t_0, t_s] \) : +```math +x_2' = u = 1 \quad \Rightarrow \quad x_2(t) = t - t_0 \\ +x_1' = x_2 \quad \Rightarrow \quad x_1(t) = \frac{(t - t_0)^2}{2} +``` + +At switching time \( t = t_s \) : +```math +x_2(t_s) = t_s - t_0 \\ +x_1(t_s) = \frac{(t_s - t_0)^2}{2} +``` + +On \( t \in [t_s, 0] \) : +```math +x_2' = u = -1 \quad \Rightarrow \quad x_2(t) = x_2(t_s) - (t - t_s) \\ +x_1' = x_2 \quad \Rightarrow \quad x_1(t) = x_1(t_s) + \int_{t_s}^t x_2(s) ds +``` + +Final velocity condition: +```math +x_2(0) = 0 \quad \Rightarrow \quad t_s - t_0 + t_s = 0 \quad \Rightarrow \quad t_0 = 2 t_s +``` + +Final position: +```math +x_1(0) = x_1(t_s) + \frac{t_s^2}{2} \quad \Rightarrow \quad x_1(0) = t_s^2 = 1 \quad \Rightarrow \quad t_s = -1 +``` + +We deduce: +```math +t_0 = 2 * t_s = -2 +``` + +### Final solution: +- Switching time: \( t_s = -1 \) +- Initial time: \( t_0 = -2 \) + +Control: +```math +u(t) = 1 \quad \text{on} \quad [-2, -1] \\ +u(t) = -1 \quad \text{on} \quad [-1, 0] +``` ## From free final time to fixed final time : From c508174d6075528500ac41ae0629708489427a6b Mon Sep 17 00:00:00 2001 From: Youri Date: Thu, 12 Jun 2025 15:36:53 +0200 Subject: [PATCH 09/20] Third example with free t0 and tf --- docs/src/assets/Manifest.toml | 6 +-- docs/src/tutorial-free-times.md | 86 +++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/docs/src/assets/Manifest.toml b/docs/src/assets/Manifest.toml index a5c6501..e323262 100644 --- a/docs/src/assets/Manifest.toml +++ b/docs/src/assets/Manifest.toml @@ -1,6 +1,6 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.11.2" +julia_version = "1.11.5" manifest_format = "2.0" project_hash = "e7183c2ad8bb7ad385c3187377b285a34e621395" @@ -1476,7 +1476,7 @@ version = "0.3.27+1" [[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+2" +version = "0.8.5+0" [[deps.OpenSSH_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "OpenSSL_jll", "Zlib_jll"] @@ -2317,7 +2317,7 @@ uuid = "781d530d-4396-4725-bb49-402e4bee1e77" version = "1.4.0" [[deps.Tutorials]] -path = "/Users/yassinmoukan/Desktop/stage_co/Tutorials.jl" +path = "C:\\Users\\youri\\Desktop\\ENSEEIHT\\Stage\\Tutorials.jl" uuid = "cb10daa6-a5e5-4c25-a171-ae181b8ea3c9" version = "0.1.4" diff --git a/docs/src/tutorial-free-times.md b/docs/src/tutorial-free-times.md index 4fde2de..30aa774 100644 --- a/docs/src/tutorial-free-times.md +++ b/docs/src/tutorial-free-times.md @@ -158,6 +158,20 @@ u(t) = \begin{cases} \end{cases} ``` +Now we can compare the results found with the direct method whith the theoritical analysis : +```@example main-disc +tf = variable(sol) +u = control(sol) +p = costate(sol) +x = state(sol) +p° = -1 + +xf = x(tf) +pf = p(tf) + +Htf = pf[1]*xf[1] + pf[2]*u(tf) +``` + We can analyse the influence of using different discretization sizes (grid_size), and observed the following results for the optimal tf: ```@example main-disc @@ -280,5 +294,77 @@ u(t) = 1 \quad \text{on} \quad [-2, -1] \\ u(t) = -1 \quad \text{on} \quad [-1, 0] ``` +## An example with free final and free initial time : +```@example both_time +using OptimalControl +using NLPModelsIpopt +using BenchmarkTools +using DataFrames +using Plots +using Printf + +function double_integrator_freet0tf() + @def ocp begin + v=(t0, tf) ∈ R², variable + t ∈ [t0, tf], time + x ∈ R², state + u ∈ R, control + -1 ≤ u(t) ≤ 1 + x(t0) == [0, 0] + x(tf) == [1, 0] + 0.05 ≤ t0 ≤ 10 + 0.05 ≤ tf ≤ 10 + 0.01 ≤ tf - t0 ≤ Inf + ẋ(t) == [x₂(t), u(t)] + t0 → max + end + + return ocp +end +nothing # hide +``` + +# Direct resolution with both free times : + +We now solve the problem using a direct method, with automatic treatment of the free initial time. + +```@example both_time +ocp = double_integrator_freet0tf() +sol = solve(ocp; grid_size=100) +plot(sol; label="direct", size=(800, 800)) +``` + + +## A more concrete example about the change of orbit of a satellite : + +```@example orbit +using OptimalControl +using NLPModelsIpopt +using BenchmarkTools +using DataFrames +using Plots +using Printf + +function double_integrator_freet0tf() + @def ocp begin + v=(t0, tf) ∈ R², variable + t ∈ [t0, tf], time + x ∈ R², state + u ∈ R, control + -1 ≤ u(t) ≤ 1 + x(t0) == [0, 0] + x(tf) == [1, 0] + 0.05 ≤ t0 ≤ 10 + 0.05 ≤ tf ≤ 10 + 0.01 ≤ tf - t0 ≤ Inf + ẋ(t) == [x₂(t), u(t)] + t0 → max + end + + return ocp +end +nothing # hide +``` + ## From free final time to fixed final time : From 24105aaf957f45292426d072987ca3201009253a Mon Sep 17 00:00:00 2001 From: Youri Date: Thu, 12 Jun 2025 16:34:20 +0200 Subject: [PATCH 10/20] result analysis + concrete example with minimal orbit time --- docs/src/tutorial-free-times.md | 51 ++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/docs/src/tutorial-free-times.md b/docs/src/tutorial-free-times.md index 30aa774..59f4456 100644 --- a/docs/src/tutorial-free-times.md +++ b/docs/src/tutorial-free-times.md @@ -62,7 +62,7 @@ plot(sol; label="direct", size=(800, 800)) Here the theorical part : ```math -H = p1*x1 + p2*u +H = p1*x2 + p2*u ``` Conditions of Pontryagin's theorem : ```math @@ -169,9 +169,16 @@ p° = -1 xf = x(tf) pf = p(tf) -Htf = pf[1]*xf[1] + pf[2]*u(tf) +Htf = pf[1]*xf[2] + pf[2]*u(tf) +@printf("H(tf) = %.3f\n", Htf) +@printf("x(tf) = [%.3f, %.3f]\n", xf[1], xf[2]) +@printf("p(tf) = [%.3f, %.3f]\n", pf[1], pf[2]) ``` +The numerical results closely match the theoretical predictions: the final state x(tf)=[1,0] is exactly satisfied. +The costate and Hamiltonian values at final time show a small deviation (≈ 0.01), likely due to numerical precision. +Overall, the direct method confirms the theoretical analysis with excellent accuracy. + We can analyse the influence of using different discretization sizes (grid_size), and observed the following results for the optimal tf: ```@example main-disc @@ -345,20 +352,33 @@ using DataFrames using Plots using Printf -function double_integrator_freet0tf() +const x₁₀ = -42272.67 # initial position x +const x₂₀ = 0 # initial position y +const x₃₀ = 0 # initial velocity in x +const x₄₀ = -5696.72 # initial velocity in y +const μ = 5.1658620912*1e12 # gravitational parameter +const γ_max = 0.05 # maximal thrust norm +const r_f = 1.0 # target orbit radius (final distance to origin) + +function min_orbit_tf() @def ocp begin - v=(t0, tf) ∈ R², variable - t ∈ [t0, tf], time - x ∈ R², state - u ∈ R, control - -1 ≤ u(t) ≤ 1 - x(t0) == [0, 0] - x(tf) == [1, 0] - 0.05 ≤ t0 ≤ 10 - 0.05 ≤ tf ≤ 10 - 0.01 ≤ tf - t0 ≤ Inf - ẋ(t) == [x₂(t), u(t)] - t0 → max + tf ∈ R, variable + t ∈ [0, tf], time + x ∈ R^4, state + u ∈ R^2, control + norm(u(t)) ≤ γ_max + x(0) == [x₁₀, x₂₀, x₃₀, x₄₀] + x(tf)[1]^2 + x(tf)[2]^2 == r_f^2 + x(tf)[3] == -√(μ / r_f^3) * x(tf)[2] + x(tf)[4] == √(μ / r_f^3) * x(tf)[1] + 0.05 ≤ tf ≤ Inf + ẋ(t) == [ + x[3](t), + x[4](t), + -μ * x[1](t) / ( (x[1](t)^2 + x[2](t)^2)^(3/2) ) + u[1](t), + -μ * x[2](t) / ( (x[1](t)^2 + x[2](t)^2)^(3/2) ) + u[2](t) + ] + tf → min end return ocp @@ -366,5 +386,4 @@ end nothing # hide ``` - ## From free final time to fixed final time : From 5aa5817f006225e6a17b23855a243e91d8aba5f2 Mon Sep 17 00:00:00 2001 From: Youri Date: Mon, 16 Jun 2025 11:23:17 +0200 Subject: [PATCH 11/20] indirect resolution on first example --- docs/src/tutorial-free-times.md | 141 +++++++++++++++++++++++++++++--- 1 file changed, 131 insertions(+), 10 deletions(-) diff --git a/docs/src/tutorial-free-times.md b/docs/src/tutorial-free-times.md index 59f4456..45ee1b5 100644 --- a/docs/src/tutorial-free-times.md +++ b/docs/src/tutorial-free-times.md @@ -360,23 +360,22 @@ const μ = 5.1658620912*1e12 # gravitational parameter const γ_max = 0.05 # maximal thrust norm const r_f = 1.0 # target orbit radius (final distance to origin) + function min_orbit_tf() @def ocp begin tf ∈ R, variable t ∈ [0, tf], time - x ∈ R^4, state - u ∈ R^2, control - norm(u(t)) ≤ γ_max + x ∈ R⁴, state + u ∈ R², control + u₁(t)^2 + u₂(t)^2 ≤ γ_max^2 x(0) == [x₁₀, x₂₀, x₃₀, x₄₀] - x(tf)[1]^2 + x(tf)[2]^2 == r_f^2 - x(tf)[3] == -√(μ / r_f^3) * x(tf)[2] - x(tf)[4] == √(μ / r_f^3) * x(tf)[1] + x₁(tf)^2 + x₂(tf)^2 == r_f^2 0.05 ≤ tf ≤ Inf ẋ(t) == [ - x[3](t), - x[4](t), - -μ * x[1](t) / ( (x[1](t)^2 + x[2](t)^2)^(3/2) ) + u[1](t), - -μ * x[2](t) / ( (x[1](t)^2 + x[2](t)^2)^(3/2) ) + u[2](t) + x₃(t), + x₄(t), + -μ * x₁(t) / ((x₁(t)^2 + x₂(t)^2)^(3/2)) + u₁(t), + -μ * x₂(t) / ((x₁(t)^2 + x₂(t)^2)^(3/2)) + u₂(t) ] tf → min end @@ -386,4 +385,126 @@ end nothing # hide ``` +# Direct resolution with minimal orbital time : + +We now solve the problem using a direct method, with automatic treatment of the free initial time. + +@example orbit +ocp = min_orbit_tf() +sol = solve(ocp; grid_size=100) +plot(sol; label="direct", size=(800, 800)) + +## Indirect method + +We keep the structure of the solution found with the direct method +```@example main-disc +t = time_grid(sol) +x = state(sol) +u = control(sol) +p = costate(sol) + +H(x,p,u,t) = p[1](t)*x[2](t) + p[2](t)*u(u) +H(xf, pf, tf) = pf[1]*xf[2] + pf[2]*u(tf) + +# Hamiltonian vector field +F1(x) = [0, 1] +H1 = Lift(F1) +φ(t) = H1(x(t), p(t)) # switching function +``` + + +First, lets define the different flows +```@example main-disc +using OrdinaryDiffEq # to get the Flow function from OptimalControl + +const u1 = 1 +const u0 = -1 + +f1 = Flow(ocp, (x, p, tf) -> u1) +f0 = Flow(ocp, (x, p, tf) -> u0) +``` + +And with this we have the following shooting function +```@example main-disc +x0 = [0.0, 0.0] # état initial +xf_target = [1.0, 0.0] # état final + +function shoot!(s, p0, t1, tf) +x1, p1 = f1(0.0, x0, p0, t1) +xf, pf = f0(t1, x1, p1, tf) + +# Conditions de tir +s[1:2] = xf .- xf_target # état final +s[3] = H(xf, pf, tf) - 1 # condition de transversalité H(tf) = 1 +s[4] = H1(x1, p1) # φ = 0 au switch +end +``` + +Before solving our problem we must find a good initial guess to help the convergence of the algorithm +```@example main-disc +p0 = p(0.0) +φ_arr = abs.(φ.(t)) +t1 = t[argmin(φ_arr)] +tf = t[end] + +println("p0 ≈ ", p0) +println("t1 ≈ ", t1) +println("tf ≈ ", tf) + +s = zeros(4) +shoot!(s, p0, t1, tf) + +ξ = [p0..., t1, tf] +``` + +And we finally can solve the problem using an indirect method +```@example main-disc +using NonlinearSolve +using DifferentiationInterface +import ForwardDiff + +backend = AutoForwardDiff() + +struct MYSOL + x::Vector{Float64} +end + +function fsolve(f, j, x; kwargs...) + try + MINPACK.fsolve(f, j, x; kwargs...) + catch e + println("MINPACK error:") + println(e) + println("→ Using NonlinearSolve fallback") + + # Wrap pour respecter l'interface de NonlinearProblem + function wrapped_f(s, ξ, p) + f(s, ξ) + return nothing + end + + prob = NonlinearProblem(wrapped_f, x) + sol = solve(prob; abstol=1e-8, reltol=1e-8) + return MYSOL(sol.u) + end +end + +# Agrégation du problème +nle! = (s, ξ) -> shoot!(s, ξ[1:2], ξ[3], ξ[4]) +jnle! = (js, ξ) -> jacobian!(nle!, similar(ξ), js, backend, ξ) + +ξ0 = [p0... , t1, tf] +indirect_sol = fsolve(nle!, jnle!, ξ0) + +p0 = indirect_sol.x[1:2] +t1 = indirect_sol.x[3] +tf = indirect_sol.x[4] + +f = f1 * (t1, f0) +flow_sol = f((0.0, tf), x0, p0) + +plot!(flow_sol, label="indirect", color=:red) +``` + ## From free final time to fixed final time : + From 1e25fb89118488bac789a8c68f332f88ac028e69 Mon Sep 17 00:00:00 2001 From: yassin moukan Date: Mon, 16 Jun 2025 15:54:00 +0200 Subject: [PATCH 12/20] indirect method for first example + roll-up menu for verification + direct method for orbit example --- docs/src/assets/Manifest.toml | 6 +- docs/src/tutorial-free-times.md | 248 ++++++++++++++++---------------- 2 files changed, 131 insertions(+), 123 deletions(-) diff --git a/docs/src/assets/Manifest.toml b/docs/src/assets/Manifest.toml index e323262..a5c6501 100644 --- a/docs/src/assets/Manifest.toml +++ b/docs/src/assets/Manifest.toml @@ -1,6 +1,6 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.11.5" +julia_version = "1.11.2" manifest_format = "2.0" project_hash = "e7183c2ad8bb7ad385c3187377b285a34e621395" @@ -1476,7 +1476,7 @@ version = "0.3.27+1" [[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.5+0" +version = "0.8.1+2" [[deps.OpenSSH_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "OpenSSL_jll", "Zlib_jll"] @@ -2317,7 +2317,7 @@ uuid = "781d530d-4396-4725-bb49-402e4bee1e77" version = "1.4.0" [[deps.Tutorials]] -path = "C:\\Users\\youri\\Desktop\\ENSEEIHT\\Stage\\Tutorials.jl" +path = "/Users/yassinmoukan/Desktop/stage_co/Tutorials.jl" uuid = "cb10daa6-a5e5-4c25-a171-ae181b8ea3c9" version = "0.1.4" diff --git a/docs/src/tutorial-free-times.md b/docs/src/tutorial-free-times.md index 45ee1b5..ebd7abf 100644 --- a/docs/src/tutorial-free-times.md +++ b/docs/src/tutorial-free-times.md @@ -58,7 +58,10 @@ sol = solve(ocp; grid_size=100) plot(sol; label="direct", size=(800, 800)) ``` -## Verification of results +# Verification of results +```@raw html +
Verification of results. +``` Here the theorical part : ```math @@ -157,6 +160,10 @@ u(t) = \begin{cases} -1 & \text{si } t \in [1, 2] \end{cases} ``` +```@raw html +
+``` + Now we can compare the results found with the direct method whith the theoritical analysis : ```@example main-disc @@ -190,6 +197,117 @@ end This example shows that problems with a free final time can be sensitive to discretization. A small grid may lead to suboptimal or slightly inaccurate results. +## Indirect method + +We keep the structure of the solution found with the direct method +```@example main-disc +t = time_grid(sol) +x = state(sol) +u = control(sol) +p = costate(sol) + +H(x,p,u,t) = p[1](t)*x[2](t) + p[2](t)*u(u) +H(xf, pf, tf) = pf[1]*xf[2] + pf[2]*u(tf) + +# Hamiltonian vector field +F1(x) = [0, 1] +H1 = Lift(F1) +φ(t) = H1(x(t), p(t)) # switching function +``` + + +First, lets define the different flows +```@example main-disc +using OrdinaryDiffEq # to get the Flow function from OptimalControl + +const u1 = 1 +const u0 = -1 + +f1 = Flow(ocp, (x, p, tf) -> u1) +f0 = Flow(ocp, (x, p, tf) -> u0) +``` + +And with this we have the following shooting function +```@example main-disc +x0 = [0.0, 0.0] # état initial +xf_target = [1.0, 0.0] # état final + +function shoot!(s, p0, t1, tf) +x1, p1 = f1(0.0, x0, p0, t1) +xf, pf = f0(t1, x1, p1, tf) + +# Conditions de tir +s[1:2] = xf .- xf_target # état final +s[3] = H(xf, pf, tf) - 1 # condition de transversalité H(tf) = 1 +s[4] = H1(x1, p1) # φ = 0 au switch +end +``` + +Before solving our problem we must find a good initial guess to help the convergence of the algorithm +```@example main-disc +p0 = p(0.0) +φ_arr = abs.(φ.(t)) +t1 = t[argmin(φ_arr)] +tf = t[end] + +println("p0 ≈ ", p0) +println("t1 ≈ ", t1) +println("tf ≈ ", tf) + +s = zeros(4) +shoot!(s, p0, t1, tf) + +ξ = [p0..., t1, tf] +``` + +And we finally can solve the problem using an indirect method +```@example main-disc +using NonlinearSolve +using DifferentiationInterface +import ForwardDiff + +backend = AutoForwardDiff() + +struct MYSOL + x::Vector{Float64} +end + +function fsolve(f, j, x; kwargs...) + try + MINPACK.fsolve(f, j, x; kwargs...) + catch e + println("MINPACK error:") + println(e) + println("→ Using NonlinearSolve fallback") + + # Wrap pour respecter l'interface de NonlinearProblem + function wrapped_f(s, ξ, p) + f(s, ξ) + return nothing + end + + prob = NonlinearProblem(wrapped_f, x) + sol = solve(prob; abstol=1e-8, reltol=1e-8) + return MYSOL(sol.u) + end +end + +# Agrégation du problème +nle! = (s, ξ) -> shoot!(s, ξ[1:2], ξ[3], ξ[4]) +jnle! = (js, ξ) -> jacobian!(nle!, similar(ξ), js, backend, ξ) + +ξ0 = [p0... , t1, tf] +indirect_sol = fsolve(nle!, jnle!, ξ0) + +p0 = indirect_sol.x[1:2] +t1 = indirect_sol.x[3] +tf = indirect_sol.x[4] + +f = f1 * (t1, f0) +flow_sol = f((0.0, tf), x0, p0) + +plot!(flow_sol, label="indirect", color=:red) +``` # Now we will try an example with a free initial time @@ -233,7 +351,9 @@ plot(sol; label="direct", size=(800, 800)) ## Verification of results - +```@raw html +
Verification of results. +``` Here is the theoretical part using Pontryagin's Maximum Principle: ```math H = p_1 x_2 + p_2 u + 1 @@ -300,7 +420,9 @@ Control: u(t) = 1 \quad \text{on} \quad [-2, -1] \\ u(t) = -1 \quad \text{on} \quad [-1, 0] ``` - +```@raw html +
+``` ## An example with free final and free initial time : ```@example both_time using OptimalControl @@ -358,7 +480,7 @@ const x₃₀ = 0 # initial velocity in x const x₄₀ = -5696.72 # initial velocity in y const μ = 5.1658620912*1e12 # gravitational parameter const γ_max = 0.05 # maximal thrust norm -const r_f = 1.0 # target orbit radius (final distance to origin) +const r_f = 42165 # target orbit radius (final distance to origin) function min_orbit_tf() @@ -389,122 +511,8 @@ nothing # hide We now solve the problem using a direct method, with automatic treatment of the free initial time. -@example orbit +```@example orbit ocp = min_orbit_tf() -sol = solve(ocp; grid_size=100) +sol = solve(ocp;init=(variable=13.4,), grid_size=100) plot(sol; label="direct", size=(800, 800)) - -## Indirect method - -We keep the structure of the solution found with the direct method -```@example main-disc -t = time_grid(sol) -x = state(sol) -u = control(sol) -p = costate(sol) - -H(x,p,u,t) = p[1](t)*x[2](t) + p[2](t)*u(u) -H(xf, pf, tf) = pf[1]*xf[2] + pf[2]*u(tf) - -# Hamiltonian vector field -F1(x) = [0, 1] -H1 = Lift(F1) -φ(t) = H1(x(t), p(t)) # switching function ``` - - -First, lets define the different flows -```@example main-disc -using OrdinaryDiffEq # to get the Flow function from OptimalControl - -const u1 = 1 -const u0 = -1 - -f1 = Flow(ocp, (x, p, tf) -> u1) -f0 = Flow(ocp, (x, p, tf) -> u0) -``` - -And with this we have the following shooting function -```@example main-disc -x0 = [0.0, 0.0] # état initial -xf_target = [1.0, 0.0] # état final - -function shoot!(s, p0, t1, tf) -x1, p1 = f1(0.0, x0, p0, t1) -xf, pf = f0(t1, x1, p1, tf) - -# Conditions de tir -s[1:2] = xf .- xf_target # état final -s[3] = H(xf, pf, tf) - 1 # condition de transversalité H(tf) = 1 -s[4] = H1(x1, p1) # φ = 0 au switch -end -``` - -Before solving our problem we must find a good initial guess to help the convergence of the algorithm -```@example main-disc -p0 = p(0.0) -φ_arr = abs.(φ.(t)) -t1 = t[argmin(φ_arr)] -tf = t[end] - -println("p0 ≈ ", p0) -println("t1 ≈ ", t1) -println("tf ≈ ", tf) - -s = zeros(4) -shoot!(s, p0, t1, tf) - -ξ = [p0..., t1, tf] -``` - -And we finally can solve the problem using an indirect method -```@example main-disc -using NonlinearSolve -using DifferentiationInterface -import ForwardDiff - -backend = AutoForwardDiff() - -struct MYSOL - x::Vector{Float64} -end - -function fsolve(f, j, x; kwargs...) - try - MINPACK.fsolve(f, j, x; kwargs...) - catch e - println("MINPACK error:") - println(e) - println("→ Using NonlinearSolve fallback") - - # Wrap pour respecter l'interface de NonlinearProblem - function wrapped_f(s, ξ, p) - f(s, ξ) - return nothing - end - - prob = NonlinearProblem(wrapped_f, x) - sol = solve(prob; abstol=1e-8, reltol=1e-8) - return MYSOL(sol.u) - end -end - -# Agrégation du problème -nle! = (s, ξ) -> shoot!(s, ξ[1:2], ξ[3], ξ[4]) -jnle! = (js, ξ) -> jacobian!(nle!, similar(ξ), js, backend, ξ) - -ξ0 = [p0... , t1, tf] -indirect_sol = fsolve(nle!, jnle!, ξ0) - -p0 = indirect_sol.x[1:2] -t1 = indirect_sol.x[3] -tf = indirect_sol.x[4] - -f = f1 * (t1, f0) -flow_sol = f((0.0, tf), x0, p0) - -plot!(flow_sol, label="indirect", color=:red) -``` - -## From free final time to fixed final time : - From bd0ad1e8fdfe4c59796ce29847d30a6a1f9dd90e Mon Sep 17 00:00:00 2001 From: yassin moukan Date: Fri, 20 Jun 2025 12:36:38 +0200 Subject: [PATCH 13/20] indirect method for orbital example --- docs/src/tutorial-free-times.md | 121 +++++++++++++++++++++++++++++++- 1 file changed, 120 insertions(+), 1 deletion(-) diff --git a/docs/src/tutorial-free-times.md b/docs/src/tutorial-free-times.md index ebd7abf..8454975 100644 --- a/docs/src/tutorial-free-times.md +++ b/docs/src/tutorial-free-times.md @@ -473,6 +473,13 @@ using BenchmarkTools using DataFrames using Plots using Printf +using OrdinaryDiffEq +using NonlinearSolve +using DifferentiationInterface +import ForwardDiff +using LinearAlgebra +using NLsolve + const x₁₀ = -42272.67 # initial position x const x₂₀ = 0 # initial position y @@ -481,7 +488,8 @@ const x₄₀ = -5696.72 # initial velocity in y const μ = 5.1658620912*1e12 # gravitational parameter const γ_max = 0.05 # maximal thrust norm const r_f = 42165 # target orbit radius (final distance to origin) - +const rf3 = r_f^3 +const α = sqrt(μ/rf3); function min_orbit_tf() @def ocp begin @@ -516,3 +524,114 @@ ocp = min_orbit_tf() sol = solve(ocp;init=(variable=13.4,), grid_size=100) plot(sol; label="direct", size=(800, 800)) ``` +# Indirect resolution with minimal orbital time : +We write down the pseudo hamiltonien of our problem +```@example orbit + +function H(x, p, u) + r3 = sqrt(x[1]^2 + x[2]^2)^3 + h = p[1]*x[3] + p[2]*x[4] + p[3]*((-μ*x[1]/r3) + u[1]) + p[4]*((-μ*x[2]/r3) + u[2]) + return h +end + +function Hv(x, p, u) + n = size(x, 1) + dx = zeros(eltype(x), n) + dp = zeros(eltype(x), n) + r = sqrt(x[1]^2 + x[2]^2) + dx[1],dx[2],dx[3],dx[4] = x[3], x[4], (-μ*x[1]/r^3) + u[1], (-μ*x[2]/r^3) + u[2] + dp[1],dp[2],dp[3],dp[4] = μ*((r^2 - 3*x[1]^2)*p[3]/r^5) -(3*p[4]*μ*x[2]*x[1]/r^5), μ*((r^2 - 3*x[2]^2)*p[4]/r^5) -(3*p[3]*μ*x[2]*x[1]/r^5), -p[1], -p[2] + return dx, dp +end + +function control(p) + u = zeros(eltype(p),2) + u[1] = (p[3]*γ_max)/sqrt(p[3]^2 + p[4]^2) + u[2] = (p[4]*γ_max)/sqrt(p[3]^2 + p[4]^2) + return u +end +``` +We calculate our hamitonien flow +```@raw html +
Flow function. +``` +```@example orbit + +function Flow(Hv) + function rhs!(dz, z, dummy, t) + n = size(z, 1)÷2 + dz[1:n], dz[n+1:2n] = Hv(z[1:n], z[n+1:2n]) + end + + # cas vectoriel + function f(tspan::Tuple{Real, Real}, x0::Vector{<:Real}, p0::Vector{<:Real}; abstol=1e-12, reltol=1e-12, saveat=0.01) + z0 = [ x0 ; p0 ] + ode = ODEProblem(rhs!, z0, tspan) + sol = solve(ode, Tsit5(), abstol=abstol, reltol=reltol, saveat=saveat) + return sol + end + + function f(t0::Real, x0::Vector{<:Real}, p0::Vector{<:Real}, tf::Real; abstol=1e-12, reltol=1e-12, saveat=[]) + sol = f((t0, tf), x0, p0, abstol=abstol, reltol=reltol, saveat=saveat) + n = size(x0, 1) + return sol(tf)[1:n], sol(tf)[n+1:2n] + end + + # cas scalaire + function rhs_scalar!(dz, z, dummy, t) + dz[1], dz[2] = Hv(z[1], z[2]) + end + + function f(tspan::Tuple{Real, Real}, x0::Real, p0::Real; abstol=1e-12, reltol=1e-12, saveat=0.01) + z0 = [ x0 ; p0 ] + ode = ODEProblem(rhs_scalar!, z0, tspan) + sol = solve(ode, Tsit5(), abstol=abstol, reltol=reltol, saveat=saveat) + return sol + end + + function f(t0::Real, x0::Real, p0::Real, tf::Real; abstol=1e-12, reltol=1e-12, saveat=[]) + sol = f((t0, tf), x0, p0, abstol=abstol, reltol=reltol, saveat=saveat) + return sol(tf)[1], sol(tf)[2] + end + + return f + +end; +``` +```@raw html +
+``` + +```@example orbit +f = Flow((x, p) -> Hv(x, p, control(p))); +``` +We calculate our shooting function + +```@example orbit +const x0 = [x₁₀, x₂₀, x₃₀, x₄₀] +function shoot(p0, tf) + s = zeros(eltype(p0), 5) + x, p = f(0, x0, p0, tf) + s[1] = sqrt(x[1]^2 + x[2]^2) - r_f + s[2] = x[3] + x[2]*α + s[3] = x[4] - x[1]*α + s[4] = x[2]*(p[1]+ α*p[4]) - x[1]*(p[2] - α*p[3]) + s[5] = H(x, p, control(p)) -1 + return s +end; +``` +Inital guess +```@example orbit +y_guess = [1.0323e-4, 4.915e-5, 3.568e-4, -1.554e-4, 13.4] +``` + +Jacobian of the shooting function +```@example orbit +foo(y) = shoot(y[1:4], y[5]) +jfoo(y) = ForwardDiff.jacobian(foo, y) +``` + +Solving shoot(p0, tf) = 0 +```@example orbit +nl_sol = nlsolve(foo, y_guess; xtol=1e-8, method=:trust_region, show_trace=true, autodiff=:finite) +``` \ No newline at end of file From 5d00a4745ee1ae5628f331bd1ad73cd7ec42a7a9 Mon Sep 17 00:00:00 2001 From: yassin moukan Date: Mon, 23 Jun 2025 16:21:48 +0200 Subject: [PATCH 14/20] indirect method for orbital time without visualisation --- docs/src/tutorial-free-times.md | 271 ++++++++++++++++++++++---------- 1 file changed, 188 insertions(+), 83 deletions(-) diff --git a/docs/src/tutorial-free-times.md b/docs/src/tutorial-free-times.md index 8454975..b0ba2db 100644 --- a/docs/src/tutorial-free-times.md +++ b/docs/src/tutorial-free-times.md @@ -525,113 +525,218 @@ sol = solve(ocp;init=(variable=13.4,), grid_size=100) plot(sol; label="direct", size=(800, 800)) ``` # Indirect resolution with minimal orbital time : -We write down the pseudo hamiltonien of our problem -```@example orbit -function H(x, p, u) - r3 = sqrt(x[1]^2 + x[2]^2)^3 - h = p[1]*x[3] + p[2]*x[4] + p[3]*((-μ*x[1]/r3) + u[1]) + p[4]*((-μ*x[2]/r3) + u[2]) - return h -end -function Hv(x, p, u) - n = size(x, 1) - dx = zeros(eltype(x), n) - dp = zeros(eltype(x), n) - r = sqrt(x[1]^2 + x[2]^2) - dx[1],dx[2],dx[3],dx[4] = x[3], x[4], (-μ*x[1]/r^3) + u[1], (-μ*x[2]/r^3) + u[2] - dp[1],dp[2],dp[3],dp[4] = μ*((r^2 - 3*x[1]^2)*p[3]/r^5) -(3*p[4]*μ*x[2]*x[1]/r^5), μ*((r^2 - 3*x[2]^2)*p[4]/r^5) -(3*p[3]*μ*x[2]*x[1]/r^5), -p[1], -p[2] - return dx, dp +## Hamiltonian and optimal control + +From the Pontryagin Maximum Principle, the optimal control is bang-bang: + +```@example orbit +ocp = min_orbit_tf() + +function optimal_control(p) + p₃, p₄ = p[3], p[4] + p_norm = sqrt(p₃^2 + p₄^2) + + if p_norm > 1e-10 + u₁ = -γ_max * p₃ / p_norm + u₂ = -γ_max * p₄ / p_norm + return [u₁, u₂] + else + return [0.0, 0.0] + end end -function control(p) - u = zeros(eltype(p),2) - u[1] = (p[3]*γ_max)/sqrt(p[3]^2 + p[4]^2) - u[2] = (p[4]*γ_max)/sqrt(p[3]^2 + p[4]^2) - return u +function hamiltonian(x, p, u) + x₁, x₂, x₃, x₄ = x + p₁, p₂, p₃, p₄ = p + u₁, u₂ = u + + r = sqrt(x₁^2 + x₂^2) + r³ = r^3 + + return (p₁*x₃ + p₂*x₄ + + p₃*(-μ*x₁/r³ + u₁) + + p₄*(-μ*x₂/r³ + u₂)) end ``` -We calculate our hamitonien flow -```@raw html -
Flow function. -``` -```@example orbit -function Flow(Hv) - function rhs!(dz, z, dummy, t) - n = size(z, 1)÷2 - dz[1:n], dz[n+1:2n] = Hv(z[1:n], z[n+1:2n]) - end +## Augmented dynamics + +We define the combined state-costate system: + +```@example orbit +function augmented_dynamics!(dx, x_aug, params, t) + x = x_aug[1:4] + p = x_aug[5:8] - # cas vectoriel - function f(tspan::Tuple{Real, Real}, x0::Vector{<:Real}, p0::Vector{<:Real}; abstol=1e-12, reltol=1e-12, saveat=0.01) - z0 = [ x0 ; p0 ] - ode = ODEProblem(rhs!, z0, tspan) - sol = solve(ode, Tsit5(), abstol=abstol, reltol=reltol, saveat=saveat) - return sol - end + x₁, x₂, x₃, x₄ = x + p₁, p₂, p₃, p₄ = p - function f(t0::Real, x0::Vector{<:Real}, p0::Vector{<:Real}, tf::Real; abstol=1e-12, reltol=1e-12, saveat=[]) - sol = f((t0, tf), x0, p0, abstol=abstol, reltol=reltol, saveat=saveat) - n = size(x0, 1) - return sol(tf)[1:n], sol(tf)[n+1:2n] - end + r = sqrt(x₁^2 + x₂^2) + r³ = r^3 + r⁵ = r^5 - # cas scalaire - function rhs_scalar!(dz, z, dummy, t) - dz[1], dz[2] = Hv(z[1], z[2]) - end + u = optimal_control(p) + u₁, u₂ = u + + # State dynamics + dx[1] = x₃ + dx[2] = x₄ + dx[3] = -μ*x₁/r³ + u₁ + dx[4] = -μ*x₂/r³ + u₂ + + # Costate dynamics + dx[5] = -p₃ * μ * (2*x₁^2 - x₂^2) / r⁵ + dx[6] = -p₄ * μ * (2*x₂^2 - x₁^2) / r⁵ + dx[7] = -p₁ + dx[8] = -p₂ +end - function f(tspan::Tuple{Real, Real}, x0::Real, p0::Real; abstol=1e-12, reltol=1e-12, saveat=0.01) - z0 = [ x0 ; p0 ] - ode = ODEProblem(rhs_scalar!, z0, tspan) - sol = solve(ode, Tsit5(), abstol=abstol, reltol=reltol, saveat=saveat) +function create_flow() + function flow_func(t_span, x0, p0) + x_aug0 = vcat(x0, p0) + prob = ODEProblem(augmented_dynamics!, x_aug0, t_span) + sol = solve(prob, Tsit5(), reltol=1e-12, abstol=1e-12) return sol end + return flow_func +end - function f(t0::Real, x0::Real, p0::Real, tf::Real; abstol=1e-12, reltol=1e-12, saveat=[]) - sol = f((t0, tf), x0, p0, abstol=abstol, reltol=reltol, saveat=saveat) - return sol(tf)[1], sol(tf)[2] - end +flow = create_flow() +``` - return f +## Shooting function -end; -``` -```@raw html -
-``` +The shooting function encodes the boundary conditions: ```@example orbit -f = Flow((x, p) -> Hv(x, p, control(p))); +function shooting_function!(s, ξ) + p0 = ξ[1:4] + tf = ξ[5] + + x0 = [x₁₀, x₂₀, x₃₀, x₄₀] + + try + sol = flow((0.0, tf), x0, p0) + + x_final = sol.u[end][1:4] + p_final = sol.u[end][5:8] + + x₁f, x₂f, x₃f, x₄f = x_final + p₁f, p₂f, p₃f, p₄f = p_final + + s[1] = x₁f^2 + x₂f^2 - r_f^2 + s[2] = p₁f + s[3] = p₂f + + u_final = optimal_control(p_final) + H_final = hamiltonian(x_final, p_final, u_final) + s[4] = H_final + + s[5] = p₁f^2 + p₂f^2 + p₃f^2 + p₄f^2 - 1.0 + + catch e + fill!(s, 1e6) + end + + return nothing +end ``` -We calculate our shooting function + +## Initial guess and solver setup ```@example orbit -const x0 = [x₁₀, x₂₀, x₃₀, x₄₀] -function shoot(p0, tf) - s = zeros(eltype(p0), 5) - x, p = f(0, x0, p0, tf) - s[1] = sqrt(x[1]^2 + x[2]^2) - r_f - s[2] = x[3] + x[2]*α - s[3] = x[4] - x[1]*α - s[4] = x[2]*(p[1]+ α*p[4]) - x[1]*(p[2] - α*p[3]) - s[5] = H(x, p, control(p)) -1 - return s -end; -``` -Inital guess -```@example orbit -y_guess = [1.0323e-4, 4.915e-5, 3.568e-4, -1.554e-4, 13.4] +# Initial guess for the shooting variables [p₀, tf] +# We use the direct solution to get a good initial guess +function get_initial_guess() + # Use the direct solution final time as initial guess + tf_guess = 13.4 + + # Initial guess for costates - start with normalized values + p0_guess = [1.0323e-4, 4.915e-5, 3.568e-4, -1.554e-4] + + return vcat(p0_guess, tf_guess) +end + +ξ_init = get_initial_guess() +println("Initial guess: p₀ = $(ξ_init[1:4]), tf = $(ξ_init[5])") ``` -Jacobian of the shooting function +## Solve the shooting problem + ```@example orbit -foo(y) = shoot(y[1:4], y[5]) -jfoo(y) = ForwardDiff.jacobian(foo, y) +# Solve the shooting problem using NLsolve +function solve_indirect_method() + println("Solving indirect method...") + + # Set up the shooting problem + result = nlsolve(shooting_function!, ξ_init, + method=:trust_region, + ftol=1e-10, + xtol=1e-10, + iterations=1000, + show_trace=false) + + if result.f_converged || result.x_converged + println("Shooting method converged!") + println("Final residual norm: $(norm(result.zero))") + return result.zero + else + println("Shooting method failed to converge") + println("Final residual norm: $(norm(result.zero))") + return nothing + end +end + +ξ_solution = solve_indirect_method() + +if ξ_solution !== nothing + p0_opt = ξ_solution[1:4] + tf_opt = ξ_solution[5] + + println("\nOptimal solution:") + println("p₀ = $(p0_opt)") + println("tf = $(tf_opt)") + + # Verify the solution + s_check = zeros(5) + shooting_function!(s_check, ξ_solution) + println("Shooting function residual: $(s_check)") +end ``` -Solving shoot(p0, tf) = 0 +## Generate the indirect solution trajectory + ```@example orbit -nl_sol = nlsolve(foo, y_guess; xtol=1e-8, method=:trust_region, show_trace=true, autodiff=:finite) -``` \ No newline at end of file +if ξ_solution !== nothing + # Generate the optimal trajectory + x0 = [x₁₀, x₂₀, x₃₀, x₄₀] + p0_opt = ξ_solution[1:4] + tf_opt = ξ_solution[5] + + # Solve the optimal trajectory + sol_indirect = flow((0.0, tf_opt), x0, p0_opt) + + # Extract time points for plotting + t_indirect = range(0, tf_opt, length=1000) + + # Evaluate solution at time points + x_traj = zeros(length(t_indirect), 4) + p_traj = zeros(length(t_indirect), 4) + u_traj = zeros(length(t_indirect), 2) + + for (i, t) in enumerate(t_indirect) + state_full = sol_indirect(t) + x_traj[i, :] = state_full[1:4] + p_traj[i, :] = state_full[5:8] + u_traj[i, :] = optimal_control(state_full[5:8]) + end + + println("Indirect solution generated successfully!") + println("Final time: $(tf_opt)") + println("Final position: $(x_traj[end, 1:2])") + println("Final radius: $(sqrt(x_traj[end, 1]^2 + x_traj[end, 2]^2))") +end +``` + From 05fa63a6983004868ccf9bb8003484becd3fbeff Mon Sep 17 00:00:00 2001 From: yassin moukan Date: Tue, 24 Jun 2025 15:13:46 +0200 Subject: [PATCH 15/20] Plots for indirect orbital times and modifications on previous code --- docs/src/tutorial-free-times.md | 121 +++++++++++++++++++++++--------- 1 file changed, 87 insertions(+), 34 deletions(-) diff --git a/docs/src/tutorial-free-times.md b/docs/src/tutorial-free-times.md index b0ba2db..c5b0b93 100644 --- a/docs/src/tutorial-free-times.md +++ b/docs/src/tutorial-free-times.md @@ -60,7 +60,8 @@ plot(sol; label="direct", size=(800, 800)) # Verification of results ```@raw html -
Verification of results. +
+ Click to show/hide mathematical verification. ``` Here the theorical part : @@ -197,7 +198,7 @@ end This example shows that problems with a free final time can be sensitive to discretization. A small grid may lead to suboptimal or slightly inaccurate results. -## Indirect method +# Direct resolution with free initial time : We keep the structure of the solution found with the direct method ```@example main-disc @@ -352,7 +353,8 @@ plot(sol; label="direct", size=(800, 800)) ## Verification of results ```@raw html -
Verification of results. +
+ Click to show/hide mathematical verification. ``` Here is the theoretical part using Pontryagin's Maximum Principle: ```math @@ -423,7 +425,7 @@ u(t) = -1 \quad \text{on} \quad [-1, 0] ```@raw html
``` -## An example with free final and free initial time : +## An example with both final and inital times being free: ```@example both_time using OptimalControl using NLPModelsIpopt @@ -453,7 +455,8 @@ end nothing # hide ``` -# Direct resolution with both free times : +# Direct resolution with both final and inital times being free: + We now solve the problem using a direct method, with automatic treatment of the free initial time. @@ -464,7 +467,7 @@ plot(sol; label="direct", size=(800, 800)) ``` -## A more concrete example about the change of orbit of a satellite : +## A more concrete example about the change of orbit of a satellite: ```@example orbit using OptimalControl @@ -539,8 +542,8 @@ function optimal_control(p) p_norm = sqrt(p₃^2 + p₄^2) if p_norm > 1e-10 - u₁ = -γ_max * p₃ / p_norm - u₂ = -γ_max * p₄ / p_norm + u₁ = γ_max * p₃ / p_norm + u₂ = γ_max * p₄ / p_norm return [u₁, u₂] else return [0.0, 0.0] @@ -562,7 +565,10 @@ end ``` ## Augmented dynamics - +```@raw html +
+Click to show/hide Augmented dynamics code +``` We define the combined state-costate system: ```@example orbit @@ -580,17 +586,17 @@ function augmented_dynamics!(dx, x_aug, params, t) u = optimal_control(p) u₁, u₂ = u - # State dynamics + # State dynamics (unchanged) dx[1] = x₃ dx[2] = x₄ dx[3] = -μ*x₁/r³ + u₁ dx[4] = -μ*x₂/r³ + u₂ - # Costate dynamics - dx[5] = -p₃ * μ * (2*x₁^2 - x₂^2) / r⁵ - dx[6] = -p₄ * μ * (2*x₂^2 - x₁^2) / r⁵ - dx[7] = -p₁ - dx[8] = -p₂ + # CORRECTED Costate dynamics: ṗ = -∂H/∂x + dx[5] = -(p₃ * μ * (3*x₁^2/r⁵ - 1/r³) + p₄ * μ * (3*x₁*x₂/r⁵)) # -∂H/∂x₁ + dx[6] = -(p₃ * μ * (3*x₁*x₂/r⁵) + p₄ * μ * (3*x₂^2/r⁵ - 1/r³)) # -∂H/∂x₂ + dx[7] = -p₁ # -∂H/∂x₃ + dx[8] = -p₂ # -∂H/∂x₄ end function create_flow() @@ -605,7 +611,9 @@ end flow = create_flow() ``` - +```@raw html +
+``` ## Shooting function The shooting function encodes the boundary conditions: @@ -614,32 +622,32 @@ The shooting function encodes the boundary conditions: function shooting_function!(s, ξ) p0 = ξ[1:4] tf = ξ[5] - + x0 = [x₁₀, x₂₀, x₃₀, x₄₀] - + try sol = flow((0.0, tf), x0, p0) - + x_final = sol.u[end][1:4] p_final = sol.u[end][5:8] - + x₁f, x₂f, x₃f, x₄f = x_final p₁f, p₂f, p₃f, p₄f = p_final - + s[1] = x₁f^2 + x₂f^2 - r_f^2 s[2] = p₁f s[3] = p₂f - + u_final = optimal_control(p_final) H_final = hamiltonian(x_final, p_final, u_final) s[4] = H_final - + s[5] = p₁f^2 + p₂f^2 + p₃f^2 + p₄f^2 - 1.0 - + catch e fill!(s, 1e6) end - + return nothing end ``` @@ -648,12 +656,10 @@ end ```@example orbit # Initial guess for the shooting variables [p₀, tf] -# We use the direct solution to get a good initial guess + function get_initial_guess() - # Use the direct solution final time as initial guess - tf_guess = 13.4 + tf_guess = 13.4 - # Initial guess for costates - start with normalized values p0_guess = [1.0323e-4, 4.915e-5, 3.568e-4, -1.554e-4] return vcat(p0_guess, tf_guess) @@ -669,15 +675,15 @@ println("Initial guess: p₀ = $(ξ_init[1:4]), tf = $(ξ_init[5])") # Solve the shooting problem using NLsolve function solve_indirect_method() println("Solving indirect method...") - + # Set up the shooting problem - result = nlsolve(shooting_function!, ξ_init, + result = nlsolve(shooting_function!, ξ_init, method=:trust_region, ftol=1e-10, xtol=1e-10, iterations=1000, show_trace=false) - + if result.f_converged || result.x_converged println("Shooting method converged!") println("Final residual norm: $(norm(result.zero))") @@ -694,11 +700,11 @@ end if ξ_solution !== nothing p0_opt = ξ_solution[1:4] tf_opt = ξ_solution[5] - + println("\nOptimal solution:") println("p₀ = $(p0_opt)") println("tf = $(tf_opt)") - + # Verify the solution s_check = zeros(5) shooting_function!(s_check, ξ_solution) @@ -739,4 +745,51 @@ if ξ_solution !== nothing println("Final radius: $(sqrt(x_traj[end, 1]^2 + x_traj[end, 2]^2))") end ``` +# Visualistion of results +```@raw html +
+Click to show/hide indirect method visualization code +``` +```@example orbit + + +# Simple visualization - just the basic plots +if ξ_solution !== nothing + p0_opt = ξ_solution[1:4] + tf_opt = ξ_solution[5] + + x0 = [x₁₀, x₂₀, x₃₀, x₄₀] + sol_indirect = flow((0.0, tf_opt), x0, p0_opt) + + t_indirect = range(0, tf_opt, length=1000) + + x_traj = zeros(length(t_indirect), 4) + p_traj = zeros(length(t_indirect), 4) + u_traj = zeros(length(t_indirect), 2) + + for (i, t) in enumerate(t_indirect) + state_full = sol_indirect(t) + x_traj[i, :] = state_full[1:4] + p_traj[i, :] = state_full[5:8] + u_traj[i, :] = optimal_control(state_full[5:8]) + end + + # Combined plot with 3 rows + plt = plot(layout=(3,1), size=(800, 900)) + + plot!(plt[1], t_indirect, [x_traj[:,1] x_traj[:,2] x_traj[:,3] x_traj[:,4]], + label=["x₁" "x₂" "x₃" "x₄"], title="States") + + plot!(plt[2], t_indirect, [p_traj[:,1] p_traj[:,2] p_traj[:,3] p_traj[:,4]], + label=["p₁" "p₂" "p₃" "p₄"], title="Costates") + + plot!(plt[3], t_indirect, [u_traj[:,1] u_traj[:,2]], + label=["u₁" "u₂"], title="Control") + + plt # This returns the plot for documentation +end +``` +```@raw html +
+``` \ No newline at end of file From 1f83452187f39a9f879f6cfc4c487fff36a0772e Mon Sep 17 00:00:00 2001 From: Olivier Cots Date: Wed, 25 Jun 2025 15:14:08 +0200 Subject: [PATCH 16/20] split free times --- docs/Project.toml | 1 - docs/make.jl | 7 +- docs/src/assets/Manifest.toml | 282 ++++--- ...d => tutorial-free-times-final-initial.md} | 2 +- docs/src/tutorial-free-times-final.md | 331 ++++++++ docs/src/tutorial-free-times-initial.md | 795 ++++++++++++++++++ docs/src/tutorial-free-times-orbital.md | 795 ++++++++++++++++++ 7 files changed, 2093 insertions(+), 120 deletions(-) rename docs/src/{tutorial-free-times.md => tutorial-free-times-final-initial.md} (99%) create mode 100644 docs/src/tutorial-free-times-final.md create mode 100644 docs/src/tutorial-free-times-initial.md create mode 100644 docs/src/tutorial-free-times-orbital.md diff --git a/docs/Project.toml b/docs/Project.toml index 0cc8353..8fe9d0e 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -15,7 +15,6 @@ OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" -Tutorials = "cb10daa6-a5e5-4c25-a171-ae181b8ea3c9" [compat] BenchmarkTools = "1.6" diff --git a/docs/make.jl b/docs/make.jl index ce35cf6..dfd39e8 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -29,7 +29,12 @@ makedocs(; "Tutorials and Advanced Features" => [ "Discrete continuation" => "tutorial-continuation.md", "Discretisation methods" => "tutorial-discretisation.md", - "Free times" => "tutorial-free-times.md", + "Free times" => [ + "Final time" => "tutorial-free-times-final.md", + "Initial time" => "tutorial-free-times-initial.md", + "Final and initial times" => "tutorial-free-times-final-initial.md", + "Orbital transfer min time" => "tutorial-free-times-orbital.md", + ], "NLP manipulations" => "tutorial-nlp.md", "Indirect simple shooting" => "tutorial-iss.md", "Goddard: direct, indirect" => "tutorial-goddard.md", diff --git a/docs/src/assets/Manifest.toml b/docs/src/assets/Manifest.toml index a5c6501..eb7f009 100644 --- a/docs/src/assets/Manifest.toml +++ b/docs/src/assets/Manifest.toml @@ -1,6 +1,6 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.11.2" +julia_version = "1.11.5" manifest_format = "2.0" project_hash = "e7183c2ad8bb7ad385c3187377b285a34e621395" @@ -88,6 +88,12 @@ version = "1.1.3" uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" version = "1.1.2" +[[deps.ArnoldiMethod]] +deps = ["LinearAlgebra", "Random", "StaticArrays"] +git-tree-sha1 = "d57bd3762d308bded22c3b82d033bff85f6195c6" +uuid = "ec485272-7323-5ecc-a04f-4719b315124d" +version = "0.4.0" + [[deps.ArrayInterface]] deps = ["Adapt", "LinearAlgebra"] git-tree-sha1 = "9606d7832795cbef89e06a550475be300364a8aa" @@ -163,13 +169,12 @@ version = "0.1.6" [[deps.BracketingNonlinearSolve]] deps = ["CommonSolve", "ConcreteStructs", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase"] -git-tree-sha1 = "a9014924595b7a2c1dd14aac516e38fa10ada656" +git-tree-sha1 = "637ebe439ba587828fd997b7810d8171eed2ea1b" uuid = "70df07ce-3d50-431d-a3e7-ca6ddb60ac1e" -version = "1.3.0" -weakdeps = ["ChainRulesCore", "ForwardDiff"] +version = "1.2.0" +weakdeps = ["ForwardDiff"] [deps.BracketingNonlinearSolve.extensions] - BracketingNonlinearSolveChainRulesCoreExt = ["ChainRulesCore", "ForwardDiff"] BracketingNonlinearSolveForwardDiffExt = "ForwardDiff" [[deps.Bzip2_jll]] @@ -186,9 +191,9 @@ version = "0.2.6" [[deps.CTBase]] deps = ["DocStringExtensions"] -git-tree-sha1 = "1e0fb19f883cda373412fd40f2ccad71758cb876" +git-tree-sha1 = "715a4381eb5e1d11ee92d86759ada1c27689345f" uuid = "54762871-cc72-4466-b8e8-f6c8b58076cd" -version = "0.16.1" +version = "0.16.0" [[deps.CTDirect]] deps = ["ADNLPModels", "CTBase", "CTModels", "CTParser", "DocStringExtensions", "HSL", "MKL", "NLPModelsIpopt", "SparseArrays"] @@ -239,9 +244,9 @@ version = "0.2.5" [[deps.Cairo_jll]] deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "fde3bf89aead2e723284a8ff9cdf5b551ed700e8" +git-tree-sha1 = "2ac646d71d0d24b44f3f8c84da8c9f4d70fb67df" uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.18.5+0" +version = "1.18.4+0" [[deps.ChainRulesCore]] deps = ["Compat", "LinearAlgebra"] @@ -293,9 +298,9 @@ weakdeps = ["SpecialFunctions"] [[deps.Colors]] deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] -git-tree-sha1 = "37ea44092930b1811e666c3bc38065d7d87fcc74" +git-tree-sha1 = "64e15186f0aa277e174aa81798f7eb8598e0157e" uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.13.1" +version = "0.13.0" [[deps.CommonSolve]] git-tree-sha1 = "0eee5eb66b1cf62cd6ad1b460238e60e4b09400c" @@ -420,9 +425,9 @@ version = "1.9.1" [[deps.DiffEqBase]] deps = ["ArrayInterface", "ConcreteStructs", "DataStructures", "DocStringExtensions", "EnumX", "EnzymeCore", "FastBroadcast", "FastClosures", "FastPower", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "Parameters", "PrecompileTools", "Printf", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "Setfield", "Static", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "TruncatedStacktraces"] -git-tree-sha1 = "a0e5b5669df9465bc3dd32ea4a8ddeefbc0f7b5c" +git-tree-sha1 = "1bcd3a5c585c477e5d0595937ea7b5adcda6c621" uuid = "2b5f629d-d688-5b77-993f-72d75c75574e" -version = "6.175.0" +version = "6.174.0" [deps.DiffEqBase.extensions] DiffEqBaseCUDAExt = "CUDA" @@ -435,7 +440,6 @@ version = "6.175.0" DiffEqBaseMPIExt = "MPI" DiffEqBaseMeasurementsExt = "Measurements" DiffEqBaseMonteCarloMeasurementsExt = "MonteCarloMeasurements" - DiffEqBaseMooncakeExt = "Mooncake" DiffEqBaseReverseDiffExt = "ReverseDiff" DiffEqBaseSparseArraysExt = "SparseArrays" DiffEqBaseTrackerExt = "Tracker" @@ -452,7 +456,6 @@ version = "6.175.0" MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" - Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" @@ -526,15 +529,15 @@ uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" version = "1.11.0" [[deps.DocStringExtensions]] -git-tree-sha1 = "7442a5dfe1ebb773c29cc2962a8980f47221d76c" +git-tree-sha1 = "e7b7e6f178525d17c720ab9c081e4ef04429f860" uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.9.5" +version = "0.9.4" [[deps.Documenter]] deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "CodecZlib", "Dates", "DocStringExtensions", "Downloads", "Git", "IOCapture", "InteractiveUtils", "JSON", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "TOML", "Test", "Unicode"] -git-tree-sha1 = "0ef76e54dfe9d736350a79334dc66236598c8d38" +git-tree-sha1 = "6c182d0bd94142d7cbc3ae8a1e74668f15d0dd65" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "1.12.0" +version = "1.11.4" [[deps.Downloads]] deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] @@ -547,9 +550,9 @@ uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" version = "1.0.5" [[deps.EnzymeCore]] -git-tree-sha1 = "7d7822a643c33bbff4eab9c87ca8459d7c688db0" +git-tree-sha1 = "1eb59f40a772d0fbd4cb75e00b3fa7f5f79c975a" uuid = "f151be2c-9106-41f4-ab19-57ee4f262869" -version = "0.8.11" +version = "0.8.9" weakdeps = ["Adapt"] [deps.EnzymeCore.extensions] @@ -786,9 +789,9 @@ version = "2.49.0+0" [[deps.Glib_jll]] deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "fee60557e4f19d0fe5cd169211fdda80e494f4e8" +git-tree-sha1 = "b0036b392358c80d2d2124746c2bf3d48d457938" uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.84.0+0" +version = "2.82.4+0" [[deps.Graphite2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -796,6 +799,12 @@ git-tree-sha1 = "8a6dbda1fd736d60cc477d99f2e7a042acfa46e8" uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" version = "1.3.15+0" +[[deps.Graphs]] +deps = ["ArnoldiMethod", "Compat", "DataStructures", "Distributed", "Inflate", "LinearAlgebra", "Random", "SharedArrays", "SimpleTraits", "SparseArrays", "Statistics"] +git-tree-sha1 = "3169fd3440a02f35e549728b0890904cfd4ae58a" +uuid = "86223c79-3864-5bf0-83f7-82e725a168b6" +version = "1.12.1" + [[deps.Grisu]] git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe" @@ -821,9 +830,9 @@ version = "1.10.16" [[deps.HarfBuzz_jll]] deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll"] -git-tree-sha1 = "f923f9a774fcf3f5cb761bfa43aeadd689714813" +git-tree-sha1 = "55c53be97790242c29031e5cd45e8ac296dadda3" uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" -version = "8.5.1+0" +version = "8.5.0+0" [[deps.Hwloc_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -842,6 +851,11 @@ git-tree-sha1 = "debdd00ffef04665ccbb3e150747a77560e8fad1" uuid = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" version = "0.1.1" +[[deps.Inflate]] +git-tree-sha1 = "d1b1b796e47d94588b3757fe84fbf65a5ec4a80d" +uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" +version = "0.1.5" + [[deps.InlineStrings]] git-tree-sha1 = "6a9fde685a7ac1eb3495f8e812c5a7c3711c2d5e" uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" @@ -893,9 +907,9 @@ version = "1.3.1" [[deps.Ipopt]] deps = ["Ipopt_jll", "LinearAlgebra", "OpenBLAS32_jll", "PrecompileTools"] -git-tree-sha1 = "4ad0d2dea51e5d49866b40a2d2521da6a1be7097" +git-tree-sha1 = "100030874c53b61d8c21d1bcb725265555d146ff" uuid = "b6b21f68-93f8-5de0-b562-5493be1d77c9" -version = "1.10.6" +version = "1.10.3" [deps.Ipopt.extensions] IpoptMathOptInterfaceExt = "MathOptInterface" @@ -905,9 +919,9 @@ version = "1.10.6" [[deps.Ipopt_jll]] deps = ["ASL_jll", "Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "MUMPS_seq_jll", "SPRAL_jll", "libblastrampoline_jll"] -git-tree-sha1 = "1bb978524c2837be596aeb2b69951feb6b9822f8" +git-tree-sha1 = "4f55ad688c698a4f77d892a1cb673f7e8a30f178" uuid = "9cc047cb-c261-5740-88fc-0cf96f7bdcc7" -version = "300.1400.1701+0" +version = "300.1400.1700+0" [[deps.IrrationalConstants]] git-tree-sha1 = "e2222959fbc6c19554dc15174c81bf7bf3aa691c" @@ -992,21 +1006,19 @@ version = "1.4.0" [[deps.Latexify]] deps = ["Format", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "OrderedCollections", "Requires"] -git-tree-sha1 = "4f34eaabe49ecb3fb0d58d6015e32fd31a733199" +git-tree-sha1 = "cd10d2cc78d34c0e2a3a36420ab607b611debfbb" uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" -version = "0.16.8" +version = "0.16.7" [deps.Latexify.extensions] DataFramesExt = "DataFrames" SparseArraysExt = "SparseArrays" SymEngineExt = "SymEngine" - TectonicExt = "tectonic_jll" [deps.Latexify.weakdeps] DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SymEngine = "123dc426-2d89-5057-bbad-38513e3affd8" - tectonic_jll = "d7dd28d6-a5e6-559c-9131-7eb760cdacc5" [[deps.LayoutPointers]] deps = ["ArrayInterface", "LinearAlgebra", "ManualMemory", "SIMDTypes", "Static", "StaticArrayInterface"] @@ -1072,10 +1084,10 @@ uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" version = "1.11.0" [[deps.Libffi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "c8da7e6a91781c41a863611c7e966098d783c57a" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "27ecae93dd25ee0909666e6835051dd684cc035e" uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" -version = "3.4.7+0" +version = "3.2.2+2" [[deps.Libglvnd_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll", "Xorg_libXext_jll"] @@ -1152,9 +1164,9 @@ version = "2.10.0" [[deps.LinearSolve]] deps = ["ArrayInterface", "ChainRulesCore", "ConcreteStructs", "DocStringExtensions", "EnumX", "GPUArraysCore", "InteractiveUtils", "Krylov", "LazyArrays", "Libdl", "LinearAlgebra", "MKL_jll", "Markdown", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "Setfield", "StaticArraysCore", "UnPack"] -git-tree-sha1 = "c0d1a91a50af6778863d320761f807f641f74935" +git-tree-sha1 = "6cc433e4e8cf070fc54bf29f620685d21890fe07" uuid = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" -version = "3.17.0" +version = "3.14.1" [deps.LinearSolve.extensions] LinearSolveBandedMatricesExt = "BandedMatrices" @@ -1249,9 +1261,9 @@ version = "0.4.17" [[deps.MUMPS_seq_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "METIS_jll", "libblastrampoline_jll"] -git-tree-sha1 = "196f61d99adc06f32c32bc4afe5298d9b1e862c8" +git-tree-sha1 = "0eab12f94948ca67908aec14b9f2ebefd17463fe" uuid = "d7ed1dd3-d0ae-5e8e-bfb4-87a502085b8d" -version = "500.800.0+0" +version = "500.700.301+0" [[deps.MacroTools]] git-tree-sha1 = "1e0228a030642014fe5cfe68c2c0a818f9e3f522" @@ -1350,10 +1362,10 @@ uuid = "f4238b75-b362-5c4c-b852-0801c9a21d71" version = "0.10.4" [[deps.NLSolversBase]] -deps = ["ADTypes", "DifferentiationInterface", "Distributed", "FiniteDiff", "ForwardDiff"] -git-tree-sha1 = "25a6638571a902ecfb1ae2a18fc1575f86b1d4df" +deps = ["DiffResults", "Distributed", "FiniteDiff", "ForwardDiff"] +git-tree-sha1 = "a0b464d183da839699f4c79e7606d9d186ec172c" uuid = "d41bc354-129a-5804-8e4c-c37616107c6c" -version = "7.10.0" +version = "7.8.3" [[deps.NaNMath]] deps = ["OpenLibm_jll"] @@ -1399,9 +1411,9 @@ version = "4.9.0" [[deps.NonlinearSolveBase]] deps = ["ADTypes", "Adapt", "ArrayInterface", "CommonSolve", "Compat", "ConcreteStructs", "DifferentiationInterface", "EnzymeCore", "FastClosures", "LinearAlgebra", "Markdown", "MaybeInplace", "Preferences", "Printf", "RecursiveArrayTools", "SciMLBase", "SciMLJacobianOperators", "SciMLOperators", "StaticArraysCore", "SymbolicIndexingInterface", "TimerOutputs"] -git-tree-sha1 = "404d71dd057759f4d590191a643113485c4a482a" +git-tree-sha1 = "1a6f6b161a644beac3c46a46f9bbb830c24abffb" uuid = "be0214bd-f91f-a760-ac4e-3421ce2b2da0" -version = "1.12.0" +version = "1.8.0" [deps.NonlinearSolveBase.extensions] NonlinearSolveBaseBandedMatricesExt = "BandedMatrices" @@ -1429,9 +1441,9 @@ version = "1.5.0" [[deps.NonlinearSolveQuasiNewton]] deps = ["ArrayInterface", "CommonSolve", "ConcreteStructs", "DiffEqBase", "LinearAlgebra", "LinearSolve", "MaybeInplace", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase", "SciMLOperators", "StaticArraysCore"] -git-tree-sha1 = "e3888bdbab6e0bfadbc3164ef4595e40e7b7e954" +git-tree-sha1 = "b69a68ef3a7bba7ab1d5ef6321ed6d9a613142b0" uuid = "9a2c21bd-3a47-402d-9113-8faf9a0ee114" -version = "1.6.0" +version = "1.5.0" weakdeps = ["ForwardDiff"] [deps.NonlinearSolveQuasiNewton.extensions] @@ -1476,7 +1488,7 @@ version = "0.3.27+1" [[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+2" +version = "0.8.5+0" [[deps.OpenSSH_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "OpenSSL_jll", "Zlib_jll"] @@ -1521,9 +1533,9 @@ version = "1.8.1" [[deps.OrdinaryDiffEq]] deps = ["ADTypes", "Adapt", "ArrayInterface", "DataStructures", "DiffEqBase", "DocStringExtensions", "EnumX", "ExponentialUtilities", "FastBroadcast", "FastClosures", "FillArrays", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "InteractiveUtils", "LineSearches", "LinearAlgebra", "LinearSolve", "Logging", "MacroTools", "MuladdMacro", "NonlinearSolve", "OrdinaryDiffEqAdamsBashforthMoulton", "OrdinaryDiffEqBDF", "OrdinaryDiffEqCore", "OrdinaryDiffEqDefault", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqExplicitRK", "OrdinaryDiffEqExponentialRK", "OrdinaryDiffEqExtrapolation", "OrdinaryDiffEqFIRK", "OrdinaryDiffEqFeagin", "OrdinaryDiffEqFunctionMap", "OrdinaryDiffEqHighOrderRK", "OrdinaryDiffEqIMEXMultistep", "OrdinaryDiffEqLinear", "OrdinaryDiffEqLowOrderRK", "OrdinaryDiffEqLowStorageRK", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqNordsieck", "OrdinaryDiffEqPDIRK", "OrdinaryDiffEqPRK", "OrdinaryDiffEqQPRK", "OrdinaryDiffEqRKN", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqSDIRK", "OrdinaryDiffEqSSPRK", "OrdinaryDiffEqStabilizedIRK", "OrdinaryDiffEqStabilizedRK", "OrdinaryDiffEqSymplecticRK", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "Polyester", "PreallocationTools", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleNonlinearSolve", "SimpleUnPack", "SparseArrays", "Static", "StaticArrayInterface", "StaticArrays", "TruncatedStacktraces"] -git-tree-sha1 = "1c2b2df870944e0dc01454fd87479847c55fa26c" +git-tree-sha1 = "56d5500e9970f0112a4e1ab6474d6fedde61ef64" uuid = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" -version = "6.98.0" +version = "6.97.0" [[deps.OrdinaryDiffEqAdamsBashforthMoulton]] deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqLowOrderRK", "Polyester", "RecursiveArrayTools", "Reexport", "Static"] @@ -1533,9 +1545,9 @@ version = "1.2.0" [[deps.OrdinaryDiffEqBDF]] deps = ["ADTypes", "ArrayInterface", "DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqSDIRK", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "StaticArrays", "TruncatedStacktraces"] -git-tree-sha1 = "9124a686af119063bb4d3a8f87044a8f312fcad9" +git-tree-sha1 = "970ac761ae1c4249fc70d75760c328269a518585" uuid = "6ad6398a-0878-4a85-9266-38940aa047c8" -version = "1.6.0" +version = "1.3.0" [[deps.OrdinaryDiffEqCore]] deps = ["ADTypes", "Accessors", "Adapt", "ArrayInterface", "DataStructures", "DiffEqBase", "DocStringExtensions", "EnumX", "FastBroadcast", "FastClosures", "FastPower", "FillArrays", "FunctionWrappersWrappers", "InteractiveUtils", "LinearAlgebra", "Logging", "MacroTools", "MuladdMacro", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleUnPack", "Static", "StaticArrayInterface", "StaticArraysCore", "SymbolicIndexingInterface", "TruncatedStacktraces"] @@ -1554,10 +1566,10 @@ uuid = "50262376-6c5a-4cf5-baba-aaf4f84d72d7" version = "1.4.0" [[deps.OrdinaryDiffEqDifferentiation]] -deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "ConstructionBase", "DiffEqBase", "DifferentiationInterface", "FastBroadcast", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEqCore", "SciMLBase", "SciMLOperators", "SparseArrays", "SparseMatrixColorings", "StaticArrayInterface", "StaticArrays"] -git-tree-sha1 = "efecf0c4cc44e16251b0e718f08b0876b2a82b80" +deps = ["ADTypes", "ArrayInterface", "DiffEqBase", "FastBroadcast", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEqCore", "SciMLBase", "SparseArrays", "SparseDiffTools", "StaticArrayInterface", "StaticArrays"] +git-tree-sha1 = "9a535370247496c1375ba6d08b0493c0d856d25b" uuid = "4302a76b-040a-498a-8c04-15b101fed76b" -version = "1.10.0" +version = "1.4.0" [[deps.OrdinaryDiffEqExplicitRK]] deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "TruncatedStacktraces"] @@ -1579,9 +1591,9 @@ version = "1.5.0" [[deps.OrdinaryDiffEqFIRK]] deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "FastGaussQuadrature", "FastPower", "LinearAlgebra", "LinearSolve", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators"] -git-tree-sha1 = "0da8ec3491821262a3d2828e6370e76b51a770a3" +git-tree-sha1 = "588f454cc1c48c20a32f03838af4983053a1d3ae" uuid = "5960d6e9-dd7a-4743-88e7-cf307b64f125" -version = "1.12.0" +version = "1.9.0" [[deps.OrdinaryDiffEqFeagin]] deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "RecursiveArrayTools", "Reexport", "Static"] @@ -1627,9 +1639,9 @@ version = "1.3.0" [[deps.OrdinaryDiffEqNonlinearSolve]] deps = ["ADTypes", "ArrayInterface", "DiffEqBase", "FastBroadcast", "FastClosures", "ForwardDiff", "LinearAlgebra", "LinearSolve", "MuladdMacro", "NonlinearSolve", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "PreallocationTools", "RecursiveArrayTools", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleNonlinearSolve", "StaticArrays"] -git-tree-sha1 = "ffdb0f5207b0e30f8b1edf99b3b9546d9c48ccaf" +git-tree-sha1 = "1b89e3e84752a3cbd2c94db565e6ea7acb5279b2" uuid = "127b3ac7-2247-4354-8eb6-78cf4e7c58e8" -version = "1.10.0" +version = "1.5.0" [[deps.OrdinaryDiffEqNordsieck]] deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqTsit5", "Polyester", "RecursiveArrayTools", "Reexport", "Static"] @@ -1639,9 +1651,9 @@ version = "1.1.0" [[deps.OrdinaryDiffEqPDIRK]] deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Polyester", "Reexport", "StaticArrays"] -git-tree-sha1 = "ab9897e4bc8e3cf8e15f1cf61dbdd15d6a2341d7" +git-tree-sha1 = "f74b27b8b811a83d77a9cad6293e793ab0804cdc" uuid = "5dd0a6cf-3d4b-4314-aa06-06d4e299bc89" -version = "1.3.1" +version = "1.3.0" [[deps.OrdinaryDiffEqPRK]] deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "Reexport"] @@ -1662,10 +1674,10 @@ uuid = "af6ede74-add8-4cfd-b1df-9a4dbb109d7a" version = "1.1.0" [[deps.OrdinaryDiffEqRosenbrock]] -deps = ["ADTypes", "DiffEqBase", "DifferentiationInterface", "FastBroadcast", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "LinearSolve", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "Static"] -git-tree-sha1 = "1ce0096d920e95773220e818f29bf4b37ea2bb78" +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "LinearSolve", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "Static"] +git-tree-sha1 = "bede3226fd485741e364239e23236e07ace77639" uuid = "43230ef6-c299-4910-a778-202eb28ce4ce" -version = "1.11.0" +version = "1.8.0" [[deps.OrdinaryDiffEqSDIRK]] deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "RecursiveArrayTools", "Reexport", "SciMLBase", "TruncatedStacktraces"] @@ -1714,11 +1726,17 @@ deps = ["Artifacts", "Libdl"] uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" version = "10.42.0+1" +[[deps.PackageExtensionCompat]] +git-tree-sha1 = "fb28e33b8a95c4cee25ce296c817d89cc2e53518" +uuid = "65ce6f38-6b18-4e1d-a461-8949797d7930" +version = "1.0.2" +weakdeps = ["Requires", "TOML"] + [[deps.Pango_jll]] deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl"] -git-tree-sha1 = "275a9a6d85dc86c24d03d1837a0010226a96f540" +git-tree-sha1 = "3b31172c032a1def20c98dae3f2cdc9d10e3b561" uuid = "36c8627f-9965-5494-a995-c6b170f724f3" -version = "1.56.3+0" +version = "1.56.1+0" [[deps.Parameters]] deps = ["OrderedCollections", "UnPack"] @@ -1781,9 +1799,9 @@ version = "1.40.13" [[deps.Polyester]] deps = ["ArrayInterface", "BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "ManualMemory", "PolyesterWeave", "Static", "StaticArrayInterface", "StrideArraysCore", "ThreadingUtilities"] -git-tree-sha1 = "6f7cd22a802094d239824c57d94c8e2d0f7cfc7d" +git-tree-sha1 = "2082cc4be5e765bd982ed04ea06c068f4f702410" uuid = "f517fe37-dbe3-4b94-8317-1923a5111588" -version = "0.7.18" +version = "0.7.17" [[deps.PolyesterWeave]] deps = ["BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "Static", "ThreadingUtilities"] @@ -1961,9 +1979,9 @@ version = "1.16.1" [[deps.RuntimeGeneratedFunctions]] deps = ["ExprTools", "SHA", "Serialization"] -git-tree-sha1 = "86a8a8b783481e1ea6b9c91dd949cb32191f8ab4" +git-tree-sha1 = "7cb9d10026d630ce2dd2a1fc6006a3d5041b34c0" uuid = "7e49a35a-f44a-4d26-94aa-eba1b4ca6b47" -version = "0.5.15" +version = "0.5.14" [[deps.SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" @@ -1976,15 +1994,15 @@ version = "0.1.0" [[deps.SPRAL_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "Libdl", "METIS_jll", "libblastrampoline_jll"] -git-tree-sha1 = "4f9833187a65ead66ed1907b44d5f20606282e3f" +git-tree-sha1 = "11f3da4b25efacd1cec8e263421f2a9003a5e8e0" uuid = "319450e9-13b8-58e8-aa9f-8fd1420848ab" -version = "2025.5.20+0" +version = "2024.5.8+0" [[deps.SciMLBase]] -deps = ["ADTypes", "Accessors", "Adapt", "ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "Moshi", "PrecompileTools", "Preferences", "Printf", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "SciMLStructures", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface"] -git-tree-sha1 = "d7bef263e23c7f5392071e4538b1949cee7ae458" +deps = ["ADTypes", "Accessors", "ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "Moshi", "PrecompileTools", "Preferences", "Printf", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "SciMLStructures", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface"] +git-tree-sha1 = "2fd047893cb0089b180fcbb7e8434ba15dcc2841" uuid = "0bca4576-84f4-4d90-8ffe-ffa030f20462" -version = "2.99.0" +version = "2.87.0" [deps.SciMLBase.extensions] SciMLBaseChainRulesCoreExt = "ChainRulesCore" @@ -2009,15 +2027,15 @@ version = "2.99.0" [[deps.SciMLJacobianOperators]] deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "ConstructionBase", "DifferentiationInterface", "FastClosures", "LinearAlgebra", "SciMLBase", "SciMLOperators"] -git-tree-sha1 = "7da1216346ad79499d08d7e2a3dbf297dc80c829" +git-tree-sha1 = "d563758f3ce5153810adebc534d88e24d34eeb95" uuid = "19f34311-ddf3-4b8b-af20-060888a46c0e" -version = "0.1.6" +version = "0.1.5" [[deps.SciMLOperators]] deps = ["Accessors", "ArrayInterface", "DocStringExtensions", "LinearAlgebra", "MacroTools"] -git-tree-sha1 = "d82853c515a8d9d42c1ab493a2687a37f1e26c91" +git-tree-sha1 = "1c4b7f6c3e14e6de0af66e66b86d525cae10ecb4" uuid = "c0aeaf25-5076-4817-a8d5-81caf7dfa961" -version = "0.4.0" +version = "0.3.13" weakdeps = ["SparseArrays", "StaticArraysCore"] [deps.SciMLOperators.extensions] @@ -2086,6 +2104,12 @@ version = "2.5.0" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" +[[deps.SimpleTraits]] +deps = ["InteractiveUtils", "MacroTools"] +git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231" +uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d" +version = "0.9.4" + [[deps.SimpleUnPack]] git-tree-sha1 = "58e6353e72cde29b90a69527e56df1b5c3d8c437" uuid = "ce78b400-467f-4804-87d8-8f486da07d0a" @@ -2114,9 +2138,9 @@ version = "1.11.0" [[deps.SparseConnectivityTracer]] deps = ["ADTypes", "DocStringExtensions", "FillArrays", "LinearAlgebra", "Random", "SparseArrays"] -git-tree-sha1 = "2c3cbb3703f77045d4eb891b2831ca132ef4183c" +git-tree-sha1 = "fadb2d7010dd92912e5eb31a493613ad4b8c9583" uuid = "9f842d2f-2579-4b1d-911e-f412cf18a3f5" -version = "0.6.19" +version = "0.6.18" [deps.SparseConnectivityTracer.extensions] SparseConnectivityTracerDataInterpolationsExt = "DataInterpolations" @@ -2132,11 +2156,31 @@ version = "0.6.19" NaNMath = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" +[[deps.SparseDiffTools]] +deps = ["ADTypes", "Adapt", "ArrayInterface", "Compat", "DataStructures", "FiniteDiff", "ForwardDiff", "Graphs", "LinearAlgebra", "PackageExtensionCompat", "Random", "Reexport", "SciMLOperators", "Setfield", "SparseArrays", "StaticArrayInterface", "StaticArrays", "UnPack", "VertexSafeGraphs"] +git-tree-sha1 = "ccbf06a08573200853b1bd06203d8ccce8449578" +uuid = "47a9eef4-7e08-11e9-0b38-333d64bd3804" +version = "2.26.0" + + [deps.SparseDiffTools.extensions] + SparseDiffToolsEnzymeExt = "Enzyme" + SparseDiffToolsPolyesterExt = "Polyester" + SparseDiffToolsPolyesterForwardDiffExt = "PolyesterForwardDiff" + SparseDiffToolsSymbolicsExt = "Symbolics" + SparseDiffToolsZygoteExt = "Zygote" + + [deps.SparseDiffTools.weakdeps] + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588" + PolyesterForwardDiff = "98d1487c-24ca-40b6-b7ab-df2af84e126b" + Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" + Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" + [[deps.SparseMatrixColorings]] -deps = ["ADTypes", "DocStringExtensions", "LinearAlgebra", "PrecompileTools", "Random", "SparseArrays"] -git-tree-sha1 = "ab958b4fec46d1f1d057bb8e2a99bfdb90744646" +deps = ["ADTypes", "DocStringExtensions", "LinearAlgebra", "Random", "SparseArrays"] +git-tree-sha1 = "76e9564f0de0d1d7a46095e758ae13ceba680cfb" uuid = "0a514795-09f3-496d-8182-132a7b665d35" -version = "0.4.20" +version = "0.4.19" [deps.SparseMatrixColorings.extensions] SparseMatrixColoringsCliqueTreesExt = "CliqueTrees" @@ -2207,9 +2251,9 @@ weakdeps = ["SparseArrays"] [[deps.StatsAPI]] deps = ["LinearAlgebra"] -git-tree-sha1 = "9d72a13a3f4dd3795a195ac5a44d7d6ff5f552ff" +git-tree-sha1 = "1ff449ad350c9c4cbc756624d6f8a8c3ef56d3ed" uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" -version = "1.7.1" +version = "1.7.0" [[deps.StatsBase]] deps = ["AliasTables", "DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] @@ -2267,9 +2311,9 @@ version = "1.0.1" [[deps.Tables]] deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "OrderedCollections", "TableTraits"] -git-tree-sha1 = "f2c1efbc8f3a609aadf318094f8fc5204bdaf344" +git-tree-sha1 = "598cd7c1f68d1e205689b1c2fe65a9f85846f297" uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.12.1" +version = "1.12.0" [[deps.Tar]] deps = ["ArgTools", "SHA"] @@ -2317,7 +2361,7 @@ uuid = "781d530d-4396-4725-bb49-402e4bee1e77" version = "1.4.0" [[deps.Tutorials]] -path = "/Users/yassinmoukan/Desktop/stage_co/Tutorials.jl" +path = "/Users/ocots/Research/logiciels/dev/control-toolbox/Tutorials" uuid = "cb10daa6-a5e5-4c25-a171-ae181b8ea3c9" version = "0.1.4" @@ -2348,28 +2392,32 @@ version = "0.4.1" [[deps.Unitful]] deps = ["Dates", "LinearAlgebra", "Random"] -git-tree-sha1 = "02c1ac8104c9cf941395db79c611483909c04c7d" +git-tree-sha1 = "d62610ec45e4efeabf7032d67de2ffdea8344bed" uuid = "1986cc42-f94f-5a68-af5c-568840ba703d" -version = "1.23.0" -weakdeps = ["ConstructionBase", "ForwardDiff", "InverseFunctions", "Printf"] +version = "1.22.1" +weakdeps = ["ConstructionBase", "InverseFunctions"] [deps.Unitful.extensions] ConstructionBaseUnitfulExt = "ConstructionBase" - ForwardDiffExt = "ForwardDiff" InverseFunctionsUnitfulExt = "InverseFunctions" - PrintfExt = "Printf" [[deps.UnitfulLatexify]] deps = ["LaTeXStrings", "Latexify", "Unitful"] -git-tree-sha1 = "af305cc62419f9bd61b6644d19170a4d258c7967" +git-tree-sha1 = "975c354fcd5f7e1ddcc1f1a23e6e091d99e99bc8" uuid = "45397f5d-5981-4c77-b2b3-fc36d6e9b728" -version = "1.7.0" +version = "1.6.4" [[deps.Unzip]] git-tree-sha1 = "ca0969166a028236229f63514992fc073799bb78" uuid = "41fe7b60-77ed-43a1-b4f0-825fd5a5650d" version = "0.2.0" +[[deps.VertexSafeGraphs]] +deps = ["Graphs"] +git-tree-sha1 = "8351f8d73d7e880bfc042a8b6922684ebeafb35c" +uuid = "19fa3120-7c27-5ec5-8db8-b0b0aa330d6f" +version = "0.2.0" + [[deps.Vulkan_Loader_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Wayland_jll", "Xorg_libX11_jll", "Xorg_libXrandr_jll", "xkbcommon_jll"] git-tree-sha1 = "2f0486047a07670caad3a81a075d2e518acc5c59" @@ -2377,10 +2425,10 @@ uuid = "a44049a8-05dd-5a78-86c9-5fde0876e88c" version = "1.3.243+0" [[deps.Wayland_jll]] -deps = ["Artifacts", "EpollShim_jll", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "XML2_jll"] -git-tree-sha1 = "49be0be57db8f863a902d59c0083d73281ecae8e" +deps = ["Artifacts", "EpollShim_jll", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] +git-tree-sha1 = "85c7811eddec9e7f22615371c3cc81a504c508ee" uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" -version = "1.23.1+0" +version = "1.21.0+2" [[deps.Wayland_protocols_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -2503,28 +2551,28 @@ uuid = "12413925-8142-5f55-bb0e-6d7ca50bb09b" version = "0.4.0+1" [[deps.Xorg_xcb_util_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll"] -git-tree-sha1 = "68da27247e7d8d8dafd1fcf0c3654ad6506f5f97" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll"] +git-tree-sha1 = "e7fd7b2881fa2eaa72717420894d3938177862d1" uuid = "2def613f-5ad1-5310-b15b-b15d46f528f5" -version = "0.4.1+0" +version = "0.4.0+1" [[deps.Xorg_xcb_util_keysyms_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] -git-tree-sha1 = "44ec54b0e2acd408b0fb361e1e9244c60c9c3dd4" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] +git-tree-sha1 = "d1151e2c45a544f32441a567d1690e701ec89b00" uuid = "975044d2-76e6-5fbe-bf08-97ce7c6574c7" -version = "0.4.1+0" +version = "0.4.0+1" [[deps.Xorg_xcb_util_renderutil_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] -git-tree-sha1 = "5b0263b6d080716a02544c55fdff2c8d7f9a16a0" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] +git-tree-sha1 = "dfd7a8f38d4613b6a575253b3174dd991ca6183e" uuid = "0d47668e-0667-5a69-a72c-f761630bfb7e" -version = "0.3.10+0" +version = "0.3.9+1" [[deps.Xorg_xcb_util_wm_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] -git-tree-sha1 = "f233c83cad1fa0e70b7771e0e21b061a116f2763" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] +git-tree-sha1 = "e78d10aab01a4a154142c5006ed44fd9e8e31b67" uuid = "c22f9ab0-d5fe-5066-847c-f4bb1cd4e361" -version = "0.4.2+0" +version = "0.4.1+1" [[deps.Xorg_xkbcomp_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxkbfile_jll"] @@ -2603,10 +2651,10 @@ uuid = "1183f4f0-6f2a-5f1a-908b-139f9cdfea6f" version = "0.2.2+0" [[deps.libevdev_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "56d643b57b188d30cccc25e331d416d3d358e557" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "141fe65dc3efabb0b1d5ba74e91f6ad26f84cc22" uuid = "2db6ffa8-e38f-5e21-84af-90c45d0032cc" -version = "1.13.4+0" +version = "1.11.0+0" [[deps.libfdk_aac_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -2633,10 +2681,10 @@ uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" version = "1.3.7+2" [[deps.mtdev_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "b4d631fd51f2e9cdd93724ae25b2efc198b059b1" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "814e154bdb7be91d78b6802843f76b6ece642f11" uuid = "009596ad-96f7-51b1-9f1b-5ce2d5e8a71e" -version = "1.1.7+0" +version = "1.1.6+0" [[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] diff --git a/docs/src/tutorial-free-times.md b/docs/src/tutorial-free-times-final-initial.md similarity index 99% rename from docs/src/tutorial-free-times.md rename to docs/src/tutorial-free-times-final-initial.md index c5b0b93..d45d32b 100644 --- a/docs/src/tutorial-free-times.md +++ b/docs/src/tutorial-free-times-final-initial.md @@ -1,5 +1,5 @@ ```@meta -Draft = false +Draft = true ``` # [Free Initial and Final Times](@id tutorial-free-times) diff --git a/docs/src/tutorial-free-times-final.md b/docs/src/tutorial-free-times-final.md new file mode 100644 index 0000000..694a04b --- /dev/null +++ b/docs/src/tutorial-free-times-final.md @@ -0,0 +1,331 @@ +```@meta +Draft = false +``` + +# [Optimal control problem with free final time](@id tutorial-free-times-final) + +In this tutorial, we explore an optimal control problem with free final time `tf`. + +Here is the required packages for the tutorial: + +```@example main-free-final +using LinearAlgebra: norm +using NLPModelsIpopt +using NonlinearSolve +using OptimalControl +using OrdinaryDiffEq # to get the Flow function from OptimalControl +using Plots +using Printf +``` + +## Definition of the problem + +```@example main-free-final +t0 = 0 # initial time +x0 = [0, 0] # initial point +xf_target = [1, 0] # final point + +@def ocp begin + tf ∈ R, variable + t ∈ [t0, tf], time + x ∈ R², state + u ∈ R, control + -1 ≤ u(t) ≤ 1 + x(t0) == x0 + x(tf) == xf_target + 0.05 ≤ tf ≤ Inf + ẋ(t) == [x₂(t), u(t)] + tf → min +end +nothing # hide +``` + +In the definition, the line that shows that the problem has a **free** final time is the following: + +```julia +tf ∈ R, variable +``` + +The variable `tf` is indeed the final time according to: + +```julia +t ∈ [t0, tf], time +``` + +## Direct resolution: + +You may need to add this type of constrain in your problem definition : + +To help the direct solver to converge, we have added a constraint on the variable `tf`: + +```julia +0.05 ≤ tf ≤ Inf +``` + +We now solve the problem using a direct method, with automatic treatment of the free final time. + +```@example main-free-final +sol = solve(ocp; grid_size=100) +``` + +And plot the solution. + +```@example main-free-final +plt = plot(sol; label="direct", size=(800, 800)) +``` + +## Verification of results + +!!! note "Mathematical computations" + + ```@raw html +
+ Click to show/hide mathematical computations. + ``` + + Here is the theorical part. The pseudo-Hamiltonian is: + + ```math + H(x, p, u) = p_1 x_2 + p_2 u. + ``` + + Pontryagin's theorem gives: + + ```math + \begin{aligned} + \dot{p}_1(t) &= 0, \\ + \dot{p}_2(t) &= -p_1(t). \\ + \end{aligned} + ``` + + Hence, $p_1(t) = \mathrm{cst} = \alpha$ and $p_2(t) = -\alpha t + \beta$, $\beta = p_2(0)$. According to Pontryagin's theorem, we have: + + ```math + \begin{aligned} + p_1(t) &= 1, \\ + p_2(t) &= 1-t. \\ + \end{aligned} + ``` + + Besides, the pseudo-Hamiltonian satisfies: + ```math + H(x(t_f), p(t_f), u(t_f)) = -p° = 1. + ``` + + For this problem, we can prove that the optimal control $u$ satisfies: + + ```math + u(t) = \left\{ + \begin{aligned} + 1 & \quad\text{if}\quad t \in [0, t_1], \\ + -1 & \quad\text{if}\quad t \in (t_1, t_f], \\ + \end{aligned} + \right. + ``` + + where $t_1$ is a constant to determined, as $t_f$. To do so, we have to compute the state $x$: + + On t ∈ [0,t1] : + ```math + x1' = x2 \\ + x2'= u = 1 \\ + ``` + + ```math + x2(t) = t \\ + x1(t) = (1/2)*t^2 + ``` + + When t = t1 : + ```math + x2(t1) = t1 \\ + x1(t1) = (1/2)*t1^2 + ``` + + On t ∈ [t1,tf] + ```math + x2(t) = -t + 2*t1 \\ + x1(t) = -(1/2)*t^2 + 2*t1*t + C2 \\ + ``` + + ```math + x1(t1) = -(1/2)*t1^2 + 2*t1^2 + C2 = (1/2)*t1^2 \\ + C2 = -t1^2 \\ + ``` + + ```math + x1(t) = -(1/2)*t^2 + 2*t1*t - t1^2 + ``` + + Finally you can solve the terminal conditions : + ```math + x1(tf) = 1, \quad x2(tf) = 0 \\ + ``` + + ```math + x2(tf) = -tf + 2*t1 = 0 \\ + tf = 2*t1 \\ + ``` + + ```math + x1(tf) = -(1/2)*tf^2 + 2*t1*tf - t1^2 = 1 \\ + -2*t1^2 + 4*t1 - t1^2 = t1 = 1 \\ + ``` + + ```math + t1 = 1, \quad t2 = 2 + ``` + + To sum up we find the following solutions : + ```math + x1(t) = \begin{cases} + (1/2)*t^2 & \text{si } t \in [0, 1) \\ + -(1/2)*t^2 + 2*t - 1 & \text{si } t \in [1, 2] + \end{cases} + \qquad + x2(t) = \begin{cases} + t & \text{si } t \in [0, 1) \\ + 2 - t & \text{si } t \in [1, 2] + \end{cases} + ``` + + ```math + p1(t) = 1, \quad p2(t) = 1-t, \quad p°=-1 + ``` + + ```math + u(t) = \begin{cases} + 1 & \text{si } t \in [0, 1) \\ + -1 & \text{si } t \in [1, 2] + \end{cases} + ``` + ```@raw html +
+ ``` + +Now we can compare the results found with the direct method whith the theoritical analysis: +```@example main-free-final +tf = variable(sol) +u = control(sol) +p = costate(sol) +x = state(sol) +p° = -1 +H(t) = p(t)[1]*x(t)[2] + p(t)[2]*u(t) + +@printf("H(tf) = %.3f\n", H(tf)) +@printf("x(tf) = [%.3f, %.3f]\n", x(tf)[1], x(tf)[2]) +@printf("p(tf) = [%.3f, %.3f]\n", p(tf)[1], p(tf)[2]) +``` + +The numerical results closely match the theoretical predictions: the final state $x(t_f)=[1,0]$ is exactly satisfied. +The costate and Hamiltonian values at final time show a small deviation (≈ 0.01), likely due to numerical precision. +Overall, the direct method confirms the theoretical analysis with excellent accuracy. + +We can analyse the influence of using different discretization sizes (`grid_size`), and observed the following results for the optimal $t_f$: + +```@example main-free-final +for N in [20, 50, 100, 200] + solN = solve(ocp; grid_size=N, display=false) + @printf("grid_size = %3d → tf = %.5f\n", N, objective(solN)) +end +``` + +This example shows that problems with a free final time can be sensitive to discretization. A small grid may lead to suboptimal or slightly inaccurate results. + +## Indirect method + +We first define the pseudo-Hamiltonian and the switching function needed to define the shooting function. + +```@example main-free-final +# Pseudo-Hamitlonian +H(x, p, u) = p[1]*x[2] + p[2]*u + +# Hamiltonian lift +F1(x) = [0, 1] +H1 = Lift(F1) +nothing # hide +``` + +Then, we define the different flows, associated to control laws $u(t)=+1$ and $u(t)=-1$. + +```@example main-free-final +const u_pos = 1 +const u_neg = -1 + +f_pos = Flow(ocp, (x, p, tf) -> u_pos) # it depends on tf since tf is the variable +f_neg = Flow(ocp, (x, p, tf) -> u_neg) +nothing # hide +``` + +We can now define the shooting function following the structure given by the direct method. + +```@example main-free-final +function shoot!(s, p0, t1, tf) + + # the flows + x1, p1 = f_pos(t0, x0, p0, t1) + xf, pf = f_neg(t1, x1, p1, tf) + + # final control + uf = -1 + + # shooting conditions + s[1:2] = xf .- xf_target # reach the target + s[3] = H(xf, pf, uf) - 1 # final condition on the pseudo-Hamiltonian + s[4] = H1(x1, p1) # switching condition + +end +nothing # hide +``` + +Before solving our problem we must find a good initial guess to help the convergence of the algorithm. + +```@example main-free-final +t = time_grid(sol) +x = state(sol) +p = costate(sol) +φ(t) = H1(x(t), p(t)) # switching function + +p0 = p(t0) +t1 = t[argmin(abs.(φ.(t)))] +tf = t[end] + +println("p0 = ", p0) +println("t1 = ", t1) +println("tf = ", tf) + +# Norm of the shooting function at initial guess +s = zeros(4) +shoot!(s, p0, t1, tf) +println("\nNorm of the shooting function: ‖s‖ = ", norm(s), "\n") +``` + +And we finally can solve the problem using an indirect method. + +```@example main-free-final +# Aggregated function +nle! = (s, ξ, λ) -> shoot!(s, ξ[1:2], ξ[3], ξ[4]) + +# Nonlinear problem +ξ_guess = [p0..., t1, tf] # Initial guess +prob = NonlinearProblem(nle!, ξ_guess) + +# Resolution +indirect_sol = solve(prob; show_trace=Val(true), abstol=1e-8, reltol=1e-8) +``` + +We can now plot and compare with the direct method. + +```@example main-free-final +# Data +p0 = indirect_sol.u[1:2] +t1 = indirect_sol.u[3] +tf = indirect_sol.u[4] + +# Compute the optimal solution +f = f_pos * (t1, f_neg) # concatenate the flows +flow_sol = f((t0, tf), x0, p0; saveat=range(t0, tf, 100)) + +# Plot the solution +plot!(plt, flow_sol; label="indirect", color=:red) +``` \ No newline at end of file diff --git a/docs/src/tutorial-free-times-initial.md b/docs/src/tutorial-free-times-initial.md new file mode 100644 index 0000000..d45d32b --- /dev/null +++ b/docs/src/tutorial-free-times-initial.md @@ -0,0 +1,795 @@ +```@meta +Draft = true +``` + +# [Free Initial and Final Times](@id tutorial-free-times) + +In this tutorial, we explore optimal control problems with free initial time `t₀` and/or final time `t_f`. These problems require special treatment in both direct and indirect methods, particularly when handling time-varying constraints and objectives. + + +```@example main-disc +using OptimalControl +using NLPModelsIpopt +using BenchmarkTools +using DataFrames +using Plots +using Printf + +function double_integrator_mintf() + @def ocp begin + tf ∈ R, variable + t ∈ [0, tf], time + x ∈ R², state + u ∈ R, control + -1 ≤ u(t) ≤ 1 + x(0) == [0, 0] + x(tf) == [1, 0] + 0.05 ≤ tf ≤ Inf + ẋ(t) == [x₂(t), u(t)] + tf → min + end + return ocp +end +nothing # hide +``` + +To allow the use of a free final time, you define t0 and/or tf as variables, and then set the global time t between these two variables. Here is a complete example : + +```julia +v=(t0, tf) ∈ R², variable +t ∈ [t0, tf], time +``` + +You may need to add this type of constrain in your problem definition : + +```julia +0.05 ≤ tf ≤ Inf +``` + +It is to ensure and help the convergence of the algorithm depending on your problem and it can also be needed when dealing with problems having a free initial time, as we will see in the next example. + +## Direct resolution with free final time : + +We now solve the problem using a direct method, with automatic treatment of the free final time. + +```@example main-disc +ocp = double_integrator_mintf() +sol = solve(ocp; grid_size=100) +plot(sol; label="direct", size=(800, 800)) +``` + +# Verification of results +```@raw html +
+ Click to show/hide mathematical verification. +``` + +Here the theorical part : +```math +H = p1*x2 + p2*u +``` +Conditions of Pontryagin's theorem : +```math +p1' = 0, \quad p2' = -p1 \\ +p1 = p1(0) = constante = 1\\ +p2 = -p1*t + p2(0) = 1-t\\ +``` +Transversal conditions : +```math +H[tf] = -p° = 1 +``` +We find u : +```math +u(t) = 1 ∈ [0,t1] \\ +u(t) = -1 ∈ [t1, tf] +``` +Where t1 is a constant to determined. + +Now we have to integrate x' : + +On t ∈ [0,t1] : +```math +x1' = x2 \\ +x2'= u = 1 \\ +``` + +```math +x2(t) = t \\ +x1(t) = (1/2)*t^2 +``` + +When t = t1 : +```math +x2(t1) = t1 \\ +x1(t1) = (1/2)*t1^2 +``` + +On t ∈ [t1,tf] +```math +x2(t) = -t + 2*t1 \\ +x1(t) = -(1/2)*t^2 + 2*t1*t + C2 \\ +``` + +```math +x1(t1) = -(1/2)*t1^2 + 2*t1^2 + C2 = (1/2)*t1^2 \\ +C2 = -t1^2 \\ +``` + +```math +x1(t) = -(1/2)*t^2 + 2*t1*t - t1^2 +``` + +Finally you can solve the terminal conditions : +```math +x1(tf) = 1, \quad x2(tf) = 0 \\ +``` + +```math +x2(tf) = -tf + 2*t1 = 0 \\ +tf = 2*t1 \\ +``` + +```math +x1(tf) = -(1/2)*tf^2 + 2*t1*tf - t1^2 = 1 \\ +-2*t1^2 + 4*t1 - t1^2 = t1 = 1 \\ +``` + +```math +t1 = 1, \quad t2 = 2 +``` + +To sum up we find the following solutions : +```math +x1(t) = \begin{cases} +(1/2)*t^2 & \text{si } t \in [0, 1) \\ +-(1/2)*t^2 + 2*t - 1 & \text{si } t \in [1, 2] +\end{cases} +\qquad +x2(t) = \begin{cases} +t & \text{si } t \in [0, 1) \\ +2 - t & \text{si } t \in [1, 2] +\end{cases} +``` + +```math +p1(t) = 1, \quad p2(t) = 1-t, \quad p°=-1 +``` + +```math +u(t) = \begin{cases} +1 & \text{si } t \in [0, 1) \\ +-1 & \text{si } t \in [1, 2] +\end{cases} +``` +```@raw html +
+``` + + +Now we can compare the results found with the direct method whith the theoritical analysis : +```@example main-disc +tf = variable(sol) +u = control(sol) +p = costate(sol) +x = state(sol) +p° = -1 + +xf = x(tf) +pf = p(tf) + +Htf = pf[1]*xf[2] + pf[2]*u(tf) +@printf("H(tf) = %.3f\n", Htf) +@printf("x(tf) = [%.3f, %.3f]\n", xf[1], xf[2]) +@printf("p(tf) = [%.3f, %.3f]\n", pf[1], pf[2]) +``` + +The numerical results closely match the theoretical predictions: the final state x(tf)=[1,0] is exactly satisfied. +The costate and Hamiltonian values at final time show a small deviation (≈ 0.01), likely due to numerical precision. +Overall, the direct method confirms the theoretical analysis with excellent accuracy. + +We can analyse the influence of using different discretization sizes (grid_size), and observed the following results for the optimal tf: + +```@example main-disc +for N in [20, 50, 100, 200] + solN = solve(ocp; grid_size=N, display=false) + @printf("grid_size = %3d → tf = %.5f\n", N, objective(solN)) +end +``` + +This example shows that problems with a free final time can be sensitive to discretization. A small grid may lead to suboptimal or slightly inaccurate results. + +# Direct resolution with free initial time : + +We keep the structure of the solution found with the direct method +```@example main-disc +t = time_grid(sol) +x = state(sol) +u = control(sol) +p = costate(sol) + +H(x,p,u,t) = p[1](t)*x[2](t) + p[2](t)*u(u) +H(xf, pf, tf) = pf[1]*xf[2] + pf[2]*u(tf) + +# Hamiltonian vector field +F1(x) = [0, 1] +H1 = Lift(F1) +φ(t) = H1(x(t), p(t)) # switching function +``` + + +First, lets define the different flows +```@example main-disc +using OrdinaryDiffEq # to get the Flow function from OptimalControl + +const u1 = 1 +const u0 = -1 + +f1 = Flow(ocp, (x, p, tf) -> u1) +f0 = Flow(ocp, (x, p, tf) -> u0) +``` + +And with this we have the following shooting function +```@example main-disc +x0 = [0.0, 0.0] # état initial +xf_target = [1.0, 0.0] # état final + +function shoot!(s, p0, t1, tf) +x1, p1 = f1(0.0, x0, p0, t1) +xf, pf = f0(t1, x1, p1, tf) + +# Conditions de tir +s[1:2] = xf .- xf_target # état final +s[3] = H(xf, pf, tf) - 1 # condition de transversalité H(tf) = 1 +s[4] = H1(x1, p1) # φ = 0 au switch +end +``` + +Before solving our problem we must find a good initial guess to help the convergence of the algorithm +```@example main-disc +p0 = p(0.0) +φ_arr = abs.(φ.(t)) +t1 = t[argmin(φ_arr)] +tf = t[end] + +println("p0 ≈ ", p0) +println("t1 ≈ ", t1) +println("tf ≈ ", tf) + +s = zeros(4) +shoot!(s, p0, t1, tf) + +ξ = [p0..., t1, tf] +``` + +And we finally can solve the problem using an indirect method +```@example main-disc +using NonlinearSolve +using DifferentiationInterface +import ForwardDiff + +backend = AutoForwardDiff() + +struct MYSOL + x::Vector{Float64} +end + +function fsolve(f, j, x; kwargs...) + try + MINPACK.fsolve(f, j, x; kwargs...) + catch e + println("MINPACK error:") + println(e) + println("→ Using NonlinearSolve fallback") + + # Wrap pour respecter l'interface de NonlinearProblem + function wrapped_f(s, ξ, p) + f(s, ξ) + return nothing + end + + prob = NonlinearProblem(wrapped_f, x) + sol = solve(prob; abstol=1e-8, reltol=1e-8) + return MYSOL(sol.u) + end +end + +# Agrégation du problème +nle! = (s, ξ) -> shoot!(s, ξ[1:2], ξ[3], ξ[4]) +jnle! = (js, ξ) -> jacobian!(nle!, similar(ξ), js, backend, ξ) + +ξ0 = [p0... , t1, tf] +indirect_sol = fsolve(nle!, jnle!, ξ0) + +p0 = indirect_sol.x[1:2] +t1 = indirect_sol.x[3] +tf = indirect_sol.x[4] + +f = f1 * (t1, f0) +flow_sol = f((0.0, tf), x0, p0) + +plot!(flow_sol, label="indirect", color=:red) +``` + +# Now we will try an example with a free initial time + +```@example initial_time +using OptimalControl +using NLPModelsIpopt +using BenchmarkTools +using DataFrames +using Plots +using Printf + +function double_integrator_mint0() + @def ocp begin + t0 ∈ R, variable + tf=0 + t ∈ [t0, tf], time + x ∈ R², state + u ∈ R, control + -1 ≤ u(t) ≤ 1 + x(t0) == [0, 0] + x(tf) == [1, 0] + 0.05 ≤ -t0 ≤ Inf + ẋ(t) == [x₂(t), u(t)] + -t0 → min + end + return ocp +end +nothing # hide +``` + +# Direct resolution with free initial time : + +We now solve the problem using a direct method, with automatic treatment of the free initial time. + +```@example initial_time +ocp = double_integrator_mint0() +sol = solve(ocp; grid_size=100) +plot(sol; label="direct", size=(800, 800)) +``` + + + +## Verification of results +```@raw html +
+ Click to show/hide mathematical verification. +``` +Here is the theoretical part using Pontryagin's Maximum Principle: +```math +H = p_1 x_2 + p_2 u + 1 +``` + +Conditions from Pontryagin’s theorem: +```math +p_1' = 0 \quad \Rightarrow \quad p_1 = c_1 \quad (\text{constant}) \\ +p_2' = -p_1 \quad \Rightarrow \quad p_2 = -c_1 t + c_2 +``` + +Switching condition: +```math +p_2(t_s) = 0 \quad \Rightarrow \quad c_2 = c_1 t_s +``` + +Optimal control: +```math +u(t) = 1 \quad \text{on} \quad [t_0, t_s] \\ +u(t) = -1 \quad \text{on} \quad [t_s, 0] +``` + +Now we integrate the system: + +On \( t \in [t_0, t_s] \) : +```math +x_2' = u = 1 \quad \Rightarrow \quad x_2(t) = t - t_0 \\ +x_1' = x_2 \quad \Rightarrow \quad x_1(t) = \frac{(t - t_0)^2}{2} +``` + +At switching time \( t = t_s \) : +```math +x_2(t_s) = t_s - t_0 \\ +x_1(t_s) = \frac{(t_s - t_0)^2}{2} +``` + +On \( t \in [t_s, 0] \) : +```math +x_2' = u = -1 \quad \Rightarrow \quad x_2(t) = x_2(t_s) - (t - t_s) \\ +x_1' = x_2 \quad \Rightarrow \quad x_1(t) = x_1(t_s) + \int_{t_s}^t x_2(s) ds +``` + +Final velocity condition: +```math +x_2(0) = 0 \quad \Rightarrow \quad t_s - t_0 + t_s = 0 \quad \Rightarrow \quad t_0 = 2 t_s +``` + +Final position: +```math +x_1(0) = x_1(t_s) + \frac{t_s^2}{2} \quad \Rightarrow \quad x_1(0) = t_s^2 = 1 \quad \Rightarrow \quad t_s = -1 +``` + +We deduce: +```math +t_0 = 2 * t_s = -2 +``` + +### Final solution: +- Switching time: \( t_s = -1 \) +- Initial time: \( t_0 = -2 \) + +Control: +```math +u(t) = 1 \quad \text{on} \quad [-2, -1] \\ +u(t) = -1 \quad \text{on} \quad [-1, 0] +``` +```@raw html +
+``` +## An example with both final and inital times being free: +```@example both_time +using OptimalControl +using NLPModelsIpopt +using BenchmarkTools +using DataFrames +using Plots +using Printf + +function double_integrator_freet0tf() + @def ocp begin + v=(t0, tf) ∈ R², variable + t ∈ [t0, tf], time + x ∈ R², state + u ∈ R, control + -1 ≤ u(t) ≤ 1 + x(t0) == [0, 0] + x(tf) == [1, 0] + 0.05 ≤ t0 ≤ 10 + 0.05 ≤ tf ≤ 10 + 0.01 ≤ tf - t0 ≤ Inf + ẋ(t) == [x₂(t), u(t)] + t0 → max + end + + return ocp +end +nothing # hide +``` + +# Direct resolution with both final and inital times being free: + + +We now solve the problem using a direct method, with automatic treatment of the free initial time. + +```@example both_time +ocp = double_integrator_freet0tf() +sol = solve(ocp; grid_size=100) +plot(sol; label="direct", size=(800, 800)) +``` + + +## A more concrete example about the change of orbit of a satellite: + +```@example orbit +using OptimalControl +using NLPModelsIpopt +using BenchmarkTools +using DataFrames +using Plots +using Printf +using OrdinaryDiffEq +using NonlinearSolve +using DifferentiationInterface +import ForwardDiff +using LinearAlgebra +using NLsolve + + +const x₁₀ = -42272.67 # initial position x +const x₂₀ = 0 # initial position y +const x₃₀ = 0 # initial velocity in x +const x₄₀ = -5696.72 # initial velocity in y +const μ = 5.1658620912*1e12 # gravitational parameter +const γ_max = 0.05 # maximal thrust norm +const r_f = 42165 # target orbit radius (final distance to origin) +const rf3 = r_f^3 +const α = sqrt(μ/rf3); + +function min_orbit_tf() + @def ocp begin + tf ∈ R, variable + t ∈ [0, tf], time + x ∈ R⁴, state + u ∈ R², control + u₁(t)^2 + u₂(t)^2 ≤ γ_max^2 + x(0) == [x₁₀, x₂₀, x₃₀, x₄₀] + x₁(tf)^2 + x₂(tf)^2 == r_f^2 + 0.05 ≤ tf ≤ Inf + ẋ(t) == [ + x₃(t), + x₄(t), + -μ * x₁(t) / ((x₁(t)^2 + x₂(t)^2)^(3/2)) + u₁(t), + -μ * x₂(t) / ((x₁(t)^2 + x₂(t)^2)^(3/2)) + u₂(t) + ] + tf → min + end + + return ocp +end +nothing # hide +``` + +# Direct resolution with minimal orbital time : + +We now solve the problem using a direct method, with automatic treatment of the free initial time. + +```@example orbit +ocp = min_orbit_tf() +sol = solve(ocp;init=(variable=13.4,), grid_size=100) +plot(sol; label="direct", size=(800, 800)) +``` +# Indirect resolution with minimal orbital time : + + +## Hamiltonian and optimal control + +From the Pontryagin Maximum Principle, the optimal control is bang-bang: + +```@example orbit +ocp = min_orbit_tf() + +function optimal_control(p) + p₃, p₄ = p[3], p[4] + p_norm = sqrt(p₃^2 + p₄^2) + + if p_norm > 1e-10 + u₁ = γ_max * p₃ / p_norm + u₂ = γ_max * p₄ / p_norm + return [u₁, u₂] + else + return [0.0, 0.0] + end +end + +function hamiltonian(x, p, u) + x₁, x₂, x₃, x₄ = x + p₁, p₂, p₃, p₄ = p + u₁, u₂ = u + + r = sqrt(x₁^2 + x₂^2) + r³ = r^3 + + return (p₁*x₃ + p₂*x₄ + + p₃*(-μ*x₁/r³ + u₁) + + p₄*(-μ*x₂/r³ + u₂)) +end +``` + +## Augmented dynamics +```@raw html +
+Click to show/hide Augmented dynamics code +``` +We define the combined state-costate system: + +```@example orbit +function augmented_dynamics!(dx, x_aug, params, t) + x = x_aug[1:4] + p = x_aug[5:8] + + x₁, x₂, x₃, x₄ = x + p₁, p₂, p₃, p₄ = p + + r = sqrt(x₁^2 + x₂^2) + r³ = r^3 + r⁵ = r^5 + + u = optimal_control(p) + u₁, u₂ = u + + # State dynamics (unchanged) + dx[1] = x₃ + dx[2] = x₄ + dx[3] = -μ*x₁/r³ + u₁ + dx[4] = -μ*x₂/r³ + u₂ + + # CORRECTED Costate dynamics: ṗ = -∂H/∂x + dx[5] = -(p₃ * μ * (3*x₁^2/r⁵ - 1/r³) + p₄ * μ * (3*x₁*x₂/r⁵)) # -∂H/∂x₁ + dx[6] = -(p₃ * μ * (3*x₁*x₂/r⁵) + p₄ * μ * (3*x₂^2/r⁵ - 1/r³)) # -∂H/∂x₂ + dx[7] = -p₁ # -∂H/∂x₃ + dx[8] = -p₂ # -∂H/∂x₄ +end + +function create_flow() + function flow_func(t_span, x0, p0) + x_aug0 = vcat(x0, p0) + prob = ODEProblem(augmented_dynamics!, x_aug0, t_span) + sol = solve(prob, Tsit5(), reltol=1e-12, abstol=1e-12) + return sol + end + return flow_func +end + +flow = create_flow() +``` +```@raw html +
+``` +## Shooting function + +The shooting function encodes the boundary conditions: + +```@example orbit +function shooting_function!(s, ξ) + p0 = ξ[1:4] + tf = ξ[5] + + x0 = [x₁₀, x₂₀, x₃₀, x₄₀] + + try + sol = flow((0.0, tf), x0, p0) + + x_final = sol.u[end][1:4] + p_final = sol.u[end][5:8] + + x₁f, x₂f, x₃f, x₄f = x_final + p₁f, p₂f, p₃f, p₄f = p_final + + s[1] = x₁f^2 + x₂f^2 - r_f^2 + s[2] = p₁f + s[3] = p₂f + + u_final = optimal_control(p_final) + H_final = hamiltonian(x_final, p_final, u_final) + s[4] = H_final + + s[5] = p₁f^2 + p₂f^2 + p₃f^2 + p₄f^2 - 1.0 + + catch e + fill!(s, 1e6) + end + + return nothing +end +``` + +## Initial guess and solver setup + +```@example orbit +# Initial guess for the shooting variables [p₀, tf] + +function get_initial_guess() + tf_guess = 13.4 + + p0_guess = [1.0323e-4, 4.915e-5, 3.568e-4, -1.554e-4] + + return vcat(p0_guess, tf_guess) +end + +ξ_init = get_initial_guess() +println("Initial guess: p₀ = $(ξ_init[1:4]), tf = $(ξ_init[5])") +``` + +## Solve the shooting problem + +```@example orbit +# Solve the shooting problem using NLsolve +function solve_indirect_method() + println("Solving indirect method...") + + # Set up the shooting problem + result = nlsolve(shooting_function!, ξ_init, + method=:trust_region, + ftol=1e-10, + xtol=1e-10, + iterations=1000, + show_trace=false) + + if result.f_converged || result.x_converged + println("Shooting method converged!") + println("Final residual norm: $(norm(result.zero))") + return result.zero + else + println("Shooting method failed to converge") + println("Final residual norm: $(norm(result.zero))") + return nothing + end +end + +ξ_solution = solve_indirect_method() + +if ξ_solution !== nothing + p0_opt = ξ_solution[1:4] + tf_opt = ξ_solution[5] + + println("\nOptimal solution:") + println("p₀ = $(p0_opt)") + println("tf = $(tf_opt)") + + # Verify the solution + s_check = zeros(5) + shooting_function!(s_check, ξ_solution) + println("Shooting function residual: $(s_check)") +end +``` + +## Generate the indirect solution trajectory + +```@example orbit +if ξ_solution !== nothing + # Generate the optimal trajectory + x0 = [x₁₀, x₂₀, x₃₀, x₄₀] + p0_opt = ξ_solution[1:4] + tf_opt = ξ_solution[5] + + # Solve the optimal trajectory + sol_indirect = flow((0.0, tf_opt), x0, p0_opt) + + # Extract time points for plotting + t_indirect = range(0, tf_opt, length=1000) + + # Evaluate solution at time points + x_traj = zeros(length(t_indirect), 4) + p_traj = zeros(length(t_indirect), 4) + u_traj = zeros(length(t_indirect), 2) + + for (i, t) in enumerate(t_indirect) + state_full = sol_indirect(t) + x_traj[i, :] = state_full[1:4] + p_traj[i, :] = state_full[5:8] + u_traj[i, :] = optimal_control(state_full[5:8]) + end + + println("Indirect solution generated successfully!") + println("Final time: $(tf_opt)") + println("Final position: $(x_traj[end, 1:2])") + println("Final radius: $(sqrt(x_traj[end, 1]^2 + x_traj[end, 2]^2))") +end +``` +# Visualistion of results +```@raw html +
+Click to show/hide indirect method visualization code +``` + +```@example orbit + + +# Simple visualization - just the basic plots +if ξ_solution !== nothing + p0_opt = ξ_solution[1:4] + tf_opt = ξ_solution[5] + + x0 = [x₁₀, x₂₀, x₃₀, x₄₀] + sol_indirect = flow((0.0, tf_opt), x0, p0_opt) + + t_indirect = range(0, tf_opt, length=1000) + + x_traj = zeros(length(t_indirect), 4) + p_traj = zeros(length(t_indirect), 4) + u_traj = zeros(length(t_indirect), 2) + + for (i, t) in enumerate(t_indirect) + state_full = sol_indirect(t) + x_traj[i, :] = state_full[1:4] + p_traj[i, :] = state_full[5:8] + u_traj[i, :] = optimal_control(state_full[5:8]) + end + + # Combined plot with 3 rows + plt = plot(layout=(3,1), size=(800, 900)) + + plot!(plt[1], t_indirect, [x_traj[:,1] x_traj[:,2] x_traj[:,3] x_traj[:,4]], + label=["x₁" "x₂" "x₃" "x₄"], title="States") + + plot!(plt[2], t_indirect, [p_traj[:,1] p_traj[:,2] p_traj[:,3] p_traj[:,4]], + label=["p₁" "p₂" "p₃" "p₄"], title="Costates") + + plot!(plt[3], t_indirect, [u_traj[:,1] u_traj[:,2]], + label=["u₁" "u₂"], title="Control") + + plt # This returns the plot for documentation +end +``` +```@raw html +
+``` \ No newline at end of file diff --git a/docs/src/tutorial-free-times-orbital.md b/docs/src/tutorial-free-times-orbital.md new file mode 100644 index 0000000..d45d32b --- /dev/null +++ b/docs/src/tutorial-free-times-orbital.md @@ -0,0 +1,795 @@ +```@meta +Draft = true +``` + +# [Free Initial and Final Times](@id tutorial-free-times) + +In this tutorial, we explore optimal control problems with free initial time `t₀` and/or final time `t_f`. These problems require special treatment in both direct and indirect methods, particularly when handling time-varying constraints and objectives. + + +```@example main-disc +using OptimalControl +using NLPModelsIpopt +using BenchmarkTools +using DataFrames +using Plots +using Printf + +function double_integrator_mintf() + @def ocp begin + tf ∈ R, variable + t ∈ [0, tf], time + x ∈ R², state + u ∈ R, control + -1 ≤ u(t) ≤ 1 + x(0) == [0, 0] + x(tf) == [1, 0] + 0.05 ≤ tf ≤ Inf + ẋ(t) == [x₂(t), u(t)] + tf → min + end + return ocp +end +nothing # hide +``` + +To allow the use of a free final time, you define t0 and/or tf as variables, and then set the global time t between these two variables. Here is a complete example : + +```julia +v=(t0, tf) ∈ R², variable +t ∈ [t0, tf], time +``` + +You may need to add this type of constrain in your problem definition : + +```julia +0.05 ≤ tf ≤ Inf +``` + +It is to ensure and help the convergence of the algorithm depending on your problem and it can also be needed when dealing with problems having a free initial time, as we will see in the next example. + +## Direct resolution with free final time : + +We now solve the problem using a direct method, with automatic treatment of the free final time. + +```@example main-disc +ocp = double_integrator_mintf() +sol = solve(ocp; grid_size=100) +plot(sol; label="direct", size=(800, 800)) +``` + +# Verification of results +```@raw html +
+ Click to show/hide mathematical verification. +``` + +Here the theorical part : +```math +H = p1*x2 + p2*u +``` +Conditions of Pontryagin's theorem : +```math +p1' = 0, \quad p2' = -p1 \\ +p1 = p1(0) = constante = 1\\ +p2 = -p1*t + p2(0) = 1-t\\ +``` +Transversal conditions : +```math +H[tf] = -p° = 1 +``` +We find u : +```math +u(t) = 1 ∈ [0,t1] \\ +u(t) = -1 ∈ [t1, tf] +``` +Where t1 is a constant to determined. + +Now we have to integrate x' : + +On t ∈ [0,t1] : +```math +x1' = x2 \\ +x2'= u = 1 \\ +``` + +```math +x2(t) = t \\ +x1(t) = (1/2)*t^2 +``` + +When t = t1 : +```math +x2(t1) = t1 \\ +x1(t1) = (1/2)*t1^2 +``` + +On t ∈ [t1,tf] +```math +x2(t) = -t + 2*t1 \\ +x1(t) = -(1/2)*t^2 + 2*t1*t + C2 \\ +``` + +```math +x1(t1) = -(1/2)*t1^2 + 2*t1^2 + C2 = (1/2)*t1^2 \\ +C2 = -t1^2 \\ +``` + +```math +x1(t) = -(1/2)*t^2 + 2*t1*t - t1^2 +``` + +Finally you can solve the terminal conditions : +```math +x1(tf) = 1, \quad x2(tf) = 0 \\ +``` + +```math +x2(tf) = -tf + 2*t1 = 0 \\ +tf = 2*t1 \\ +``` + +```math +x1(tf) = -(1/2)*tf^2 + 2*t1*tf - t1^2 = 1 \\ +-2*t1^2 + 4*t1 - t1^2 = t1 = 1 \\ +``` + +```math +t1 = 1, \quad t2 = 2 +``` + +To sum up we find the following solutions : +```math +x1(t) = \begin{cases} +(1/2)*t^2 & \text{si } t \in [0, 1) \\ +-(1/2)*t^2 + 2*t - 1 & \text{si } t \in [1, 2] +\end{cases} +\qquad +x2(t) = \begin{cases} +t & \text{si } t \in [0, 1) \\ +2 - t & \text{si } t \in [1, 2] +\end{cases} +``` + +```math +p1(t) = 1, \quad p2(t) = 1-t, \quad p°=-1 +``` + +```math +u(t) = \begin{cases} +1 & \text{si } t \in [0, 1) \\ +-1 & \text{si } t \in [1, 2] +\end{cases} +``` +```@raw html +
+``` + + +Now we can compare the results found with the direct method whith the theoritical analysis : +```@example main-disc +tf = variable(sol) +u = control(sol) +p = costate(sol) +x = state(sol) +p° = -1 + +xf = x(tf) +pf = p(tf) + +Htf = pf[1]*xf[2] + pf[2]*u(tf) +@printf("H(tf) = %.3f\n", Htf) +@printf("x(tf) = [%.3f, %.3f]\n", xf[1], xf[2]) +@printf("p(tf) = [%.3f, %.3f]\n", pf[1], pf[2]) +``` + +The numerical results closely match the theoretical predictions: the final state x(tf)=[1,0] is exactly satisfied. +The costate and Hamiltonian values at final time show a small deviation (≈ 0.01), likely due to numerical precision. +Overall, the direct method confirms the theoretical analysis with excellent accuracy. + +We can analyse the influence of using different discretization sizes (grid_size), and observed the following results for the optimal tf: + +```@example main-disc +for N in [20, 50, 100, 200] + solN = solve(ocp; grid_size=N, display=false) + @printf("grid_size = %3d → tf = %.5f\n", N, objective(solN)) +end +``` + +This example shows that problems with a free final time can be sensitive to discretization. A small grid may lead to suboptimal or slightly inaccurate results. + +# Direct resolution with free initial time : + +We keep the structure of the solution found with the direct method +```@example main-disc +t = time_grid(sol) +x = state(sol) +u = control(sol) +p = costate(sol) + +H(x,p,u,t) = p[1](t)*x[2](t) + p[2](t)*u(u) +H(xf, pf, tf) = pf[1]*xf[2] + pf[2]*u(tf) + +# Hamiltonian vector field +F1(x) = [0, 1] +H1 = Lift(F1) +φ(t) = H1(x(t), p(t)) # switching function +``` + + +First, lets define the different flows +```@example main-disc +using OrdinaryDiffEq # to get the Flow function from OptimalControl + +const u1 = 1 +const u0 = -1 + +f1 = Flow(ocp, (x, p, tf) -> u1) +f0 = Flow(ocp, (x, p, tf) -> u0) +``` + +And with this we have the following shooting function +```@example main-disc +x0 = [0.0, 0.0] # état initial +xf_target = [1.0, 0.0] # état final + +function shoot!(s, p0, t1, tf) +x1, p1 = f1(0.0, x0, p0, t1) +xf, pf = f0(t1, x1, p1, tf) + +# Conditions de tir +s[1:2] = xf .- xf_target # état final +s[3] = H(xf, pf, tf) - 1 # condition de transversalité H(tf) = 1 +s[4] = H1(x1, p1) # φ = 0 au switch +end +``` + +Before solving our problem we must find a good initial guess to help the convergence of the algorithm +```@example main-disc +p0 = p(0.0) +φ_arr = abs.(φ.(t)) +t1 = t[argmin(φ_arr)] +tf = t[end] + +println("p0 ≈ ", p0) +println("t1 ≈ ", t1) +println("tf ≈ ", tf) + +s = zeros(4) +shoot!(s, p0, t1, tf) + +ξ = [p0..., t1, tf] +``` + +And we finally can solve the problem using an indirect method +```@example main-disc +using NonlinearSolve +using DifferentiationInterface +import ForwardDiff + +backend = AutoForwardDiff() + +struct MYSOL + x::Vector{Float64} +end + +function fsolve(f, j, x; kwargs...) + try + MINPACK.fsolve(f, j, x; kwargs...) + catch e + println("MINPACK error:") + println(e) + println("→ Using NonlinearSolve fallback") + + # Wrap pour respecter l'interface de NonlinearProblem + function wrapped_f(s, ξ, p) + f(s, ξ) + return nothing + end + + prob = NonlinearProblem(wrapped_f, x) + sol = solve(prob; abstol=1e-8, reltol=1e-8) + return MYSOL(sol.u) + end +end + +# Agrégation du problème +nle! = (s, ξ) -> shoot!(s, ξ[1:2], ξ[3], ξ[4]) +jnle! = (js, ξ) -> jacobian!(nle!, similar(ξ), js, backend, ξ) + +ξ0 = [p0... , t1, tf] +indirect_sol = fsolve(nle!, jnle!, ξ0) + +p0 = indirect_sol.x[1:2] +t1 = indirect_sol.x[3] +tf = indirect_sol.x[4] + +f = f1 * (t1, f0) +flow_sol = f((0.0, tf), x0, p0) + +plot!(flow_sol, label="indirect", color=:red) +``` + +# Now we will try an example with a free initial time + +```@example initial_time +using OptimalControl +using NLPModelsIpopt +using BenchmarkTools +using DataFrames +using Plots +using Printf + +function double_integrator_mint0() + @def ocp begin + t0 ∈ R, variable + tf=0 + t ∈ [t0, tf], time + x ∈ R², state + u ∈ R, control + -1 ≤ u(t) ≤ 1 + x(t0) == [0, 0] + x(tf) == [1, 0] + 0.05 ≤ -t0 ≤ Inf + ẋ(t) == [x₂(t), u(t)] + -t0 → min + end + return ocp +end +nothing # hide +``` + +# Direct resolution with free initial time : + +We now solve the problem using a direct method, with automatic treatment of the free initial time. + +```@example initial_time +ocp = double_integrator_mint0() +sol = solve(ocp; grid_size=100) +plot(sol; label="direct", size=(800, 800)) +``` + + + +## Verification of results +```@raw html +
+ Click to show/hide mathematical verification. +``` +Here is the theoretical part using Pontryagin's Maximum Principle: +```math +H = p_1 x_2 + p_2 u + 1 +``` + +Conditions from Pontryagin’s theorem: +```math +p_1' = 0 \quad \Rightarrow \quad p_1 = c_1 \quad (\text{constant}) \\ +p_2' = -p_1 \quad \Rightarrow \quad p_2 = -c_1 t + c_2 +``` + +Switching condition: +```math +p_2(t_s) = 0 \quad \Rightarrow \quad c_2 = c_1 t_s +``` + +Optimal control: +```math +u(t) = 1 \quad \text{on} \quad [t_0, t_s] \\ +u(t) = -1 \quad \text{on} \quad [t_s, 0] +``` + +Now we integrate the system: + +On \( t \in [t_0, t_s] \) : +```math +x_2' = u = 1 \quad \Rightarrow \quad x_2(t) = t - t_0 \\ +x_1' = x_2 \quad \Rightarrow \quad x_1(t) = \frac{(t - t_0)^2}{2} +``` + +At switching time \( t = t_s \) : +```math +x_2(t_s) = t_s - t_0 \\ +x_1(t_s) = \frac{(t_s - t_0)^2}{2} +``` + +On \( t \in [t_s, 0] \) : +```math +x_2' = u = -1 \quad \Rightarrow \quad x_2(t) = x_2(t_s) - (t - t_s) \\ +x_1' = x_2 \quad \Rightarrow \quad x_1(t) = x_1(t_s) + \int_{t_s}^t x_2(s) ds +``` + +Final velocity condition: +```math +x_2(0) = 0 \quad \Rightarrow \quad t_s - t_0 + t_s = 0 \quad \Rightarrow \quad t_0 = 2 t_s +``` + +Final position: +```math +x_1(0) = x_1(t_s) + \frac{t_s^2}{2} \quad \Rightarrow \quad x_1(0) = t_s^2 = 1 \quad \Rightarrow \quad t_s = -1 +``` + +We deduce: +```math +t_0 = 2 * t_s = -2 +``` + +### Final solution: +- Switching time: \( t_s = -1 \) +- Initial time: \( t_0 = -2 \) + +Control: +```math +u(t) = 1 \quad \text{on} \quad [-2, -1] \\ +u(t) = -1 \quad \text{on} \quad [-1, 0] +``` +```@raw html +
+``` +## An example with both final and inital times being free: +```@example both_time +using OptimalControl +using NLPModelsIpopt +using BenchmarkTools +using DataFrames +using Plots +using Printf + +function double_integrator_freet0tf() + @def ocp begin + v=(t0, tf) ∈ R², variable + t ∈ [t0, tf], time + x ∈ R², state + u ∈ R, control + -1 ≤ u(t) ≤ 1 + x(t0) == [0, 0] + x(tf) == [1, 0] + 0.05 ≤ t0 ≤ 10 + 0.05 ≤ tf ≤ 10 + 0.01 ≤ tf - t0 ≤ Inf + ẋ(t) == [x₂(t), u(t)] + t0 → max + end + + return ocp +end +nothing # hide +``` + +# Direct resolution with both final and inital times being free: + + +We now solve the problem using a direct method, with automatic treatment of the free initial time. + +```@example both_time +ocp = double_integrator_freet0tf() +sol = solve(ocp; grid_size=100) +plot(sol; label="direct", size=(800, 800)) +``` + + +## A more concrete example about the change of orbit of a satellite: + +```@example orbit +using OptimalControl +using NLPModelsIpopt +using BenchmarkTools +using DataFrames +using Plots +using Printf +using OrdinaryDiffEq +using NonlinearSolve +using DifferentiationInterface +import ForwardDiff +using LinearAlgebra +using NLsolve + + +const x₁₀ = -42272.67 # initial position x +const x₂₀ = 0 # initial position y +const x₃₀ = 0 # initial velocity in x +const x₄₀ = -5696.72 # initial velocity in y +const μ = 5.1658620912*1e12 # gravitational parameter +const γ_max = 0.05 # maximal thrust norm +const r_f = 42165 # target orbit radius (final distance to origin) +const rf3 = r_f^3 +const α = sqrt(μ/rf3); + +function min_orbit_tf() + @def ocp begin + tf ∈ R, variable + t ∈ [0, tf], time + x ∈ R⁴, state + u ∈ R², control + u₁(t)^2 + u₂(t)^2 ≤ γ_max^2 + x(0) == [x₁₀, x₂₀, x₃₀, x₄₀] + x₁(tf)^2 + x₂(tf)^2 == r_f^2 + 0.05 ≤ tf ≤ Inf + ẋ(t) == [ + x₃(t), + x₄(t), + -μ * x₁(t) / ((x₁(t)^2 + x₂(t)^2)^(3/2)) + u₁(t), + -μ * x₂(t) / ((x₁(t)^2 + x₂(t)^2)^(3/2)) + u₂(t) + ] + tf → min + end + + return ocp +end +nothing # hide +``` + +# Direct resolution with minimal orbital time : + +We now solve the problem using a direct method, with automatic treatment of the free initial time. + +```@example orbit +ocp = min_orbit_tf() +sol = solve(ocp;init=(variable=13.4,), grid_size=100) +plot(sol; label="direct", size=(800, 800)) +``` +# Indirect resolution with minimal orbital time : + + +## Hamiltonian and optimal control + +From the Pontryagin Maximum Principle, the optimal control is bang-bang: + +```@example orbit +ocp = min_orbit_tf() + +function optimal_control(p) + p₃, p₄ = p[3], p[4] + p_norm = sqrt(p₃^2 + p₄^2) + + if p_norm > 1e-10 + u₁ = γ_max * p₃ / p_norm + u₂ = γ_max * p₄ / p_norm + return [u₁, u₂] + else + return [0.0, 0.0] + end +end + +function hamiltonian(x, p, u) + x₁, x₂, x₃, x₄ = x + p₁, p₂, p₃, p₄ = p + u₁, u₂ = u + + r = sqrt(x₁^2 + x₂^2) + r³ = r^3 + + return (p₁*x₃ + p₂*x₄ + + p₃*(-μ*x₁/r³ + u₁) + + p₄*(-μ*x₂/r³ + u₂)) +end +``` + +## Augmented dynamics +```@raw html +
+Click to show/hide Augmented dynamics code +``` +We define the combined state-costate system: + +```@example orbit +function augmented_dynamics!(dx, x_aug, params, t) + x = x_aug[1:4] + p = x_aug[5:8] + + x₁, x₂, x₃, x₄ = x + p₁, p₂, p₃, p₄ = p + + r = sqrt(x₁^2 + x₂^2) + r³ = r^3 + r⁵ = r^5 + + u = optimal_control(p) + u₁, u₂ = u + + # State dynamics (unchanged) + dx[1] = x₃ + dx[2] = x₄ + dx[3] = -μ*x₁/r³ + u₁ + dx[4] = -μ*x₂/r³ + u₂ + + # CORRECTED Costate dynamics: ṗ = -∂H/∂x + dx[5] = -(p₃ * μ * (3*x₁^2/r⁵ - 1/r³) + p₄ * μ * (3*x₁*x₂/r⁵)) # -∂H/∂x₁ + dx[6] = -(p₃ * μ * (3*x₁*x₂/r⁵) + p₄ * μ * (3*x₂^2/r⁵ - 1/r³)) # -∂H/∂x₂ + dx[7] = -p₁ # -∂H/∂x₃ + dx[8] = -p₂ # -∂H/∂x₄ +end + +function create_flow() + function flow_func(t_span, x0, p0) + x_aug0 = vcat(x0, p0) + prob = ODEProblem(augmented_dynamics!, x_aug0, t_span) + sol = solve(prob, Tsit5(), reltol=1e-12, abstol=1e-12) + return sol + end + return flow_func +end + +flow = create_flow() +``` +```@raw html +
+``` +## Shooting function + +The shooting function encodes the boundary conditions: + +```@example orbit +function shooting_function!(s, ξ) + p0 = ξ[1:4] + tf = ξ[5] + + x0 = [x₁₀, x₂₀, x₃₀, x₄₀] + + try + sol = flow((0.0, tf), x0, p0) + + x_final = sol.u[end][1:4] + p_final = sol.u[end][5:8] + + x₁f, x₂f, x₃f, x₄f = x_final + p₁f, p₂f, p₃f, p₄f = p_final + + s[1] = x₁f^2 + x₂f^2 - r_f^2 + s[2] = p₁f + s[3] = p₂f + + u_final = optimal_control(p_final) + H_final = hamiltonian(x_final, p_final, u_final) + s[4] = H_final + + s[5] = p₁f^2 + p₂f^2 + p₃f^2 + p₄f^2 - 1.0 + + catch e + fill!(s, 1e6) + end + + return nothing +end +``` + +## Initial guess and solver setup + +```@example orbit +# Initial guess for the shooting variables [p₀, tf] + +function get_initial_guess() + tf_guess = 13.4 + + p0_guess = [1.0323e-4, 4.915e-5, 3.568e-4, -1.554e-4] + + return vcat(p0_guess, tf_guess) +end + +ξ_init = get_initial_guess() +println("Initial guess: p₀ = $(ξ_init[1:4]), tf = $(ξ_init[5])") +``` + +## Solve the shooting problem + +```@example orbit +# Solve the shooting problem using NLsolve +function solve_indirect_method() + println("Solving indirect method...") + + # Set up the shooting problem + result = nlsolve(shooting_function!, ξ_init, + method=:trust_region, + ftol=1e-10, + xtol=1e-10, + iterations=1000, + show_trace=false) + + if result.f_converged || result.x_converged + println("Shooting method converged!") + println("Final residual norm: $(norm(result.zero))") + return result.zero + else + println("Shooting method failed to converge") + println("Final residual norm: $(norm(result.zero))") + return nothing + end +end + +ξ_solution = solve_indirect_method() + +if ξ_solution !== nothing + p0_opt = ξ_solution[1:4] + tf_opt = ξ_solution[5] + + println("\nOptimal solution:") + println("p₀ = $(p0_opt)") + println("tf = $(tf_opt)") + + # Verify the solution + s_check = zeros(5) + shooting_function!(s_check, ξ_solution) + println("Shooting function residual: $(s_check)") +end +``` + +## Generate the indirect solution trajectory + +```@example orbit +if ξ_solution !== nothing + # Generate the optimal trajectory + x0 = [x₁₀, x₂₀, x₃₀, x₄₀] + p0_opt = ξ_solution[1:4] + tf_opt = ξ_solution[5] + + # Solve the optimal trajectory + sol_indirect = flow((0.0, tf_opt), x0, p0_opt) + + # Extract time points for plotting + t_indirect = range(0, tf_opt, length=1000) + + # Evaluate solution at time points + x_traj = zeros(length(t_indirect), 4) + p_traj = zeros(length(t_indirect), 4) + u_traj = zeros(length(t_indirect), 2) + + for (i, t) in enumerate(t_indirect) + state_full = sol_indirect(t) + x_traj[i, :] = state_full[1:4] + p_traj[i, :] = state_full[5:8] + u_traj[i, :] = optimal_control(state_full[5:8]) + end + + println("Indirect solution generated successfully!") + println("Final time: $(tf_opt)") + println("Final position: $(x_traj[end, 1:2])") + println("Final radius: $(sqrt(x_traj[end, 1]^2 + x_traj[end, 2]^2))") +end +``` +# Visualistion of results +```@raw html +
+Click to show/hide indirect method visualization code +``` + +```@example orbit + + +# Simple visualization - just the basic plots +if ξ_solution !== nothing + p0_opt = ξ_solution[1:4] + tf_opt = ξ_solution[5] + + x0 = [x₁₀, x₂₀, x₃₀, x₄₀] + sol_indirect = flow((0.0, tf_opt), x0, p0_opt) + + t_indirect = range(0, tf_opt, length=1000) + + x_traj = zeros(length(t_indirect), 4) + p_traj = zeros(length(t_indirect), 4) + u_traj = zeros(length(t_indirect), 2) + + for (i, t) in enumerate(t_indirect) + state_full = sol_indirect(t) + x_traj[i, :] = state_full[1:4] + p_traj[i, :] = state_full[5:8] + u_traj[i, :] = optimal_control(state_full[5:8]) + end + + # Combined plot with 3 rows + plt = plot(layout=(3,1), size=(800, 900)) + + plot!(plt[1], t_indirect, [x_traj[:,1] x_traj[:,2] x_traj[:,3] x_traj[:,4]], + label=["x₁" "x₂" "x₃" "x₄"], title="States") + + plot!(plt[2], t_indirect, [p_traj[:,1] p_traj[:,2] p_traj[:,3] p_traj[:,4]], + label=["p₁" "p₂" "p₃" "p₄"], title="Costates") + + plot!(plt[3], t_indirect, [u_traj[:,1] u_traj[:,2]], + label=["u₁" "u₂"], title="Control") + + plt # This returns the plot for documentation +end +``` +```@raw html +
+``` \ No newline at end of file From fb325ac20d6454aa7a2c8f43e6f6fb057addeca6 Mon Sep 17 00:00:00 2001 From: yassin moukan Date: Fri, 27 Jun 2025 15:28:14 +0200 Subject: [PATCH 17/20] modified the structure of the different tutorials --- docs/src/tutorial-free-times-final-initial.md | 751 +--------------- docs/src/tutorial-free-times-final.md | 43 +- docs/src/tutorial-free-times-initial.md | 830 ++---------------- docs/src/tutorial-free-times-orbital.md | 468 +--------- 4 files changed, 115 insertions(+), 1977 deletions(-) diff --git a/docs/src/tutorial-free-times-final-initial.md b/docs/src/tutorial-free-times-final-initial.md index d45d32b..784c276 100644 --- a/docs/src/tutorial-free-times-final-initial.md +++ b/docs/src/tutorial-free-times-final-initial.md @@ -1,438 +1,25 @@ ```@meta -Draft = true +Draft = false ``` -# [Free Initial and Final Times](@id tutorial-free-times) +# [Optimal control problem with free initial and free final times](@id tutorial-free-times-final-initial) -In this tutorial, we explore optimal control problems with free initial time `t₀` and/or final time `t_f`. These problems require special treatment in both direct and indirect methods, particularly when handling time-varying constraints and objectives. +In this tutorial, we explore optimal control problems with free initial time `t₀` and final time `t_f`. -```@example main-disc -using OptimalControl -using NLPModelsIpopt -using BenchmarkTools -using DataFrames -using Plots -using Printf - -function double_integrator_mintf() - @def ocp begin - tf ∈ R, variable - t ∈ [0, tf], time - x ∈ R², state - u ∈ R, control - -1 ≤ u(t) ≤ 1 - x(0) == [0, 0] - x(tf) == [1, 0] - 0.05 ≤ tf ≤ Inf - ẋ(t) == [x₂(t), u(t)] - tf → min - end - return ocp -end -nothing # hide -``` - -To allow the use of a free final time, you define t0 and/or tf as variables, and then set the global time t between these two variables. Here is a complete example : - -```julia -v=(t0, tf) ∈ R², variable -t ∈ [t0, tf], time -``` - -You may need to add this type of constrain in your problem definition : - -```julia -0.05 ≤ tf ≤ Inf -``` - -It is to ensure and help the convergence of the algorithm depending on your problem and it can also be needed when dealing with problems having a free initial time, as we will see in the next example. - -## Direct resolution with free final time : - -We now solve the problem using a direct method, with automatic treatment of the free final time. - -```@example main-disc -ocp = double_integrator_mintf() -sol = solve(ocp; grid_size=100) -plot(sol; label="direct", size=(800, 800)) -``` - -# Verification of results -```@raw html -
- Click to show/hide mathematical verification. -``` - -Here the theorical part : -```math -H = p1*x2 + p2*u -``` -Conditions of Pontryagin's theorem : -```math -p1' = 0, \quad p2' = -p1 \\ -p1 = p1(0) = constante = 1\\ -p2 = -p1*t + p2(0) = 1-t\\ -``` -Transversal conditions : -```math -H[tf] = -p° = 1 -``` -We find u : -```math -u(t) = 1 ∈ [0,t1] \\ -u(t) = -1 ∈ [t1, tf] -``` -Where t1 is a constant to determined. - -Now we have to integrate x' : - -On t ∈ [0,t1] : -```math -x1' = x2 \\ -x2'= u = 1 \\ -``` - -```math -x2(t) = t \\ -x1(t) = (1/2)*t^2 -``` - -When t = t1 : -```math -x2(t1) = t1 \\ -x1(t1) = (1/2)*t1^2 -``` - -On t ∈ [t1,tf] -```math -x2(t) = -t + 2*t1 \\ -x1(t) = -(1/2)*t^2 + 2*t1*t + C2 \\ -``` - -```math -x1(t1) = -(1/2)*t1^2 + 2*t1^2 + C2 = (1/2)*t1^2 \\ -C2 = -t1^2 \\ -``` - -```math -x1(t) = -(1/2)*t^2 + 2*t1*t - t1^2 -``` - -Finally you can solve the terminal conditions : -```math -x1(tf) = 1, \quad x2(tf) = 0 \\ -``` - -```math -x2(tf) = -tf + 2*t1 = 0 \\ -tf = 2*t1 \\ -``` - -```math -x1(tf) = -(1/2)*tf^2 + 2*t1*tf - t1^2 = 1 \\ --2*t1^2 + 4*t1 - t1^2 = t1 = 1 \\ -``` - -```math -t1 = 1, \quad t2 = 2 -``` - -To sum up we find the following solutions : -```math -x1(t) = \begin{cases} -(1/2)*t^2 & \text{si } t \in [0, 1) \\ --(1/2)*t^2 + 2*t - 1 & \text{si } t \in [1, 2] -\end{cases} -\qquad -x2(t) = \begin{cases} -t & \text{si } t \in [0, 1) \\ -2 - t & \text{si } t \in [1, 2] -\end{cases} -``` - -```math -p1(t) = 1, \quad p2(t) = 1-t, \quad p°=-1 -``` +## Here is the required packages for the tutorial: -```math -u(t) = \begin{cases} -1 & \text{si } t \in [0, 1) \\ --1 & \text{si } t \in [1, 2] -\end{cases} -``` -```@raw html -
-``` - - -Now we can compare the results found with the direct method whith the theoritical analysis : -```@example main-disc -tf = variable(sol) -u = control(sol) -p = costate(sol) -x = state(sol) -p° = -1 - -xf = x(tf) -pf = p(tf) - -Htf = pf[1]*xf[2] + pf[2]*u(tf) -@printf("H(tf) = %.3f\n", Htf) -@printf("x(tf) = [%.3f, %.3f]\n", xf[1], xf[2]) -@printf("p(tf) = [%.3f, %.3f]\n", pf[1], pf[2]) -``` - -The numerical results closely match the theoretical predictions: the final state x(tf)=[1,0] is exactly satisfied. -The costate and Hamiltonian values at final time show a small deviation (≈ 0.01), likely due to numerical precision. -Overall, the direct method confirms the theoretical analysis with excellent accuracy. - -We can analyse the influence of using different discretization sizes (grid_size), and observed the following results for the optimal tf: - -```@example main-disc -for N in [20, 50, 100, 200] - solN = solve(ocp; grid_size=N, display=false) - @printf("grid_size = %3d → tf = %.5f\n", N, objective(solN)) -end -``` - -This example shows that problems with a free final time can be sensitive to discretization. A small grid may lead to suboptimal or slightly inaccurate results. - -# Direct resolution with free initial time : - -We keep the structure of the solution found with the direct method -```@example main-disc -t = time_grid(sol) -x = state(sol) -u = control(sol) -p = costate(sol) - -H(x,p,u,t) = p[1](t)*x[2](t) + p[2](t)*u(u) -H(xf, pf, tf) = pf[1]*xf[2] + pf[2]*u(tf) - -# Hamiltonian vector field -F1(x) = [0, 1] -H1 = Lift(F1) -φ(t) = H1(x(t), p(t)) # switching function -``` - - -First, lets define the different flows -```@example main-disc -using OrdinaryDiffEq # to get the Flow function from OptimalControl - -const u1 = 1 -const u0 = -1 - -f1 = Flow(ocp, (x, p, tf) -> u1) -f0 = Flow(ocp, (x, p, tf) -> u0) -``` - -And with this we have the following shooting function -```@example main-disc -x0 = [0.0, 0.0] # état initial -xf_target = [1.0, 0.0] # état final - -function shoot!(s, p0, t1, tf) -x1, p1 = f1(0.0, x0, p0, t1) -xf, pf = f0(t1, x1, p1, tf) - -# Conditions de tir -s[1:2] = xf .- xf_target # état final -s[3] = H(xf, pf, tf) - 1 # condition de transversalité H(tf) = 1 -s[4] = H1(x1, p1) # φ = 0 au switch -end -``` - -Before solving our problem we must find a good initial guess to help the convergence of the algorithm -```@example main-disc -p0 = p(0.0) -φ_arr = abs.(φ.(t)) -t1 = t[argmin(φ_arr)] -tf = t[end] - -println("p0 ≈ ", p0) -println("t1 ≈ ", t1) -println("tf ≈ ", tf) - -s = zeros(4) -shoot!(s, p0, t1, tf) - -ξ = [p0..., t1, tf] -``` - -And we finally can solve the problem using an indirect method -```@example main-disc -using NonlinearSolve -using DifferentiationInterface -import ForwardDiff - -backend = AutoForwardDiff() - -struct MYSOL - x::Vector{Float64} -end - -function fsolve(f, j, x; kwargs...) - try - MINPACK.fsolve(f, j, x; kwargs...) - catch e - println("MINPACK error:") - println(e) - println("→ Using NonlinearSolve fallback") - - # Wrap pour respecter l'interface de NonlinearProblem - function wrapped_f(s, ξ, p) - f(s, ξ) - return nothing - end - - prob = NonlinearProblem(wrapped_f, x) - sol = solve(prob; abstol=1e-8, reltol=1e-8) - return MYSOL(sol.u) - end -end - -# Agrégation du problème -nle! = (s, ξ) -> shoot!(s, ξ[1:2], ξ[3], ξ[4]) -jnle! = (js, ξ) -> jacobian!(nle!, similar(ξ), js, backend, ξ) - -ξ0 = [p0... , t1, tf] -indirect_sol = fsolve(nle!, jnle!, ξ0) - -p0 = indirect_sol.x[1:2] -t1 = indirect_sol.x[3] -tf = indirect_sol.x[4] - -f = f1 * (t1, f0) -flow_sol = f((0.0, tf), x0, p0) - -plot!(flow_sol, label="indirect", color=:red) -``` - -# Now we will try an example with a free initial time - -```@example initial_time +```@example both_time using OptimalControl using NLPModelsIpopt using BenchmarkTools using DataFrames using Plots using Printf - -function double_integrator_mint0() - @def ocp begin - t0 ∈ R, variable - tf=0 - t ∈ [t0, tf], time - x ∈ R², state - u ∈ R, control - -1 ≤ u(t) ≤ 1 - x(t0) == [0, 0] - x(tf) == [1, 0] - 0.05 ≤ -t0 ≤ Inf - ẋ(t) == [x₂(t), u(t)] - -t0 → min - end - return ocp -end -nothing # hide ``` +## Definition of the problem -# Direct resolution with free initial time : - -We now solve the problem using a direct method, with automatic treatment of the free initial time. - -```@example initial_time -ocp = double_integrator_mint0() -sol = solve(ocp; grid_size=100) -plot(sol; label="direct", size=(800, 800)) -``` - - - -## Verification of results -```@raw html -
- Click to show/hide mathematical verification. -``` -Here is the theoretical part using Pontryagin's Maximum Principle: -```math -H = p_1 x_2 + p_2 u + 1 -``` - -Conditions from Pontryagin’s theorem: -```math -p_1' = 0 \quad \Rightarrow \quad p_1 = c_1 \quad (\text{constant}) \\ -p_2' = -p_1 \quad \Rightarrow \quad p_2 = -c_1 t + c_2 -``` - -Switching condition: -```math -p_2(t_s) = 0 \quad \Rightarrow \quad c_2 = c_1 t_s -``` - -Optimal control: -```math -u(t) = 1 \quad \text{on} \quad [t_0, t_s] \\ -u(t) = -1 \quad \text{on} \quad [t_s, 0] -``` - -Now we integrate the system: - -On \( t \in [t_0, t_s] \) : -```math -x_2' = u = 1 \quad \Rightarrow \quad x_2(t) = t - t_0 \\ -x_1' = x_2 \quad \Rightarrow \quad x_1(t) = \frac{(t - t_0)^2}{2} -``` - -At switching time \( t = t_s \) : -```math -x_2(t_s) = t_s - t_0 \\ -x_1(t_s) = \frac{(t_s - t_0)^2}{2} -``` - -On \( t \in [t_s, 0] \) : -```math -x_2' = u = -1 \quad \Rightarrow \quad x_2(t) = x_2(t_s) - (t - t_s) \\ -x_1' = x_2 \quad \Rightarrow \quad x_1(t) = x_1(t_s) + \int_{t_s}^t x_2(s) ds -``` - -Final velocity condition: -```math -x_2(0) = 0 \quad \Rightarrow \quad t_s - t_0 + t_s = 0 \quad \Rightarrow \quad t_0 = 2 t_s -``` - -Final position: -```math -x_1(0) = x_1(t_s) + \frac{t_s^2}{2} \quad \Rightarrow \quad x_1(0) = t_s^2 = 1 \quad \Rightarrow \quad t_s = -1 -``` - -We deduce: -```math -t_0 = 2 * t_s = -2 -``` - -### Final solution: -- Switching time: \( t_s = -1 \) -- Initial time: \( t_0 = -2 \) - -Control: -```math -u(t) = 1 \quad \text{on} \quad [-2, -1] \\ -u(t) = -1 \quad \text{on} \quad [-1, 0] -``` -```@raw html -
-``` -## An example with both final and inital times being free: ```@example both_time -using OptimalControl -using NLPModelsIpopt -using BenchmarkTools -using DataFrames -using Plots -using Printf function double_integrator_freet0tf() @def ocp begin @@ -463,333 +50,11 @@ We now solve the problem using a direct method, with automatic treatment of the ```@example both_time ocp = double_integrator_freet0tf() sol = solve(ocp; grid_size=100) -plot(sol; label="direct", size=(800, 800)) ``` +And plot the solution. - -## A more concrete example about the change of orbit of a satellite: - -```@example orbit -using OptimalControl -using NLPModelsIpopt -using BenchmarkTools -using DataFrames -using Plots -using Printf -using OrdinaryDiffEq -using NonlinearSolve -using DifferentiationInterface -import ForwardDiff -using LinearAlgebra -using NLsolve - - -const x₁₀ = -42272.67 # initial position x -const x₂₀ = 0 # initial position y -const x₃₀ = 0 # initial velocity in x -const x₄₀ = -5696.72 # initial velocity in y -const μ = 5.1658620912*1e12 # gravitational parameter -const γ_max = 0.05 # maximal thrust norm -const r_f = 42165 # target orbit radius (final distance to origin) -const rf3 = r_f^3 -const α = sqrt(μ/rf3); - -function min_orbit_tf() - @def ocp begin - tf ∈ R, variable - t ∈ [0, tf], time - x ∈ R⁴, state - u ∈ R², control - u₁(t)^2 + u₂(t)^2 ≤ γ_max^2 - x(0) == [x₁₀, x₂₀, x₃₀, x₄₀] - x₁(tf)^2 + x₂(tf)^2 == r_f^2 - 0.05 ≤ tf ≤ Inf - ẋ(t) == [ - x₃(t), - x₄(t), - -μ * x₁(t) / ((x₁(t)^2 + x₂(t)^2)^(3/2)) + u₁(t), - -μ * x₂(t) / ((x₁(t)^2 + x₂(t)^2)^(3/2)) + u₂(t) - ] - tf → min - end - - return ocp -end -nothing # hide -``` - -# Direct resolution with minimal orbital time : - -We now solve the problem using a direct method, with automatic treatment of the free initial time. - -```@example orbit -ocp = min_orbit_tf() -sol = solve(ocp;init=(variable=13.4,), grid_size=100) +```@example both_time plot(sol; label="direct", size=(800, 800)) ``` -# Indirect resolution with minimal orbital time : - - -## Hamiltonian and optimal control - -From the Pontryagin Maximum Principle, the optimal control is bang-bang: - -```@example orbit -ocp = min_orbit_tf() - -function optimal_control(p) - p₃, p₄ = p[3], p[4] - p_norm = sqrt(p₃^2 + p₄^2) - - if p_norm > 1e-10 - u₁ = γ_max * p₃ / p_norm - u₂ = γ_max * p₄ / p_norm - return [u₁, u₂] - else - return [0.0, 0.0] - end -end - -function hamiltonian(x, p, u) - x₁, x₂, x₃, x₄ = x - p₁, p₂, p₃, p₄ = p - u₁, u₂ = u - - r = sqrt(x₁^2 + x₂^2) - r³ = r^3 - - return (p₁*x₃ + p₂*x₄ + - p₃*(-μ*x₁/r³ + u₁) + - p₄*(-μ*x₂/r³ + u₂)) -end -``` - -## Augmented dynamics -```@raw html -
-Click to show/hide Augmented dynamics code -``` -We define the combined state-costate system: - -```@example orbit -function augmented_dynamics!(dx, x_aug, params, t) - x = x_aug[1:4] - p = x_aug[5:8] - - x₁, x₂, x₃, x₄ = x - p₁, p₂, p₃, p₄ = p - - r = sqrt(x₁^2 + x₂^2) - r³ = r^3 - r⁵ = r^5 - - u = optimal_control(p) - u₁, u₂ = u - - # State dynamics (unchanged) - dx[1] = x₃ - dx[2] = x₄ - dx[3] = -μ*x₁/r³ + u₁ - dx[4] = -μ*x₂/r³ + u₂ - - # CORRECTED Costate dynamics: ṗ = -∂H/∂x - dx[5] = -(p₃ * μ * (3*x₁^2/r⁵ - 1/r³) + p₄ * μ * (3*x₁*x₂/r⁵)) # -∂H/∂x₁ - dx[6] = -(p₃ * μ * (3*x₁*x₂/r⁵) + p₄ * μ * (3*x₂^2/r⁵ - 1/r³)) # -∂H/∂x₂ - dx[7] = -p₁ # -∂H/∂x₃ - dx[8] = -p₂ # -∂H/∂x₄ -end - -function create_flow() - function flow_func(t_span, x0, p0) - x_aug0 = vcat(x0, p0) - prob = ODEProblem(augmented_dynamics!, x_aug0, t_span) - sol = solve(prob, Tsit5(), reltol=1e-12, abstol=1e-12) - return sol - end - return flow_func -end - -flow = create_flow() -``` -```@raw html -
-``` -## Shooting function - -The shooting function encodes the boundary conditions: - -```@example orbit -function shooting_function!(s, ξ) - p0 = ξ[1:4] - tf = ξ[5] - - x0 = [x₁₀, x₂₀, x₃₀, x₄₀] - try - sol = flow((0.0, tf), x0, p0) - x_final = sol.u[end][1:4] - p_final = sol.u[end][5:8] - - x₁f, x₂f, x₃f, x₄f = x_final - p₁f, p₂f, p₃f, p₄f = p_final - - s[1] = x₁f^2 + x₂f^2 - r_f^2 - s[2] = p₁f - s[3] = p₂f - - u_final = optimal_control(p_final) - H_final = hamiltonian(x_final, p_final, u_final) - s[4] = H_final - - s[5] = p₁f^2 + p₂f^2 + p₃f^2 + p₄f^2 - 1.0 - - catch e - fill!(s, 1e6) - end - - return nothing -end -``` - -## Initial guess and solver setup - -```@example orbit -# Initial guess for the shooting variables [p₀, tf] - -function get_initial_guess() - tf_guess = 13.4 - - p0_guess = [1.0323e-4, 4.915e-5, 3.568e-4, -1.554e-4] - - return vcat(p0_guess, tf_guess) -end - -ξ_init = get_initial_guess() -println("Initial guess: p₀ = $(ξ_init[1:4]), tf = $(ξ_init[5])") -``` - -## Solve the shooting problem - -```@example orbit -# Solve the shooting problem using NLsolve -function solve_indirect_method() - println("Solving indirect method...") - - # Set up the shooting problem - result = nlsolve(shooting_function!, ξ_init, - method=:trust_region, - ftol=1e-10, - xtol=1e-10, - iterations=1000, - show_trace=false) - - if result.f_converged || result.x_converged - println("Shooting method converged!") - println("Final residual norm: $(norm(result.zero))") - return result.zero - else - println("Shooting method failed to converge") - println("Final residual norm: $(norm(result.zero))") - return nothing - end -end - -ξ_solution = solve_indirect_method() - -if ξ_solution !== nothing - p0_opt = ξ_solution[1:4] - tf_opt = ξ_solution[5] - - println("\nOptimal solution:") - println("p₀ = $(p0_opt)") - println("tf = $(tf_opt)") - - # Verify the solution - s_check = zeros(5) - shooting_function!(s_check, ξ_solution) - println("Shooting function residual: $(s_check)") -end -``` - -## Generate the indirect solution trajectory - -```@example orbit -if ξ_solution !== nothing - # Generate the optimal trajectory - x0 = [x₁₀, x₂₀, x₃₀, x₄₀] - p0_opt = ξ_solution[1:4] - tf_opt = ξ_solution[5] - - # Solve the optimal trajectory - sol_indirect = flow((0.0, tf_opt), x0, p0_opt) - - # Extract time points for plotting - t_indirect = range(0, tf_opt, length=1000) - - # Evaluate solution at time points - x_traj = zeros(length(t_indirect), 4) - p_traj = zeros(length(t_indirect), 4) - u_traj = zeros(length(t_indirect), 2) - - for (i, t) in enumerate(t_indirect) - state_full = sol_indirect(t) - x_traj[i, :] = state_full[1:4] - p_traj[i, :] = state_full[5:8] - u_traj[i, :] = optimal_control(state_full[5:8]) - end - - println("Indirect solution generated successfully!") - println("Final time: $(tf_opt)") - println("Final position: $(x_traj[end, 1:2])") - println("Final radius: $(sqrt(x_traj[end, 1]^2 + x_traj[end, 2]^2))") -end -``` -# Visualistion of results -```@raw html -
-Click to show/hide indirect method visualization code -``` - -```@example orbit - - -# Simple visualization - just the basic plots -if ξ_solution !== nothing - p0_opt = ξ_solution[1:4] - tf_opt = ξ_solution[5] - - x0 = [x₁₀, x₂₀, x₃₀, x₄₀] - sol_indirect = flow((0.0, tf_opt), x0, p0_opt) - - t_indirect = range(0, tf_opt, length=1000) - - x_traj = zeros(length(t_indirect), 4) - p_traj = zeros(length(t_indirect), 4) - u_traj = zeros(length(t_indirect), 2) - - for (i, t) in enumerate(t_indirect) - state_full = sol_indirect(t) - x_traj[i, :] = state_full[1:4] - p_traj[i, :] = state_full[5:8] - u_traj[i, :] = optimal_control(state_full[5:8]) - end - - # Combined plot with 3 rows - plt = plot(layout=(3,1), size=(800, 900)) - - plot!(plt[1], t_indirect, [x_traj[:,1] x_traj[:,2] x_traj[:,3] x_traj[:,4]], - label=["x₁" "x₂" "x₃" "x₄"], title="States") - - plot!(plt[2], t_indirect, [p_traj[:,1] p_traj[:,2] p_traj[:,3] p_traj[:,4]], - label=["p₁" "p₂" "p₃" "p₄"], title="Costates") - - plot!(plt[3], t_indirect, [u_traj[:,1] u_traj[:,2]], - label=["u₁" "u₂"], title="Control") - - plt # This returns the plot for documentation -end -``` -```@raw html -
-``` \ No newline at end of file diff --git a/docs/src/tutorial-free-times-final.md b/docs/src/tutorial-free-times-final.md index 694a04b..cc5b1d2 100644 --- a/docs/src/tutorial-free-times-final.md +++ b/docs/src/tutorial-free-times-final.md @@ -142,57 +142,48 @@ plt = plot(sol; label="direct", size=(800, 800)) x1(t1) = (1/2)*t1^2 ``` - On t ∈ [t1,tf] + On $t \in [t_1,t_f]$ ```math - x2(t) = -t + 2*t1 \\ - x1(t) = -(1/2)*t^2 + 2*t1*t + C2 \\ + x_2(t) = -t + 2t_1 \\ + x_1(t) = -\frac{1}{2}t^2 + 2t_1 t + C_2 \\ ``` - ```math - x1(t1) = -(1/2)*t1^2 + 2*t1^2 + C2 = (1/2)*t1^2 \\ - C2 = -t1^2 \\ + x_1(t_1) = -\frac{1}{2}t_1^2 + 2t_1^2 + C_2 = \frac{1}{2}t_1^2 \\ + C_2 = -t_1^2 \\ ``` - ```math - x1(t) = -(1/2)*t^2 + 2*t1*t - t1^2 + x_1(t) = -\frac{1}{2}t^2 + 2t_1 t - t_1^2 ``` - Finally you can solve the terminal conditions : ```math - x1(tf) = 1, \quad x2(tf) = 0 \\ + x_1(t_f) = 1, \quad x_2(t_f) = 0 \\ ``` - ```math - x2(tf) = -tf + 2*t1 = 0 \\ - tf = 2*t1 \\ + x_2(t_f) = -t_f + 2t_1 = 0 \\ + t_f = 2t_1 \\ ``` - ```math - x1(tf) = -(1/2)*tf^2 + 2*t1*tf - t1^2 = 1 \\ - -2*t1^2 + 4*t1 - t1^2 = t1 = 1 \\ + x_1(t_f) = -\frac{1}{2}t_f^2 + 2t_1 t_f - t_1^2 = 1 \\ + -2t_1^2 + 4t_1^2 - t_1^2 = t_1^2 = 1 \\ ``` - ```math - t1 = 1, \quad t2 = 2 + t_1 = 1, \quad t_f = 2 ``` - To sum up we find the following solutions : ```math - x1(t) = \begin{cases} - (1/2)*t^2 & \text{si } t \in [0, 1) \\ - -(1/2)*t^2 + 2*t - 1 & \text{si } t \in [1, 2] + x_1(t) = \begin{cases} + \frac{1}{2}t^2 & \text{si } t \in [0, 1) \\ + -\frac{1}{2}t^2 + 2t - 1 & \text{si } t \in [1, 2] \end{cases} \qquad - x2(t) = \begin{cases} + x_2(t) = \begin{cases} t & \text{si } t \in [0, 1) \\ 2 - t & \text{si } t \in [1, 2] \end{cases} ``` - ```math - p1(t) = 1, \quad p2(t) = 1-t, \quad p°=-1 + p_1(t) = 1, \quad p_2(t) = 1-t, \quad p^0=-1 ``` - ```math u(t) = \begin{cases} 1 & \text{si } t \in [0, 1) \\ diff --git a/docs/src/tutorial-free-times-initial.md b/docs/src/tutorial-free-times-initial.md index d45d32b..d0ad5b0 100644 --- a/docs/src/tutorial-free-times-initial.md +++ b/docs/src/tutorial-free-times-initial.md @@ -1,316 +1,14 @@ ```@meta -Draft = true +Draft = false ``` -# [Free Initial and Final Times](@id tutorial-free-times) +# [Optimal control problem with free initial time](@id tutorial-free-times-initial) -In this tutorial, we explore optimal control problems with free initial time `t₀` and/or final time `t_f`. These problems require special treatment in both direct and indirect methods, particularly when handling time-varying constraints and objectives. -```@example main-disc -using OptimalControl -using NLPModelsIpopt -using BenchmarkTools -using DataFrames -using Plots -using Printf - -function double_integrator_mintf() - @def ocp begin - tf ∈ R, variable - t ∈ [0, tf], time - x ∈ R², state - u ∈ R, control - -1 ≤ u(t) ≤ 1 - x(0) == [0, 0] - x(tf) == [1, 0] - 0.05 ≤ tf ≤ Inf - ẋ(t) == [x₂(t), u(t)] - tf → min - end - return ocp -end -nothing # hide -``` - -To allow the use of a free final time, you define t0 and/or tf as variables, and then set the global time t between these two variables. Here is a complete example : - -```julia -v=(t0, tf) ∈ R², variable -t ∈ [t0, tf], time -``` - -You may need to add this type of constrain in your problem definition : - -```julia -0.05 ≤ tf ≤ Inf -``` - -It is to ensure and help the convergence of the algorithm depending on your problem and it can also be needed when dealing with problems having a free initial time, as we will see in the next example. - -## Direct resolution with free final time : - -We now solve the problem using a direct method, with automatic treatment of the free final time. - -```@example main-disc -ocp = double_integrator_mintf() -sol = solve(ocp; grid_size=100) -plot(sol; label="direct", size=(800, 800)) -``` - -# Verification of results -```@raw html -
- Click to show/hide mathematical verification. -``` - -Here the theorical part : -```math -H = p1*x2 + p2*u -``` -Conditions of Pontryagin's theorem : -```math -p1' = 0, \quad p2' = -p1 \\ -p1 = p1(0) = constante = 1\\ -p2 = -p1*t + p2(0) = 1-t\\ -``` -Transversal conditions : -```math -H[tf] = -p° = 1 -``` -We find u : -```math -u(t) = 1 ∈ [0,t1] \\ -u(t) = -1 ∈ [t1, tf] -``` -Where t1 is a constant to determined. - -Now we have to integrate x' : - -On t ∈ [0,t1] : -```math -x1' = x2 \\ -x2'= u = 1 \\ -``` - -```math -x2(t) = t \\ -x1(t) = (1/2)*t^2 -``` - -When t = t1 : -```math -x2(t1) = t1 \\ -x1(t1) = (1/2)*t1^2 -``` - -On t ∈ [t1,tf] -```math -x2(t) = -t + 2*t1 \\ -x1(t) = -(1/2)*t^2 + 2*t1*t + C2 \\ -``` - -```math -x1(t1) = -(1/2)*t1^2 + 2*t1^2 + C2 = (1/2)*t1^2 \\ -C2 = -t1^2 \\ -``` - -```math -x1(t) = -(1/2)*t^2 + 2*t1*t - t1^2 -``` - -Finally you can solve the terminal conditions : -```math -x1(tf) = 1, \quad x2(tf) = 0 \\ -``` - -```math -x2(tf) = -tf + 2*t1 = 0 \\ -tf = 2*t1 \\ -``` - -```math -x1(tf) = -(1/2)*tf^2 + 2*t1*tf - t1^2 = 1 \\ --2*t1^2 + 4*t1 - t1^2 = t1 = 1 \\ -``` - -```math -t1 = 1, \quad t2 = 2 -``` - -To sum up we find the following solutions : -```math -x1(t) = \begin{cases} -(1/2)*t^2 & \text{si } t \in [0, 1) \\ --(1/2)*t^2 + 2*t - 1 & \text{si } t \in [1, 2] -\end{cases} -\qquad -x2(t) = \begin{cases} -t & \text{si } t \in [0, 1) \\ -2 - t & \text{si } t \in [1, 2] -\end{cases} -``` - -```math -p1(t) = 1, \quad p2(t) = 1-t, \quad p°=-1 -``` - -```math -u(t) = \begin{cases} -1 & \text{si } t \in [0, 1) \\ --1 & \text{si } t \in [1, 2] -\end{cases} -``` -```@raw html -
-``` - - -Now we can compare the results found with the direct method whith the theoritical analysis : -```@example main-disc -tf = variable(sol) -u = control(sol) -p = costate(sol) -x = state(sol) -p° = -1 - -xf = x(tf) -pf = p(tf) - -Htf = pf[1]*xf[2] + pf[2]*u(tf) -@printf("H(tf) = %.3f\n", Htf) -@printf("x(tf) = [%.3f, %.3f]\n", xf[1], xf[2]) -@printf("p(tf) = [%.3f, %.3f]\n", pf[1], pf[2]) -``` - -The numerical results closely match the theoretical predictions: the final state x(tf)=[1,0] is exactly satisfied. -The costate and Hamiltonian values at final time show a small deviation (≈ 0.01), likely due to numerical precision. -Overall, the direct method confirms the theoretical analysis with excellent accuracy. - -We can analyse the influence of using different discretization sizes (grid_size), and observed the following results for the optimal tf: - -```@example main-disc -for N in [20, 50, 100, 200] - solN = solve(ocp; grid_size=N, display=false) - @printf("grid_size = %3d → tf = %.5f\n", N, objective(solN)) -end -``` - -This example shows that problems with a free final time can be sensitive to discretization. A small grid may lead to suboptimal or slightly inaccurate results. - -# Direct resolution with free initial time : - -We keep the structure of the solution found with the direct method -```@example main-disc -t = time_grid(sol) -x = state(sol) -u = control(sol) -p = costate(sol) - -H(x,p,u,t) = p[1](t)*x[2](t) + p[2](t)*u(u) -H(xf, pf, tf) = pf[1]*xf[2] + pf[2]*u(tf) - -# Hamiltonian vector field -F1(x) = [0, 1] -H1 = Lift(F1) -φ(t) = H1(x(t), p(t)) # switching function -``` - - -First, lets define the different flows -```@example main-disc -using OrdinaryDiffEq # to get the Flow function from OptimalControl - -const u1 = 1 -const u0 = -1 - -f1 = Flow(ocp, (x, p, tf) -> u1) -f0 = Flow(ocp, (x, p, tf) -> u0) -``` - -And with this we have the following shooting function -```@example main-disc -x0 = [0.0, 0.0] # état initial -xf_target = [1.0, 0.0] # état final - -function shoot!(s, p0, t1, tf) -x1, p1 = f1(0.0, x0, p0, t1) -xf, pf = f0(t1, x1, p1, tf) - -# Conditions de tir -s[1:2] = xf .- xf_target # état final -s[3] = H(xf, pf, tf) - 1 # condition de transversalité H(tf) = 1 -s[4] = H1(x1, p1) # φ = 0 au switch -end -``` - -Before solving our problem we must find a good initial guess to help the convergence of the algorithm -```@example main-disc -p0 = p(0.0) -φ_arr = abs.(φ.(t)) -t1 = t[argmin(φ_arr)] -tf = t[end] - -println("p0 ≈ ", p0) -println("t1 ≈ ", t1) -println("tf ≈ ", tf) - -s = zeros(4) -shoot!(s, p0, t1, tf) - -ξ = [p0..., t1, tf] -``` +## In this tutorial, we explore an optimal control problem with free initial time `t0`. -And we finally can solve the problem using an indirect method -```@example main-disc -using NonlinearSolve -using DifferentiationInterface -import ForwardDiff - -backend = AutoForwardDiff() - -struct MYSOL - x::Vector{Float64} -end - -function fsolve(f, j, x; kwargs...) - try - MINPACK.fsolve(f, j, x; kwargs...) - catch e - println("MINPACK error:") - println(e) - println("→ Using NonlinearSolve fallback") - - # Wrap pour respecter l'interface de NonlinearProblem - function wrapped_f(s, ξ, p) - f(s, ξ) - return nothing - end - - prob = NonlinearProblem(wrapped_f, x) - sol = solve(prob; abstol=1e-8, reltol=1e-8) - return MYSOL(sol.u) - end -end - -# Agrégation du problème -nle! = (s, ξ) -> shoot!(s, ξ[1:2], ξ[3], ξ[4]) -jnle! = (js, ξ) -> jacobian!(nle!, similar(ξ), js, backend, ξ) - -ξ0 = [p0... , t1, tf] -indirect_sol = fsolve(nle!, jnle!, ξ0) - -p0 = indirect_sol.x[1:2] -t1 = indirect_sol.x[3] -tf = indirect_sol.x[4] - -f = f1 * (t1, f0) -flow_sol = f((0.0, tf), x0, p0) - -plot!(flow_sol, label="indirect", color=:red) -``` - -# Now we will try an example with a free initial time +Here is the required packages for the tutorial: ```@example initial_time using OptimalControl @@ -319,6 +17,9 @@ using BenchmarkTools using DataFrames using Plots using Printf +``` +## Definition of the problem +```@example initial_time function double_integrator_mint0() @def ocp begin @@ -346,450 +47,87 @@ We now solve the problem using a direct method, with automatic treatment of the ```@example initial_time ocp = double_integrator_mint0() sol = solve(ocp; grid_size=100) -plot(sol; label="direct", size=(800, 800)) -``` - - - -## Verification of results -```@raw html -
- Click to show/hide mathematical verification. -``` -Here is the theoretical part using Pontryagin's Maximum Principle: -```math -H = p_1 x_2 + p_2 u + 1 ``` +And plot the solution. -Conditions from Pontryagin’s theorem: -```math -p_1' = 0 \quad \Rightarrow \quad p_1 = c_1 \quad (\text{constant}) \\ -p_2' = -p_1 \quad \Rightarrow \quad p_2 = -c_1 t + c_2 -``` - -Switching condition: -```math -p_2(t_s) = 0 \quad \Rightarrow \quad c_2 = c_1 t_s -``` - -Optimal control: -```math -u(t) = 1 \quad \text{on} \quad [t_0, t_s] \\ -u(t) = -1 \quad \text{on} \quad [t_s, 0] -``` - -Now we integrate the system: - -On \( t \in [t_0, t_s] \) : -```math -x_2' = u = 1 \quad \Rightarrow \quad x_2(t) = t - t_0 \\ -x_1' = x_2 \quad \Rightarrow \quad x_1(t) = \frac{(t - t_0)^2}{2} -``` - -At switching time \( t = t_s \) : -```math -x_2(t_s) = t_s - t_0 \\ -x_1(t_s) = \frac{(t_s - t_0)^2}{2} -``` - -On \( t \in [t_s, 0] \) : -```math -x_2' = u = -1 \quad \Rightarrow \quad x_2(t) = x_2(t_s) - (t - t_s) \\ -x_1' = x_2 \quad \Rightarrow \quad x_1(t) = x_1(t_s) + \int_{t_s}^t x_2(s) ds -``` - -Final velocity condition: -```math -x_2(0) = 0 \quad \Rightarrow \quad t_s - t_0 + t_s = 0 \quad \Rightarrow \quad t_0 = 2 t_s -``` - -Final position: -```math -x_1(0) = x_1(t_s) + \frac{t_s^2}{2} \quad \Rightarrow \quad x_1(0) = t_s^2 = 1 \quad \Rightarrow \quad t_s = -1 -``` - -We deduce: -```math -t_0 = 2 * t_s = -2 -``` - -### Final solution: -- Switching time: \( t_s = -1 \) -- Initial time: \( t_0 = -2 \) - -Control: -```math -u(t) = 1 \quad \text{on} \quad [-2, -1] \\ -u(t) = -1 \quad \text{on} \quad [-1, 0] -``` -```@raw html -
-``` -## An example with both final and inital times being free: -```@example both_time -using OptimalControl -using NLPModelsIpopt -using BenchmarkTools -using DataFrames -using Plots -using Printf - -function double_integrator_freet0tf() - @def ocp begin - v=(t0, tf) ∈ R², variable - t ∈ [t0, tf], time - x ∈ R², state - u ∈ R, control - -1 ≤ u(t) ≤ 1 - x(t0) == [0, 0] - x(tf) == [1, 0] - 0.05 ≤ t0 ≤ 10 - 0.05 ≤ tf ≤ 10 - 0.01 ≤ tf - t0 ≤ Inf - ẋ(t) == [x₂(t), u(t)] - t0 → max - end - - return ocp -end -nothing # hide -``` - -# Direct resolution with both final and inital times being free: - - -We now solve the problem using a direct method, with automatic treatment of the free initial time. - -```@example both_time -ocp = double_integrator_freet0tf() -sol = solve(ocp; grid_size=100) -plot(sol; label="direct", size=(800, 800)) -``` - - -## A more concrete example about the change of orbit of a satellite: - -```@example orbit -using OptimalControl -using NLPModelsIpopt -using BenchmarkTools -using DataFrames -using Plots -using Printf -using OrdinaryDiffEq -using NonlinearSolve -using DifferentiationInterface -import ForwardDiff -using LinearAlgebra -using NLsolve - - -const x₁₀ = -42272.67 # initial position x -const x₂₀ = 0 # initial position y -const x₃₀ = 0 # initial velocity in x -const x₄₀ = -5696.72 # initial velocity in y -const μ = 5.1658620912*1e12 # gravitational parameter -const γ_max = 0.05 # maximal thrust norm -const r_f = 42165 # target orbit radius (final distance to origin) -const rf3 = r_f^3 -const α = sqrt(μ/rf3); - -function min_orbit_tf() - @def ocp begin - tf ∈ R, variable - t ∈ [0, tf], time - x ∈ R⁴, state - u ∈ R², control - u₁(t)^2 + u₂(t)^2 ≤ γ_max^2 - x(0) == [x₁₀, x₂₀, x₃₀, x₄₀] - x₁(tf)^2 + x₂(tf)^2 == r_f^2 - 0.05 ≤ tf ≤ Inf - ẋ(t) == [ - x₃(t), - x₄(t), - -μ * x₁(t) / ((x₁(t)^2 + x₂(t)^2)^(3/2)) + u₁(t), - -μ * x₂(t) / ((x₁(t)^2 + x₂(t)^2)^(3/2)) + u₂(t) - ] - tf → min - end - - return ocp -end -nothing # hide -``` - -# Direct resolution with minimal orbital time : - -We now solve the problem using a direct method, with automatic treatment of the free initial time. - -```@example orbit -ocp = min_orbit_tf() -sol = solve(ocp;init=(variable=13.4,), grid_size=100) +```@example initial_time plot(sol; label="direct", size=(800, 800)) ``` -# Indirect resolution with minimal orbital time : - - -## Hamiltonian and optimal control - -From the Pontryagin Maximum Principle, the optimal control is bang-bang: - -```@example orbit -ocp = min_orbit_tf() - -function optimal_control(p) - p₃, p₄ = p[3], p[4] - p_norm = sqrt(p₃^2 + p₄^2) - - if p_norm > 1e-10 - u₁ = γ_max * p₃ / p_norm - u₂ = γ_max * p₄ / p_norm - return [u₁, u₂] - else - return [0.0, 0.0] - end -end - -function hamiltonian(x, p, u) - x₁, x₂, x₃, x₄ = x - p₁, p₂, p₃, p₄ = p - u₁, u₂ = u - - r = sqrt(x₁^2 + x₂^2) - r³ = r^3 - - return (p₁*x₃ + p₂*x₄ + - p₃*(-μ*x₁/r³ + u₁) + - p₄*(-μ*x₂/r³ + u₂)) -end -``` - -## Augmented dynamics -```@raw html -
-Click to show/hide Augmented dynamics code -``` -We define the combined state-costate system: - -```@example orbit -function augmented_dynamics!(dx, x_aug, params, t) - x = x_aug[1:4] - p = x_aug[5:8] - - x₁, x₂, x₃, x₄ = x - p₁, p₂, p₃, p₄ = p - - r = sqrt(x₁^2 + x₂^2) - r³ = r^3 - r⁵ = r^5 - - u = optimal_control(p) - u₁, u₂ = u - - # State dynamics (unchanged) - dx[1] = x₃ - dx[2] = x₄ - dx[3] = -μ*x₁/r³ + u₁ - dx[4] = -μ*x₂/r³ + u₂ - - # CORRECTED Costate dynamics: ṗ = -∂H/∂x - dx[5] = -(p₃ * μ * (3*x₁^2/r⁵ - 1/r³) + p₄ * μ * (3*x₁*x₂/r⁵)) # -∂H/∂x₁ - dx[6] = -(p₃ * μ * (3*x₁*x₂/r⁵) + p₄ * μ * (3*x₂^2/r⁵ - 1/r³)) # -∂H/∂x₂ - dx[7] = -p₁ # -∂H/∂x₃ - dx[8] = -p₂ # -∂H/∂x₄ -end - -function create_flow() - function flow_func(t_span, x0, p0) - x_aug0 = vcat(x0, p0) - prob = ODEProblem(augmented_dynamics!, x_aug0, t_span) - sol = solve(prob, Tsit5(), reltol=1e-12, abstol=1e-12) - return sol - end - return flow_func -end - -flow = create_flow() -``` -```@raw html -
-``` -## Shooting function - -The shooting function encodes the boundary conditions: - -```@example orbit -function shooting_function!(s, ξ) - p0 = ξ[1:4] - tf = ξ[5] - - x0 = [x₁₀, x₂₀, x₃₀, x₄₀] - - try - sol = flow((0.0, tf), x0, p0) - - x_final = sol.u[end][1:4] - p_final = sol.u[end][5:8] - - x₁f, x₂f, x₃f, x₄f = x_final - p₁f, p₂f, p₃f, p₄f = p_final - - s[1] = x₁f^2 + x₂f^2 - r_f^2 - s[2] = p₁f - s[3] = p₂f - - u_final = optimal_control(p_final) - H_final = hamiltonian(x_final, p_final, u_final) - s[4] = H_final - - s[5] = p₁f^2 + p₂f^2 + p₃f^2 + p₄f^2 - 1.0 - - catch e - fill!(s, 1e6) - end - - return nothing -end -``` - -## Initial guess and solver setup - -```@example orbit -# Initial guess for the shooting variables [p₀, tf] - -function get_initial_guess() - tf_guess = 13.4 - - p0_guess = [1.0323e-4, 4.915e-5, 3.568e-4, -1.554e-4] - - return vcat(p0_guess, tf_guess) -end - -ξ_init = get_initial_guess() -println("Initial guess: p₀ = $(ξ_init[1:4]), tf = $(ξ_init[5])") -``` - -## Solve the shooting problem - -```@example orbit -# Solve the shooting problem using NLsolve -function solve_indirect_method() - println("Solving indirect method...") - - # Set up the shooting problem - result = nlsolve(shooting_function!, ξ_init, - method=:trust_region, - ftol=1e-10, - xtol=1e-10, - iterations=1000, - show_trace=false) - - if result.f_converged || result.x_converged - println("Shooting method converged!") - println("Final residual norm: $(norm(result.zero))") - return result.zero - else - println("Shooting method failed to converge") - println("Final residual norm: $(norm(result.zero))") - return nothing - end -end - -ξ_solution = solve_indirect_method() - -if ξ_solution !== nothing - p0_opt = ξ_solution[1:4] - tf_opt = ξ_solution[5] - - println("\nOptimal solution:") - println("p₀ = $(p0_opt)") - println("tf = $(tf_opt)") - - # Verify the solution - s_check = zeros(5) - shooting_function!(s_check, ξ_solution) - println("Shooting function residual: $(s_check)") -end -``` - -## Generate the indirect solution trajectory - -```@example orbit -if ξ_solution !== nothing - # Generate the optimal trajectory - x0 = [x₁₀, x₂₀, x₃₀, x₄₀] - p0_opt = ξ_solution[1:4] - tf_opt = ξ_solution[5] - - # Solve the optimal trajectory - sol_indirect = flow((0.0, tf_opt), x0, p0_opt) - - # Extract time points for plotting - t_indirect = range(0, tf_opt, length=1000) - - # Evaluate solution at time points - x_traj = zeros(length(t_indirect), 4) - p_traj = zeros(length(t_indirect), 4) - u_traj = zeros(length(t_indirect), 2) - - for (i, t) in enumerate(t_indirect) - state_full = sol_indirect(t) - x_traj[i, :] = state_full[1:4] - p_traj[i, :] = state_full[5:8] - u_traj[i, :] = optimal_control(state_full[5:8]) - end - - println("Indirect solution generated successfully!") - println("Final time: $(tf_opt)") - println("Final position: $(x_traj[end, 1:2])") - println("Final radius: $(sqrt(x_traj[end, 1]^2 + x_traj[end, 2]^2))") -end -``` -# Visualistion of results -```@raw html -
-Click to show/hide indirect method visualization code -``` -```@example orbit -# Simple visualization - just the basic plots -if ξ_solution !== nothing - p0_opt = ξ_solution[1:4] - tf_opt = ξ_solution[5] - - x0 = [x₁₀, x₂₀, x₃₀, x₄₀] - sol_indirect = flow((0.0, tf_opt), x0, p0_opt) - - t_indirect = range(0, tf_opt, length=1000) - - x_traj = zeros(length(t_indirect), 4) - p_traj = zeros(length(t_indirect), 4) - u_traj = zeros(length(t_indirect), 2) - - for (i, t) in enumerate(t_indirect) - state_full = sol_indirect(t) - x_traj[i, :] = state_full[1:4] - p_traj[i, :] = state_full[5:8] - u_traj[i, :] = optimal_control(state_full[5:8]) - end - - # Combined plot with 3 rows - plt = plot(layout=(3,1), size=(800, 900)) - - plot!(plt[1], t_indirect, [x_traj[:,1] x_traj[:,2] x_traj[:,3] x_traj[:,4]], - label=["x₁" "x₂" "x₃" "x₄"], title="States") - - plot!(plt[2], t_indirect, [p_traj[:,1] p_traj[:,2] p_traj[:,3] p_traj[:,4]], - label=["p₁" "p₂" "p₃" "p₄"], title="Costates") - - plot!(plt[3], t_indirect, [u_traj[:,1] u_traj[:,2]], - label=["u₁" "u₂"], title="Control") - - plt # This returns the plot for documentation -end -``` -```@raw html -
-``` \ No newline at end of file +## Verification of results +!!! note "Mathematical computations" + ```@raw html +
+ Click to show/hide mathematical verification. + ``` + Here is the theoretical part using Pontryagin's Maximum Principle: + ```math + H = p_1 x_2 + p_2 u + 1 + ``` + + Conditions from Pontryagin’s theorem: + ```math + p_1' = 0 \quad \Rightarrow \quad p_1 = c_1 \quad (\text{constant}) \\ + p_2' = -p_1 \quad \Rightarrow \quad p_2 = -c_1 t + c_2 + ``` + + Switching condition: + ```math + p_2(t_s) = 0 \quad \Rightarrow \quad c_2 = c_1 t_s + ``` + + Optimal control: + ```math + u(t) = 1 \quad \text{on} \quad [t_0, t_s] \\ + u(t) = -1 \quad \text{on} \quad [t_s, 0] + ``` + + Now we integrate the system: + + On \( t \in [t_0, t_s] \) : + ```math + x_2' = u = 1 \quad \Rightarrow \quad x_2(t) = t - t_0 \\ + x_1' = x_2 \quad \Rightarrow \quad x_1(t) = \frac{(t - t_0)^2}{2} + ``` + + At switching time \( t = t_s \) : + ```math + x_2(t_s) = t_s - t_0 \\ + x_1(t_s) = \frac{(t_s - t_0)^2}{2} + ``` + + On \( t \in [t_s, 0] \) : + ```math + x_2' = u = -1 \quad \Rightarrow \quad x_2(t) = x_2(t_s) - (t - t_s) \\ + x_1' = x_2 \quad \Rightarrow \quad x_1(t) = x_1(t_s) + \int_{t_s}^t x_2(s) ds + ``` + + Final velocity condition: + ```math + x_2(0) = 0 \quad \Rightarrow \quad t_s - t_0 + t_s = 0 \quad \Rightarrow \quad t_0 = 2 t_s + ``` + + Final position: + ```math + x_1(0) = x_1(t_s) + \frac{t_s^2}{2} \quad \Rightarrow \quad x_1(0) = t_s^2 = 1 \quad \Rightarrow \quad t_s = -1 + ``` + + We deduce: + ```math + t_0 = 2 * t_s = -2 + ``` + + ### Final solution: + - Switching time: \( t_s = -1 \) + - Initial time: \( t_0 = -2 \) + + Control: + ```math + u(t) = 1 \quad \text{on} \quad [-2, -1] \\ + u(t) = -1 \quad \text{on} \quad [-1, 0] + ``` + ```@raw html +
+ ``` diff --git a/docs/src/tutorial-free-times-orbital.md b/docs/src/tutorial-free-times-orbital.md index d45d32b..b31cc10 100644 --- a/docs/src/tutorial-free-times-orbital.md +++ b/docs/src/tutorial-free-times-orbital.md @@ -1,471 +1,11 @@ ```@meta -Draft = true +Draft = false ``` -# [Free Initial and Final Times](@id tutorial-free-times) +# [Optimal control problem with free final orbital time](@id tutorial-free-times-orbital) -In this tutorial, we explore optimal control problems with free initial time `t₀` and/or final time `t_f`. These problems require special treatment in both direct and indirect methods, particularly when handling time-varying constraints and objectives. -```@example main-disc -using OptimalControl -using NLPModelsIpopt -using BenchmarkTools -using DataFrames -using Plots -using Printf - -function double_integrator_mintf() - @def ocp begin - tf ∈ R, variable - t ∈ [0, tf], time - x ∈ R², state - u ∈ R, control - -1 ≤ u(t) ≤ 1 - x(0) == [0, 0] - x(tf) == [1, 0] - 0.05 ≤ tf ≤ Inf - ẋ(t) == [x₂(t), u(t)] - tf → min - end - return ocp -end -nothing # hide -``` - -To allow the use of a free final time, you define t0 and/or tf as variables, and then set the global time t between these two variables. Here is a complete example : - -```julia -v=(t0, tf) ∈ R², variable -t ∈ [t0, tf], time -``` - -You may need to add this type of constrain in your problem definition : - -```julia -0.05 ≤ tf ≤ Inf -``` - -It is to ensure and help the convergence of the algorithm depending on your problem and it can also be needed when dealing with problems having a free initial time, as we will see in the next example. - -## Direct resolution with free final time : - -We now solve the problem using a direct method, with automatic treatment of the free final time. - -```@example main-disc -ocp = double_integrator_mintf() -sol = solve(ocp; grid_size=100) -plot(sol; label="direct", size=(800, 800)) -``` - -# Verification of results -```@raw html -
- Click to show/hide mathematical verification. -``` - -Here the theorical part : -```math -H = p1*x2 + p2*u -``` -Conditions of Pontryagin's theorem : -```math -p1' = 0, \quad p2' = -p1 \\ -p1 = p1(0) = constante = 1\\ -p2 = -p1*t + p2(0) = 1-t\\ -``` -Transversal conditions : -```math -H[tf] = -p° = 1 -``` -We find u : -```math -u(t) = 1 ∈ [0,t1] \\ -u(t) = -1 ∈ [t1, tf] -``` -Where t1 is a constant to determined. - -Now we have to integrate x' : - -On t ∈ [0,t1] : -```math -x1' = x2 \\ -x2'= u = 1 \\ -``` - -```math -x2(t) = t \\ -x1(t) = (1/2)*t^2 -``` - -When t = t1 : -```math -x2(t1) = t1 \\ -x1(t1) = (1/2)*t1^2 -``` - -On t ∈ [t1,tf] -```math -x2(t) = -t + 2*t1 \\ -x1(t) = -(1/2)*t^2 + 2*t1*t + C2 \\ -``` - -```math -x1(t1) = -(1/2)*t1^2 + 2*t1^2 + C2 = (1/2)*t1^2 \\ -C2 = -t1^2 \\ -``` - -```math -x1(t) = -(1/2)*t^2 + 2*t1*t - t1^2 -``` - -Finally you can solve the terminal conditions : -```math -x1(tf) = 1, \quad x2(tf) = 0 \\ -``` - -```math -x2(tf) = -tf + 2*t1 = 0 \\ -tf = 2*t1 \\ -``` - -```math -x1(tf) = -(1/2)*tf^2 + 2*t1*tf - t1^2 = 1 \\ --2*t1^2 + 4*t1 - t1^2 = t1 = 1 \\ -``` - -```math -t1 = 1, \quad t2 = 2 -``` - -To sum up we find the following solutions : -```math -x1(t) = \begin{cases} -(1/2)*t^2 & \text{si } t \in [0, 1) \\ --(1/2)*t^2 + 2*t - 1 & \text{si } t \in [1, 2] -\end{cases} -\qquad -x2(t) = \begin{cases} -t & \text{si } t \in [0, 1) \\ -2 - t & \text{si } t \in [1, 2] -\end{cases} -``` - -```math -p1(t) = 1, \quad p2(t) = 1-t, \quad p°=-1 -``` - -```math -u(t) = \begin{cases} -1 & \text{si } t \in [0, 1) \\ --1 & \text{si } t \in [1, 2] -\end{cases} -``` -```@raw html -
-``` - - -Now we can compare the results found with the direct method whith the theoritical analysis : -```@example main-disc -tf = variable(sol) -u = control(sol) -p = costate(sol) -x = state(sol) -p° = -1 - -xf = x(tf) -pf = p(tf) - -Htf = pf[1]*xf[2] + pf[2]*u(tf) -@printf("H(tf) = %.3f\n", Htf) -@printf("x(tf) = [%.3f, %.3f]\n", xf[1], xf[2]) -@printf("p(tf) = [%.3f, %.3f]\n", pf[1], pf[2]) -``` - -The numerical results closely match the theoretical predictions: the final state x(tf)=[1,0] is exactly satisfied. -The costate and Hamiltonian values at final time show a small deviation (≈ 0.01), likely due to numerical precision. -Overall, the direct method confirms the theoretical analysis with excellent accuracy. - -We can analyse the influence of using different discretization sizes (grid_size), and observed the following results for the optimal tf: - -```@example main-disc -for N in [20, 50, 100, 200] - solN = solve(ocp; grid_size=N, display=false) - @printf("grid_size = %3d → tf = %.5f\n", N, objective(solN)) -end -``` - -This example shows that problems with a free final time can be sensitive to discretization. A small grid may lead to suboptimal or slightly inaccurate results. - -# Direct resolution with free initial time : - -We keep the structure of the solution found with the direct method -```@example main-disc -t = time_grid(sol) -x = state(sol) -u = control(sol) -p = costate(sol) - -H(x,p,u,t) = p[1](t)*x[2](t) + p[2](t)*u(u) -H(xf, pf, tf) = pf[1]*xf[2] + pf[2]*u(tf) - -# Hamiltonian vector field -F1(x) = [0, 1] -H1 = Lift(F1) -φ(t) = H1(x(t), p(t)) # switching function -``` - - -First, lets define the different flows -```@example main-disc -using OrdinaryDiffEq # to get the Flow function from OptimalControl - -const u1 = 1 -const u0 = -1 - -f1 = Flow(ocp, (x, p, tf) -> u1) -f0 = Flow(ocp, (x, p, tf) -> u0) -``` - -And with this we have the following shooting function -```@example main-disc -x0 = [0.0, 0.0] # état initial -xf_target = [1.0, 0.0] # état final - -function shoot!(s, p0, t1, tf) -x1, p1 = f1(0.0, x0, p0, t1) -xf, pf = f0(t1, x1, p1, tf) - -# Conditions de tir -s[1:2] = xf .- xf_target # état final -s[3] = H(xf, pf, tf) - 1 # condition de transversalité H(tf) = 1 -s[4] = H1(x1, p1) # φ = 0 au switch -end -``` - -Before solving our problem we must find a good initial guess to help the convergence of the algorithm -```@example main-disc -p0 = p(0.0) -φ_arr = abs.(φ.(t)) -t1 = t[argmin(φ_arr)] -tf = t[end] - -println("p0 ≈ ", p0) -println("t1 ≈ ", t1) -println("tf ≈ ", tf) - -s = zeros(4) -shoot!(s, p0, t1, tf) - -ξ = [p0..., t1, tf] -``` - -And we finally can solve the problem using an indirect method -```@example main-disc -using NonlinearSolve -using DifferentiationInterface -import ForwardDiff - -backend = AutoForwardDiff() - -struct MYSOL - x::Vector{Float64} -end - -function fsolve(f, j, x; kwargs...) - try - MINPACK.fsolve(f, j, x; kwargs...) - catch e - println("MINPACK error:") - println(e) - println("→ Using NonlinearSolve fallback") - - # Wrap pour respecter l'interface de NonlinearProblem - function wrapped_f(s, ξ, p) - f(s, ξ) - return nothing - end - - prob = NonlinearProblem(wrapped_f, x) - sol = solve(prob; abstol=1e-8, reltol=1e-8) - return MYSOL(sol.u) - end -end - -# Agrégation du problème -nle! = (s, ξ) -> shoot!(s, ξ[1:2], ξ[3], ξ[4]) -jnle! = (js, ξ) -> jacobian!(nle!, similar(ξ), js, backend, ξ) - -ξ0 = [p0... , t1, tf] -indirect_sol = fsolve(nle!, jnle!, ξ0) - -p0 = indirect_sol.x[1:2] -t1 = indirect_sol.x[3] -tf = indirect_sol.x[4] - -f = f1 * (t1, f0) -flow_sol = f((0.0, tf), x0, p0) - -plot!(flow_sol, label="indirect", color=:red) -``` - -# Now we will try an example with a free initial time - -```@example initial_time -using OptimalControl -using NLPModelsIpopt -using BenchmarkTools -using DataFrames -using Plots -using Printf - -function double_integrator_mint0() - @def ocp begin - t0 ∈ R, variable - tf=0 - t ∈ [t0, tf], time - x ∈ R², state - u ∈ R, control - -1 ≤ u(t) ≤ 1 - x(t0) == [0, 0] - x(tf) == [1, 0] - 0.05 ≤ -t0 ≤ Inf - ẋ(t) == [x₂(t), u(t)] - -t0 → min - end - return ocp -end -nothing # hide -``` - -# Direct resolution with free initial time : - -We now solve the problem using a direct method, with automatic treatment of the free initial time. - -```@example initial_time -ocp = double_integrator_mint0() -sol = solve(ocp; grid_size=100) -plot(sol; label="direct", size=(800, 800)) -``` - - - -## Verification of results -```@raw html -
- Click to show/hide mathematical verification. -``` -Here is the theoretical part using Pontryagin's Maximum Principle: -```math -H = p_1 x_2 + p_2 u + 1 -``` - -Conditions from Pontryagin’s theorem: -```math -p_1' = 0 \quad \Rightarrow \quad p_1 = c_1 \quad (\text{constant}) \\ -p_2' = -p_1 \quad \Rightarrow \quad p_2 = -c_1 t + c_2 -``` - -Switching condition: -```math -p_2(t_s) = 0 \quad \Rightarrow \quad c_2 = c_1 t_s -``` - -Optimal control: -```math -u(t) = 1 \quad \text{on} \quad [t_0, t_s] \\ -u(t) = -1 \quad \text{on} \quad [t_s, 0] -``` - -Now we integrate the system: - -On \( t \in [t_0, t_s] \) : -```math -x_2' = u = 1 \quad \Rightarrow \quad x_2(t) = t - t_0 \\ -x_1' = x_2 \quad \Rightarrow \quad x_1(t) = \frac{(t - t_0)^2}{2} -``` - -At switching time \( t = t_s \) : -```math -x_2(t_s) = t_s - t_0 \\ -x_1(t_s) = \frac{(t_s - t_0)^2}{2} -``` - -On \( t \in [t_s, 0] \) : -```math -x_2' = u = -1 \quad \Rightarrow \quad x_2(t) = x_2(t_s) - (t - t_s) \\ -x_1' = x_2 \quad \Rightarrow \quad x_1(t) = x_1(t_s) + \int_{t_s}^t x_2(s) ds -``` - -Final velocity condition: -```math -x_2(0) = 0 \quad \Rightarrow \quad t_s - t_0 + t_s = 0 \quad \Rightarrow \quad t_0 = 2 t_s -``` - -Final position: -```math -x_1(0) = x_1(t_s) + \frac{t_s^2}{2} \quad \Rightarrow \quad x_1(0) = t_s^2 = 1 \quad \Rightarrow \quad t_s = -1 -``` - -We deduce: -```math -t_0 = 2 * t_s = -2 -``` - -### Final solution: -- Switching time: \( t_s = -1 \) -- Initial time: \( t_0 = -2 \) - -Control: -```math -u(t) = 1 \quad \text{on} \quad [-2, -1] \\ -u(t) = -1 \quad \text{on} \quad [-1, 0] -``` -```@raw html -
-``` -## An example with both final and inital times being free: -```@example both_time -using OptimalControl -using NLPModelsIpopt -using BenchmarkTools -using DataFrames -using Plots -using Printf - -function double_integrator_freet0tf() - @def ocp begin - v=(t0, tf) ∈ R², variable - t ∈ [t0, tf], time - x ∈ R², state - u ∈ R, control - -1 ≤ u(t) ≤ 1 - x(t0) == [0, 0] - x(tf) == [1, 0] - 0.05 ≤ t0 ≤ 10 - 0.05 ≤ tf ≤ 10 - 0.01 ≤ tf - t0 ≤ Inf - ẋ(t) == [x₂(t), u(t)] - t0 → max - end - - return ocp -end -nothing # hide -``` - -# Direct resolution with both final and inital times being free: - - -We now solve the problem using a direct method, with automatic treatment of the free initial time. - -```@example both_time -ocp = double_integrator_freet0tf() -sol = solve(ocp; grid_size=100) -plot(sol; label="direct", size=(800, 800)) -``` - ## A more concrete example about the change of orbit of a satellite: @@ -525,6 +65,10 @@ We now solve the problem using a direct method, with automatic treatment of the ```@example orbit ocp = min_orbit_tf() sol = solve(ocp;init=(variable=13.4,), grid_size=100) +``` +And plot the solution. + +```@example orbit plot(sol; label="direct", size=(800, 800)) ``` # Indirect resolution with minimal orbital time : From 6ca5c764ef1c4ea56cc7daf02ce7a91c3b2e65c0 Mon Sep 17 00:00:00 2001 From: yassin moukan Date: Thu, 3 Jul 2025 17:00:43 +0200 Subject: [PATCH 18/20] finalized the tutorials, except the orbital example --- docs/src/tutorial-free-times-final-initial.md | 34 +++++----- docs/src/tutorial-free-times-final.md | 63 ++++++++++++++----- docs/src/tutorial-free-times-initial.md | 62 +++++++++--------- 3 files changed, 95 insertions(+), 64 deletions(-) diff --git a/docs/src/tutorial-free-times-final-initial.md b/docs/src/tutorial-free-times-final-initial.md index 784c276..50be678 100644 --- a/docs/src/tutorial-free-times-final-initial.md +++ b/docs/src/tutorial-free-times-final-initial.md @@ -7,7 +7,7 @@ Draft = false In this tutorial, we explore optimal control problems with free initial time `t₀` and final time `t_f`. -## Here is the required packages for the tutorial: +## Here are the required packages for the tutorial: ```@example both_time using OptimalControl @@ -21,24 +21,21 @@ using Printf ```@example both_time -function double_integrator_freet0tf() - @def ocp begin - v=(t0, tf) ∈ R², variable - t ∈ [t0, tf], time - x ∈ R², state - u ∈ R, control - -1 ≤ u(t) ≤ 1 - x(t0) == [0, 0] - x(tf) == [1, 0] - 0.05 ≤ t0 ≤ 10 - 0.05 ≤ tf ≤ 10 - 0.01 ≤ tf - t0 ≤ Inf - ẋ(t) == [x₂(t), u(t)] - t0 → max - end - - return ocp +@def ocp begin + v=(t0, tf) ∈ R², variable + t ∈ [t0, tf], time + x ∈ R², state + u ∈ R, control + -1 ≤ u(t) ≤ 1 + x(t0) == [0, 0] + x(tf) == [1, 0] + 0.05 ≤ t0 ≤ 10 + 0.05 ≤ tf ≤ 10 + 0.01 ≤ tf - t0 ≤ Inf + ẋ(t) == [x₂(t), u(t)] + t0 → max end + nothing # hide ``` @@ -48,7 +45,6 @@ nothing # hide We now solve the problem using a direct method, with automatic treatment of the free initial time. ```@example both_time -ocp = double_integrator_freet0tf() sol = solve(ocp; grid_size=100) ``` And plot the solution. diff --git a/docs/src/tutorial-free-times-final.md b/docs/src/tutorial-free-times-final.md index cc5b1d2..f047e6a 100644 --- a/docs/src/tutorial-free-times-final.md +++ b/docs/src/tutorial-free-times-final.md @@ -127,50 +127,79 @@ plt = plot(sol; label="direct", size=(800, 800)) On t ∈ [0,t1] : ```math - x1' = x2 \\ - x2'= u = 1 \\ + \begin{aligned} + \dot{x}_1(t) &= x_2(t) \\ + \dot{x}_2(t) &= 1 \\ + \end{aligned} ``` ```math - x2(t) = t \\ - x1(t) = (1/2)*t^2 + \begin{aligned} + x_2(t) &= t \\ + x_1(t) &= (1/2)*t^2 + \end{aligned} + ``` When t = t1 : ```math - x2(t1) = t1 \\ - x1(t1) = (1/2)*t1^2 + \begin{aligned} + x_2(t1) &= t_1 \\ + x_1(t1) &= (1/2)*t_1^2 + \end{aligned} ``` On $t \in [t_1,t_f]$ ```math - x_2(t) = -t + 2t_1 \\ - x_1(t) = -\frac{1}{2}t^2 + 2t_1 t + C_2 \\ + \begin{aligned} + x_2(t) &= -t + 2t_1 \\ + x_1(t) &= -\frac{1}{2}t^2 + 2t_1 t + C_2 \\ + \end{aligned} ``` ```math - x_1(t_1) = -\frac{1}{2}t_1^2 + 2t_1^2 + C_2 = \frac{1}{2}t_1^2 \\ - C_2 = -t_1^2 \\ + \begin{aligned} + x_1(t_1) &= -\frac{1}{2}t_1^2 + 2t_1^2 + C_2 &= \frac{1}{2}t_1^2 \\ + C_2 &= -t_1^2 \\ + \end{aligned} ``` + ```math - x_1(t) = -\frac{1}{2}t^2 + 2t_1 t - t_1^2 + \begin{aligned} + x_1(t) &= -\frac{1}{2}t^2 + 2t_1 t - t_1^2 + \end{aligned} ``` Finally you can solve the terminal conditions : ```math - x_1(t_f) = 1, \quad x_2(t_f) = 0 \\ + \begin{aligned} + x_1(t_f) &= 1, \quad x_2(t_f) &= 0 \\ + \end{aligned} ``` ```math - x_2(t_f) = -t_f + 2t_1 = 0 \\ - t_f = 2t_1 \\ + \begin{aligned} + x_2(t_f) &= -t_f + 2t_1 &= 0 \\ + t_f &= 2t_1 \\ + \end{aligned} ``` ```math - x_1(t_f) = -\frac{1}{2}t_f^2 + 2t_1 t_f - t_1^2 = 1 \\ - -2t_1^2 + 4t_1^2 - t_1^2 = t_1^2 = 1 \\ + \begin{aligned} + x_1(t_f)& = -\frac{1}{2}t_f^2 + 2t_1 t_f - t_1^2 &= 1 \\ + \end{aligned} ``` + and ```math - t_1 = 1, \quad t_f = 2 + \begin{aligned} + -2t_1^2 + 4t_1^2 - t_1^2 &= t_1^2 &= 1 \\ + \end{aligned} + ``` + gets us + ```math + \begin{aligned} + t_1 &= 1, \quad t_f &= 2 + \end{aligned} ``` To sum up we find the following solutions : ```math + x_1(t) = \begin{cases} \frac{1}{2}t^2 & \text{si } t \in [0, 1) \\ -\frac{1}{2}t^2 + 2t - 1 & \text{si } t \in [1, 2] diff --git a/docs/src/tutorial-free-times-initial.md b/docs/src/tutorial-free-times-initial.md index d0ad5b0..5068581 100644 --- a/docs/src/tutorial-free-times-initial.md +++ b/docs/src/tutorial-free-times-initial.md @@ -8,7 +8,7 @@ Draft = false ## In this tutorial, we explore an optimal control problem with free initial time `t0`. -Here is the required packages for the tutorial: +Here are the required packages for the tutorial: ```@example initial_time using OptimalControl @@ -21,21 +21,18 @@ using Printf ## Definition of the problem ```@example initial_time -function double_integrator_mint0() - @def ocp begin - t0 ∈ R, variable - tf=0 - t ∈ [t0, tf], time - x ∈ R², state - u ∈ R, control - -1 ≤ u(t) ≤ 1 - x(t0) == [0, 0] - x(tf) == [1, 0] - 0.05 ≤ -t0 ≤ Inf - ẋ(t) == [x₂(t), u(t)] - -t0 → min - end - return ocp +@def ocp begin + t0 ∈ R, variable + tf=0 + t ∈ [t0, tf], time + x ∈ R², state + u ∈ R, control + -1 ≤ u(t) ≤ 1 + x(t0) == [0, 0] + x(tf) == [1, 0] + 0.05 ≤ -t0 ≤ Inf + ẋ(t) == [x₂(t), u(t)] + -t0 → min end nothing # hide ``` @@ -45,7 +42,6 @@ nothing # hide We now solve the problem using a direct method, with automatic treatment of the free initial time. ```@example initial_time -ocp = double_integrator_mint0() sol = solve(ocp; grid_size=100) ``` And plot the solution. @@ -69,8 +65,12 @@ plot(sol; label="direct", size=(800, 800)) Conditions from Pontryagin’s theorem: ```math - p_1' = 0 \quad \Rightarrow \quad p_1 = c_1 \quad (\text{constant}) \\ - p_2' = -p_1 \quad \Rightarrow \quad p_2 = -c_1 t + c_2 + \begin{aligned} + + \dot{p}_1(t) &= 0, \quad \Rightarrow \quad p_1 = c_1 \quad (\text{constant}) \\ + + \dot{p}_2(t) &= -p_1 \quad \Rightarrow \quad p_2 = -c_1 t + c_2 + \end{aligned} ``` Switching condition: @@ -86,22 +86,22 @@ plot(sol; label="direct", size=(800, 800)) Now we integrate the system: - On \( t \in [t_0, t_s] \) : + On ( t in [t_0, t_s] ) : ```math x_2' = u = 1 \quad \Rightarrow \quad x_2(t) = t - t_0 \\ x_1' = x_2 \quad \Rightarrow \quad x_1(t) = \frac{(t - t_0)^2}{2} ``` - At switching time \( t = t_s \) : + At switching time ( t = t_s ) : ```math - x_2(t_s) = t_s - t_0 \\ - x_1(t_s) = \frac{(t_s - t_0)^2}{2} + \dot{x}_2(t_s) = t_s - t_0 \\ + \dot{x}_1(t_s) = \frac{(t_s - t_0)^2}{2} ``` - On \( t \in [t_s, 0] \) : + when ( t in [t_s, 0] ) : ```math - x_2' = u = -1 \quad \Rightarrow \quad x_2(t) = x_2(t_s) - (t - t_s) \\ - x_1' = x_2 \quad \Rightarrow \quad x_1(t) = x_1(t_s) + \int_{t_s}^t x_2(s) ds + \dot{x}_2(t) = u(t) = -1 \quad \Rightarrow \quad x_2(t) = x_2(t_s) - (t - t_s) \\ + \dot{x}_1(t) = x_2(t) \quad \Rightarrow \quad x_1(t) = x_1(t_s) + \int_{t_s}^t x_2(s) ds ``` Final velocity condition: @@ -120,8 +120,14 @@ plot(sol; label="direct", size=(800, 800)) ``` ### Final solution: - - Switching time: \( t_s = -1 \) - - Initial time: \( t_0 = -2 \) + - Switching time: + ```math + t_s = -1 + ``` + - Initial time: + ```math + t_0 = -2 + ``` Control: ```math From 93170c22f4602d0e1663e21e3ca1a87119041e89 Mon Sep 17 00:00:00 2001 From: yassin moukan Date: Wed, 9 Jul 2025 14:13:24 +0200 Subject: [PATCH 19/20] final version of the free final orbital time tutorial --- docs/src/tutorial-free-times-orbital.md | 105 ++++++------------------ 1 file changed, 23 insertions(+), 82 deletions(-) diff --git a/docs/src/tutorial-free-times-orbital.md b/docs/src/tutorial-free-times-orbital.md index b31cc10..4a59643 100644 --- a/docs/src/tutorial-free-times-orbital.md +++ b/docs/src/tutorial-free-times-orbital.md @@ -4,25 +4,21 @@ Draft = false # [Optimal control problem with free final orbital time](@id tutorial-free-times-orbital) - - - ## A more concrete example about the change of orbit of a satellite: +## Here are the required packages for the tutorial: + ```@example orbit using OptimalControl using NLPModelsIpopt -using BenchmarkTools -using DataFrames using Plots using Printf -using OrdinaryDiffEq -using NonlinearSolve -using DifferentiationInterface -import ForwardDiff using LinearAlgebra using NLsolve +``` +## Definition of the problem +```@example orbit const x₁₀ = -42272.67 # initial position x const x₂₀ = 0 # initial position y @@ -44,7 +40,7 @@ function min_orbit_tf() x(0) == [x₁₀, x₂₀, x₃₀, x₄₀] x₁(tf)^2 + x₂(tf)^2 == r_f^2 0.05 ≤ tf ≤ Inf - ẋ(t) == [ + ẋ(t) == [ x₃(t), x₄(t), -μ * x₁(t) / ((x₁(t)^2 + x₂(t)^2)^(3/2)) + u₁(t), @@ -81,7 +77,7 @@ From the Pontryagin Maximum Principle, the optimal control is bang-bang: ```@example orbit ocp = min_orbit_tf() -function optimal_control(p) +function optimal_control(x, p, tf) p₃, p₄ = p[3], p[4] p_norm = sqrt(p₃^2 + p₄^2) @@ -106,58 +102,11 @@ function hamiltonian(x, p, u) p₃*(-μ*x₁/r³ + u₁) + p₄*(-μ*x₂/r³ + u₂)) end +# Create flow using OptimalControl.jl +flow = Flow(ocp, optimal_control) ``` -## Augmented dynamics -```@raw html -
-Click to show/hide Augmented dynamics code -``` -We define the combined state-costate system: -```@example orbit -function augmented_dynamics!(dx, x_aug, params, t) - x = x_aug[1:4] - p = x_aug[5:8] - - x₁, x₂, x₃, x₄ = x - p₁, p₂, p₃, p₄ = p - - r = sqrt(x₁^2 + x₂^2) - r³ = r^3 - r⁵ = r^5 - - u = optimal_control(p) - u₁, u₂ = u - - # State dynamics (unchanged) - dx[1] = x₃ - dx[2] = x₄ - dx[3] = -μ*x₁/r³ + u₁ - dx[4] = -μ*x₂/r³ + u₂ - - # CORRECTED Costate dynamics: ṗ = -∂H/∂x - dx[5] = -(p₃ * μ * (3*x₁^2/r⁵ - 1/r³) + p₄ * μ * (3*x₁*x₂/r⁵)) # -∂H/∂x₁ - dx[6] = -(p₃ * μ * (3*x₁*x₂/r⁵) + p₄ * μ * (3*x₂^2/r⁵ - 1/r³)) # -∂H/∂x₂ - dx[7] = -p₁ # -∂H/∂x₃ - dx[8] = -p₂ # -∂H/∂x₄ -end - -function create_flow() - function flow_func(t_span, x0, p0) - x_aug0 = vcat(x0, p0) - prob = ODEProblem(augmented_dynamics!, x_aug0, t_span) - sol = solve(prob, Tsit5(), reltol=1e-12, abstol=1e-12) - return sol - end - return flow_func -end - -flow = create_flow() -``` -```@raw html -
-``` ## Shooting function The shooting function encodes the boundary conditions: @@ -170,10 +119,8 @@ function shooting_function!(s, ξ) x0 = [x₁₀, x₂₀, x₃₀, x₄₀] try - sol = flow((0.0, tf), x0, p0) - - x_final = sol.u[end][1:4] - p_final = sol.u[end][5:8] + # Use OptimalControl.jl Flow interface with variable + x_final, p_final = flow(0.0, x0, p0, tf, tf) x₁f, x₂f, x₃f, x₄f = x_final p₁f, p₂f, p₃f, p₄f = p_final @@ -182,7 +129,7 @@ function shooting_function!(s, ξ) s[2] = p₁f s[3] = p₂f - u_final = optimal_control(p_final) + u_final = optimal_control(x_final, p_final, tf) H_final = hamiltonian(x_final, p_final, u_final) s[4] = H_final @@ -265,46 +212,41 @@ if ξ_solution !== nothing p0_opt = ξ_solution[1:4] tf_opt = ξ_solution[5] + # Define time points for plotting FIRST + t_indirect = range(0, tf_opt, length=1000) + # Solve the optimal trajectory sol_indirect = flow((0.0, tf_opt), x0, p0_opt) - # Extract time points for plotting - t_indirect = range(0, tf_opt, length=1000) - - # Evaluate solution at time points + # Extract trajectories by evaluating at time points x_traj = zeros(length(t_indirect), 4) p_traj = zeros(length(t_indirect), 4) u_traj = zeros(length(t_indirect), 2) for (i, t) in enumerate(t_indirect) - state_full = sol_indirect(t) - x_traj[i, :] = state_full[1:4] - p_traj[i, :] = state_full[5:8] - u_traj[i, :] = optimal_control(state_full[5:8]) + x_traj[i, :], p_traj[i, :] = flow(0.0, x0, p0_opt, t) + u_traj[i, :] = optimal_control(x_traj[i, :], p_traj[i, :], tf_opt) end - + println("Indirect solution generated successfully!") println("Final time: $(tf_opt)") println("Final position: $(x_traj[end, 1:2])") println("Final radius: $(sqrt(x_traj[end, 1]^2 + x_traj[end, 2]^2))") end ``` -# Visualistion of results +# Visualisation of results ```@raw html
Click to show/hide indirect method visualization code ``` ```@example orbit - - # Simple visualization - just the basic plots if ξ_solution !== nothing p0_opt = ξ_solution[1:4] tf_opt = ξ_solution[5] x0 = [x₁₀, x₂₀, x₃₀, x₄₀] - sol_indirect = flow((0.0, tf_opt), x0, p0_opt) t_indirect = range(0, tf_opt, length=1000) @@ -312,11 +254,10 @@ if ξ_solution !== nothing p_traj = zeros(length(t_indirect), 4) u_traj = zeros(length(t_indirect), 2) + # Use flow function directly to evaluate at each time point for (i, t) in enumerate(t_indirect) - state_full = sol_indirect(t) - x_traj[i, :] = state_full[1:4] - p_traj[i, :] = state_full[5:8] - u_traj[i, :] = optimal_control(state_full[5:8]) + x_traj[i, :], p_traj[i, :] = flow(0.0, x0, p0_opt, t) + u_traj[i, :] = optimal_control(x_traj[i, :], p_traj[i, :], tf_opt) end # Combined plot with 3 rows From 8a6a822b672a43eb7dfbdb5cf2b112b930c74ada Mon Sep 17 00:00:00 2001 From: yassin moukan Date: Thu, 17 Jul 2025 14:44:13 +0200 Subject: [PATCH 20/20] indirect method for tutorials --- docs/src/tutorial-free-times-final-initial.md | 2 - docs/src/tutorial-free-times-initial.md | 102 +++++++++++++++++- 2 files changed, 101 insertions(+), 3 deletions(-) diff --git a/docs/src/tutorial-free-times-final-initial.md b/docs/src/tutorial-free-times-final-initial.md index 50be678..e326e92 100644 --- a/docs/src/tutorial-free-times-final-initial.md +++ b/docs/src/tutorial-free-times-final-initial.md @@ -52,5 +52,3 @@ And plot the solution. ```@example both_time plot(sol; label="direct", size=(800, 800)) ``` - - diff --git a/docs/src/tutorial-free-times-initial.md b/docs/src/tutorial-free-times-initial.md index 5068581..9b9a70d 100644 --- a/docs/src/tutorial-free-times-initial.md +++ b/docs/src/tutorial-free-times-initial.md @@ -6,11 +6,12 @@ Draft = false -## In this tutorial, we explore an optimal control problem with free initial time `t0`. +In this tutorial, we explore an optimal control problem with free initial time `t0`. Here are the required packages for the tutorial: ```@example initial_time +using LinearAlgebra: norm using OptimalControl using NLPModelsIpopt using BenchmarkTools @@ -137,3 +138,102 @@ plot(sol; label="direct", size=(800, 800)) ```@raw html
``` +## Indirect method + + +Define the pseudo-Hamiltonian and switching function +```@example initial_time + +H(x, p, u) = p[1]*x[2] + p[2]*u + +# Hamiltonian lift for switching condition +F1(x) = [0, 1] +H1 = Lift(F1) + +# Define flows for u = +1 and u = -1 +const u_pos = 1 +const u_neg = -1 + + +f_pos = Flow(ocp, (x, p, t0) -> u_pos) +f_neg = Flow(ocp, (x, p, t0) -> u_neg) +``` + +Shooting function adapted for free initial time +```@example initial_time + +function shoot!(s, p0, t1, t0) + # Initial conditions + x0 = [0, 0] # fixed initial state + tf = 0 # fixed final time + + # The flows (time runs from t0 to tf = 0) + x1, p1 = f_pos(t0, x0, p0, t1) # from t0 to t1 + xf, pf = f_neg(t1, x1, p1, tf) # from t1 to tf = 0 + + # Final control + uf = -1 + + # Target final state + xf_target = [1, 0] + + # Shooting conditions + s[1:2] = xf .- xf_target # reach the target at tf = 0 + s[3] = H(xf, pf, uf) - 1 # final condition on pseudo-Hamiltonian + s[4] = H1(x1, p1) # switching condition at t1 +end +``` + +Get initial guess from direct solution +```@example initial_time + +t = time_grid(sol) +x = state(sol) +p = costate(sol) +φ(t) = H1(x(t), p(t)) # switching function + +p0 = p(t[1]) # initial costate (at t0) +t1 = t[argmin(abs.(φ.(t)))] # switching time +t0 = t[1] # initial time (negative value) + +println("p0 = ", p0) +println("t1 = ", t1) +println("t0 = ", t0) + +#Test shooting function at initial guess +s = zeros(4) +shoot!(s, p0, t1, t0) +println("\nNorm of the shooting function: ‖s‖ = ", norm(s), "\n") + +# Solve the nonlinear system +using NonlinearSolve + +# Aggregated function +nle! = (s, ξ, λ) -> shoot!(s, ξ[1:2], ξ[3], ξ[4]) + +# Initial guess: [p0[1], p0[2], t1, t0] +ξ_guess = [p0..., t1, t0] +prob = NonlinearProblem(nle!, ξ_guess) + +# Solve the problem +indirect_sol = solve(prob; show_trace=Val(true), abstol=1e-8, reltol=1e-8) +# Extract solution +p0_opt = indirect_sol.u[1:2] +t1_opt = indirect_sol.u[3] +t0_opt = indirect_sol.u[4] +``` +We can now plot and compare with the direct method.# Compute and plot the optimal trajectory + +```@example initial_time + +x0 = [0, 0] +tf = 0 + +# Concatenate flows: u=+1 from t0 to t1, then u=-1 from t1 to tf +f = f_pos * (t1_opt, f_neg) +flow_sol = f((t0_opt, tf), x0, p0_opt; saveat=range(t0_opt, tf, 100)) + +# Plot comparison +plt = plot(sol; label="direct", size=(800, 800)) +plot!(plt, flow_sol; label="indirect", color=:red) +``` \ No newline at end of file