From ac64468f9f49c0992707832debabacd2fd41a79d Mon Sep 17 00:00:00 2001 From: Robert Lampel Date: Mon, 9 Mar 2026 16:25:17 +0100 Subject: [PATCH 01/73] started adding MTK benchmarks --- lib/OptimalControlBenchmarks/Project.toml | 65 +++++++++++++ .../src/OptimalControlBenchmarks.jl | 12 +++ .../src/problem_registry.jl | 18 ++++ .../src/problems/batch_reactor.jl | 52 +++++++++++ .../src/problems/bryson_denham.jl | 63 +++++++++++++ .../src/problems/cart_pendulum.jl | 79 ++++++++++++++++ .../src/problems/catalyst_mixing.jl | 52 +++++++++++ .../src/problems/cushioned_oscillation.jl | 68 ++++++++++++++ .../src/problems/denbigh.jl | 66 ++++++++++++++ .../problems/dielectrophoretic_particle.jl | 67 ++++++++++++++ .../src/problems/double_oscillator.jl | 70 ++++++++++++++ .../src/problems/ducted_fan.jl | 91 +++++++++++++++++++ .../src/problems/egerstedt.jl | 66 ++++++++++++++ .../src/problems/electric_car.jl | 89 ++++++++++++++++++ .../src/run_benchmarks.jl | 23 +++++ .../src/solver_corleone.jl | 49 ++++++++++ lib/OptimalControlBenchmarks/src/types.jl | 5 + lib/OptimalControlBenchmarks/test.jl | 9 ++ lib/OptimalControlBenchmarks/test/runtests.jl | 4 + 19 files changed, 948 insertions(+) create mode 100644 lib/OptimalControlBenchmarks/Project.toml create mode 100644 lib/OptimalControlBenchmarks/src/OptimalControlBenchmarks.jl create mode 100644 lib/OptimalControlBenchmarks/src/problem_registry.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/denbigh.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/egerstedt.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/electric_car.jl create mode 100644 lib/OptimalControlBenchmarks/src/run_benchmarks.jl create mode 100644 lib/OptimalControlBenchmarks/src/solver_corleone.jl create mode 100644 lib/OptimalControlBenchmarks/src/types.jl create mode 100644 lib/OptimalControlBenchmarks/test.jl create mode 100644 lib/OptimalControlBenchmarks/test/runtests.jl diff --git a/lib/OptimalControlBenchmarks/Project.toml b/lib/OptimalControlBenchmarks/Project.toml new file mode 100644 index 00000000..ce77c6f7 --- /dev/null +++ b/lib/OptimalControlBenchmarks/Project.toml @@ -0,0 +1,65 @@ +name = "OptimalControlBenchmarks" +uuid = "6590a4b1-d036-41fe-a5b4-03a980244102" +authors = ["Bertram Polle"] +version = "0.0.7" + +[deps] +ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" +Corleone = "2751e0db-33e9-4374-b36d-b7219e1e6b40" +DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" +Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +LuxCore = "bb33d45b-7691-41d6-9220-0943567d0623" +ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" +Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba" +OptimizationLBFGSB = "22f7324a-a79d-40f2-bebe-3af60c77bd15" +OptimizationMOI = "fd9f6733-72f4-499f-8506-86b2bdd0dea1" +OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" +OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +Reexport = "189a3867-3050-52da-a836-e630ba90ab69" +SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" +SciMLStructures = "53ae85a6-f571-4167-b2af-e1d143709226" +StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" +SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5" +Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" + +[compat] +Aqua = "0.8" +ComponentArrays = "0.15" +Corleone = "0.0.3" +DocStringExtensions = "0.9" +ForwardDiff = "1.3" +Ipopt = "1" +LinearAlgebra = "1" +LuxCore = "1.4" +Optimization = "5" +OptimizationMOI = "1" +OrdinaryDiffEqTsit5 = "1.5" +Random = "1" +Reexport = "1.2" +SafeTestsets = "0.1" +SciMLBase = "2" +SciMLStructures = "1.7" +StableRNGs = "1" +SymbolicIndexingInterface = "0.3" +Symbolics = "7" +Test = "1" +julia = "1.10" + +[extras] +Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" +Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" +Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba" +OptimizationMOI = "fd9f6733-72f4-499f-8506-86b2bdd0dea1" +OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a" +SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[sources.Corleone] +path = "../.." + +[targets] +test = ["Aqua", "SafeTestsets", "Test"] diff --git a/lib/OptimalControlBenchmarks/src/OptimalControlBenchmarks.jl b/lib/OptimalControlBenchmarks/src/OptimalControlBenchmarks.jl new file mode 100644 index 00000000..ebbabbcc --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/OptimalControlBenchmarks.jl @@ -0,0 +1,12 @@ +module OptimalControlBenchmarks + +using ModelingToolkit + +include("types.jl") +include("problem_registry.jl") +include("solver_corleone.jl") +include("run_benchmarks.jl") + +export load_benchmarks, run_all + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problem_registry.jl b/lib/OptimalControlBenchmarks/src/problem_registry.jl new file mode 100644 index 00000000..cf0fe8d3 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problem_registry.jl @@ -0,0 +1,18 @@ +function load_benchmarks() + + benchmarks = [] + + for f in readdir(joinpath(@__DIR__,"problems")) + endswith(f,".jl") || continue + + modname = Symbol(splitext(f)[1]) + + include(joinpath(@__DIR__,"problems",f)) + + mod = getfield(@__MODULE__, modname) + + push!(benchmarks, mod.benchmark) + end + + benchmarks +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl b/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl new file mode 100644 index 00000000..9c67748c --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl @@ -0,0 +1,52 @@ +module batch_reactor + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 2 + num_controls = 1 + + @variables begin + x₁(..) = 1.0, [tunable = false] + x₂(..) = 1.0, [tunable = false] + u(..) = 298.0, [bounds = (298.0, 398.0), input = true] + end + + eqs = [ + D(x₁(t)) ~ -4.e3 * exp(-2500 / u(t)) * x₁(t)^2 + D(x₂(t)) ~ 4.e3 * exp(-2500 / u(t)) * x₁(t)^2 - 62.e4 * exp(-5000 / u(t)) * x₂(t)^2 + ] + + tspan = (0.,1.) + dt = 0.01 + cgrid = collect(0.0:dt:last(tspan))[1:end-1] + + costs = [-x₂(1.0)] + + @named oc_problem = System( + eqs, + t; + costs = costs + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :batch_reactor, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl b/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl new file mode 100644 index 00000000..a6b7392a --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl @@ -0,0 +1,63 @@ +module bryson_denham + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 2 + num_controls = 1 + + @variables begin + x(..) = 0.0, [tunable = false] + v(..) = 1.0, [tunable = false] + w(..) = 0.0, [input = true] + end + + eqs = [ + D(x(t)) ~ v(t) + D(v(t)) ~ w(t) + ] + + tspan = (0.,1.) + dt = 0.01 + cgrid = collect(0.0:dt:last(tspan))[1:end-1] + + grid_cons = [x(tᵢ) ≲ 1/9 for tᵢ in cgrid] + + cons = [ + x(last(tspan)) ~ 0., + v(last(tspan)) ~ -1., + grid_cons... + ] + + costs = [ + Symbolics.Integral(t in (0.0, 1.0))(w(t)^2) + ] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :bryson_denham, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl b/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl new file mode 100644 index 00000000..1d303dc1 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl @@ -0,0 +1,79 @@ +module cart_pendulum + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 4 + num_controls = 1 + + @variables begin + x(..) = 0.0, [tunable = false] + θ(..) = 0.0, [tunable = false] + dx(..) = 0.0, [tunable = false] + dtheta(..) = 0.0, [tunable = false] + w(..) = 0.0, [bounds=(-30,30), input = true] + end + + @parameters begin + α = 10., [tunable = false] + β = 50., [tunable = false] + γ = 0.5, [tunable = false] + M = 1., [tunable = false] + m = 0.1, [tunable = false] + g = 9.81, [tunable = false] + end + + eqs = [ + D(x(t)) ~ dx(t) + D(θ(t)) ~ dtheta(t) + D(dx(t)) ~ (w(t) + m * g * sin(θ(t)) * cos(θ(t)) + m * dtheta(t)^2 * sin(θ(t))) / (M + m * (1 - cos(θ(t)))^2) + D(dtheta(t)) ~ -g * sin(θ(t)) - ((w(t) + m * g * sin(θ(t)) * cos(θ(t)) + m * dtheta(t) * sin(θ(t))) / (M + m * (1 - cos(θ(t))^2))) * cos(θ(t)) + ] + + # Define control discretization + tspan = (0.,4.) + dt = (last(tspan) - first(tspan)) / 40 + cgrid = collect(0.0:dt:last(tspan))[1:end-1] + + grid_cons_le = [x(tᵢ) ≲ 2. for tᵢ in vcat(cgrid, last(tspan))] + grid_cons_ge = [x(tᵢ) ≳ -2. for tᵢ in vcat(cgrid, last(tspan))] + + cons = [ + grid_cons_le..., + grid_cons_ge... + ] + + costs = [ + Symbolics.Integral(t in (0.0, 1.0))( + α * x(t)^2 + β * (θ(t) - pi)^2 + γ * w(t)^2 + ), + ] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :cart_pendulum, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl b/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl new file mode 100644 index 00000000..590731c4 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl @@ -0,0 +1,52 @@ +module catalyst_mixing + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 2 + num_controls = 1 + + @variables begin + x₁(..) = 1.0, [tunable = false] + x₂(..) = 0.0, [tunable = false] + w(..) = 0.0, [bounds = (0.0, 1.0), input = true] + end + + eqs = [ + D(x₁(t)) ~ w(t) * (10 * x₂(t) - x₁(t)) + D(x₂(t)) ~ w(t) * (x₁(t) - 10 * x₂(t)) - (1 - w(t)) * x₂(t) + ] + + # Define control discretization + tspan = (0.,1.) + dt = 0.02 + cgrid = collect(0.0:dt:last(tspan))[1:end-1] + + costs = [-1 + x₁(last(tspan)) + x₂(last(tspan))] + + @named oc_problem = System( + eqs, + t; + costs = costs + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + +benchmark = OptimalControlBenchmark( + :catalyst_mixing, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl b/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl new file mode 100644 index 00000000..eb3f81ea --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl @@ -0,0 +1,68 @@ +module cushioned_oscillation + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 3 + num_controls = 1 + + @variables begin + x(..) = 2.0, [tunable = false] + v(..) = 5.0, [tunable = false] + obj(..) = 0.0, [tunable = false] + u(..) = 0.0, [bounds = (-5.0, 5.0), input = true] + end + @parameters begin + tₛ = 1., [bounds = (1.e-3, Inf), tunable = true] + end + @constants begin + m = 5., [tunable = false] + c = 10., [tunable = false] + end + + eqs = [ + D(x(t)) ~ tₛ * v(t) + D(v(t)) ~ tₛ * (1 / m * (u(t) - c * x(t))) + D(obj(t)) ~ tₛ + ] + + # Define control discretization + tspan = (0.,10.) + dt = 0.2 + cgrid = collect(0.0:dt:last(tspan))[1:end-1] + + cons = [ + x(last(tspan)) ~ 0., + v(last(tspan)) ~ 0., + ] + + costs = [obj(1.)] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :cushioned_oscillation, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/denbigh.jl b/lib/OptimalControlBenchmarks/src/problems/denbigh.jl new file mode 100644 index 00000000..a472e731 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/denbigh.jl @@ -0,0 +1,66 @@ +module denbigh + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + # We start by defining our system + num_states = 3 + num_controls = 1 + + @variables begin + x₁(..) = 1.0, [tunable = false] + x₂(..) = 0.0, [tunable = false] + x₃(..) = 0.0, [tunable = false] + T(..) = 300.0, [bounds = (273.0, 415.0), input = true] + end + @parameters begin + tₛ = 1., [bounds = (1.e-3, Inf), tunable = true] + end + @constants begin + E[1:4] = [3.e3, 6.e3, 3.e3, 0.], [tunable = false] + k⁰[1:4] = [1.e3, 1.e7, 1.e1, 1.e-3], [tunable = false] + end + + # auxiliary equations for the kᵢ + k = [k⁰[i] * exp(-E[i] / T(t)) for i in [1,2,3,4]] + + eqs = [ + D(x₁(t)) ~ -k[1] * x₁(t) - k[2] * x₁(t) + D(x₂(t)) ~ k[1] * x₁(t) - k[3] + k[4] * x₂(t) + D(x₃(t)) ~ k[3] * x₂(t) + ] + + # Define control discretization + tspan = (0.,1000.) + dt = 10. + cgrid = collect(0.0:dt:last(tspan))[1:end-1] + + costs = [-x₃(last(tspan))] + + @named oc_problem = System( + eqs, + t; + costs = costs + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :denbigh, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl b/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl new file mode 100644 index 00000000..91735554 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl @@ -0,0 +1,67 @@ +module dielectrophoretic_particle + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 3 + num_controls = 1 + + @variables begin + x₀(..) = 1.0, [tunable = false] + x₁(..) = 0.0, [tunable = false] + obj(..) = 0.0, [tunable = false] + u(..) = 1.0, [bounds = (-1.0, 1.0), input = true] + end + @parameters begin + tₛ = 1., [bounds = (1.e-1, Inf), tunable = true] + end + @constants begin + α = -0.75, [tunable = false] + c = 1., [tunable = false] + end + + eqs = [ + D(x₀(t)) ~ tₛ * (x₁(t) * u(t) + α * u(t)^2) + D(x₁(t)) ~ tₛ * (-c * x₁(t) + u(t)) + D(obj(t)) ~ tₛ + ] + + # Define control discretization + tspan = (0.,5.) + dt = 0.1 + cgrid = collect(0.0:dt:last(tspan))[1:end-1] + + cons = [ + x₀(last(tspan)) ~ 2., + ] + + costs = [obj(1.)] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :dielectrophoretic_particle, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl b/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl new file mode 100644 index 00000000..0f2fa3a9 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl @@ -0,0 +1,70 @@ +module double_oscillator + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 5 + num_controls = 1 + + @variables begin + x₀(..) = 0.0, [tunable = false] + x₁(..) = 0.0, [tunable = false] + x₂(..) = 0.0, [tunable = true] + x₃(..) = 0.0, [tunable = true] + u(..) = 1.0, [bounds = (-1.0, 1.0), input = true] + end + + @constants begin + m₁ = 200., [tunable = false] + m₂ = 2., [tunable = false] + k₁ = 100., [tunable = false] + k₂ = 3., [tunable = false] + c = 0.5, [tunable = false] + T = 2 * pi, [tunable = false] + end + + eqs = [ + D(x₀(t)) ~ x₂(t) + D(x₁(t)) ~ x₃(t) + D(x₂(t)) ~ - (k₁ + k₂) / m₁ * x₀(t) + k₂ / m₁ * x₁(t) + 1 / m₁ * sin(2 * pi / T * t) + D(x₃(t)) ~ k₂ / m₂ * x₀(t) - k₂ / m₂ * x₁(t) - c * (1 - u(t)) / m₂ * x₃(t) + ] + + # Define control discretization + tspan = (0.,2 * pi) + dt = 0.1 + cgrid = collect(0.0:dt:last(tspan))[1:end-1] + + costs = [ + Symbolics.Integral(t in (0.0, 1.0))( + 0.5 * (x₀(t)^2 + x₁(t)^2 + u(t)^2) + ), + ] + + @named oc_problem = System( + eqs, + t; + costs = costs + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :double_oscillator, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl b/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl new file mode 100644 index 00000000..ec97a1ff --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl @@ -0,0 +1,91 @@ +module ducted_fan + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 7 + num_controls = 2 + + @variables begin + x₁(..) = 0.0, [tunable = false] + v₁(..) = 0.0, [tunable = false] + x₂(..) = 0.0, [tunable = false] + v₂(..) = 0.0, [tunable = false] + α(..) = 0.0, [tunable = false] + vα(..) = 0.0, [tunable = false] + u₁(..) = 0.0, [bounds = (-5.0, 5.0), input = true] + u₂(..) = 8.5, [bounds = (0.0, 17.0), input = true] + end + @parameters begin + tₛ = 0.5, [bounds = (1.e-3, Inf), tunable = true] + end + @constants begin + m = 2.2, [tunable = false] + J = 0.05, [tunable = false] + r = 0.2, [tunable = false] + mg = 4., [tunable = false] + μ = 1., [tunable = false] + end + + eqs = [ + D(x₁(t)) ~ tₛ * (v₁(t)) + D(v₁(t)) ~ tₛ * (1 / m * (u₁(t) * cos(α(t)) - u₂(t) * sin(α(t)))) + D(x₂(t)) ~ tₛ * (v₂(t)) + D(v₂(t)) ~ tₛ * (1 / m * (-mg + u₁(t) * sin(α(t)) + u₂(t) * cos(α(t)))) + D(α(t)) ~ tₛ * (vα(t)) + D(vα(t)) ~ tₛ * (r / J * u₁(t)) + ] + + # Define control discretization + tspan = (0.,10.) + dt = 0.1 + cgrid = collect(0.0:dt:last(tspan))[1:end-1] + + grid_cons_u = [α(tᵢ) ≲ 30. for tᵢ in cgrid] + grid_cons_l = [α(tᵢ) ≳ -30. for tᵢ in cgrid] + + cons = [ + x₁(last(tspan)) ~ 1., + v₁(last(tspan)) ~ 0., + x₂(last(tspan)) ~ 0., + v₂(last(tspan)) ~ 0., + α(last(tspan)) ~ 0., + vα(last(tspan)) ~ 0., + grid_cons_u..., + grid_cons_l... + ] + + costs = [ + Symbolics.Integral(t in (0.0, last(tspan)))( + (2 * u₁(t)^2 + u₂(t)^2) + μ * tₛ + ), + ] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :ducted_fan, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl b/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl new file mode 100644 index 00000000..9ea835fd --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl @@ -0,0 +1,66 @@ +module egerstedt + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 3 + num_controls = 3 + + @variables begin + x(..) = 0.5, [tunable = false] + y(..) = 0.5, [tunable = false] + u_1(..) = 1 / 3, [bounds = (-1.0, 1.0), input = true] + u_2(..) = 1 / 3, [bounds = (-1.0, 1.0), input = true] + u_3(..) = 1 / 3, [bounds = (-1.0, 1.0), input = true] + end + + eqs = [ + D(x(t)) ~ -x(t) * u_1(t) + (x(t) + y(t)) * u_2(t) + (x(t) - y(t)) * u_3(t) + D(y(t)) ~ (x(t) + 2 * y(t)) * u_1(t) + (x(t) - 2 * y(t)) * u_2(t) + (x(t) + y(t)) * u_3(t) + ] + + # Define control discretization + tspan = (0.,1.) + dt = 0.1 + cgrid = collect(0.0:dt:last(tspan))[1:end-1] + + grid_cons = [u_1(tᵢ) + u_2(tᵢ) + u_3(tᵢ) ~ 1. for tᵢ in cgrid] + + cons = [ + grid_cons... + ] + + costs = [ + Symbolics.Integral(t in (0.0, 1.0))( + x(t)^2 + y(t)^2 + ), + ] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :egerstedt, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/electric_car.jl b/lib/OptimalControlBenchmarks/src/problems/electric_car.jl new file mode 100644 index 00000000..ea7bfea7 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/electric_car.jl @@ -0,0 +1,89 @@ +module egerstedt + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 4 + num_controls = 1 + + @variables begin + x_1(..) = 0.0, [tunable = false] + x_2(..) = 0.0, [tunable = false] + x_3(..) = 0.0, [tunable = false] + u(..) = 0.0, [bounds = (-5.0, 5.0), input = true] + end + + @constants begin + K_r = 10., [tunable = false] + rho = 1.293, [tunable = false] + C_x = 0.4, [tunable = false] + S = 2., [tunable = false] + r = 0.33, [tunable = false] + K_f = 0.03, [tunable = false] + K_m = 0.27, [tunable = false] + R_m = 0.03, [tunable = false] + L_m = 0.05, [tunable = false] + M = 250., [tunable = false] + g = 9.81, [tunable = false] + V_alim = 150., [tunable = false] + R_bat = 0.05, [tunable = false] + end + + temp = K_m * x_1(t) + temp += - r / K_r * (M * g * K_f + 0.5 * rho * S * C_x * r^2 / K_r^2 * x_2(t)^2) + temp *= K_r^2 / (M * r^2) + + eqs = [ + D(x_1(t)) ~ (V_alim * u(t) - R_m * x_1(t) - K_m * x_2(t)) / L_m + D(x_2(t)) ~ temp + D(x_3(t)) ~ r / K_r * x_2(t) + ] + + # Define control discretization + tspan = (0.,10.) + dt = 0.2 + cgrid = collect(0.0:dt:last(tspan)) + + grid_cons_u = [x_1(tᵢ) ≲ 150. for tᵢ in cgrid] + grid_cons_l = [x_1(tᵢ) ≳ -150. for tᵢ in cgrid] + + cons = [ + x_3(last(tspan)) ~ 100., + grid_cons_u..., + grid_cons_l... + ] + + costs = [ + Symbolics.Integral(t in (0.0, last(tspan)))( + V_alim * u(t) * x_1(t) + R_bat * x_1(t)^2 + ), + ] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :egerstedt, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/run_benchmarks.jl b/lib/OptimalControlBenchmarks/src/run_benchmarks.jl new file mode 100644 index 00000000..189db2ff --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/run_benchmarks.jl @@ -0,0 +1,23 @@ +#using BenchmarkTools +using OptimalControlBenchmarks + +function run_all(benchmarks) + + results = [] + + for prob in benchmarks + + println("Running ", prob.name) + sol = solve_with_corleone(prob) + println(sol) + #t = @belapsed solve_with_corleone($prob) + + push!(results, ( + name = prob.name, + #time = t + )) + + end + + return results +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/solver_corleone.jl b/lib/OptimalControlBenchmarks/src/solver_corleone.jl new file mode 100644 index 00000000..2de511d0 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/solver_corleone.jl @@ -0,0 +1,49 @@ +using ModelingToolkit +using ModelingToolkit: inputs +using Corleone +using OrdinaryDiffEqTsit5 +using Optimization +using OptimizationMOI +using Ipopt +using ForwardDiff +using ComponentArrays +using LuxCore, Random + +function solve_with_corleone(benchmark) + + data = benchmark.make_problem() + + oc_problem = data.system + cgrid = data.control_grid + + # Extract control variable + controls = inputs(oc_problem) + + control_map = [ + c => cgrid for c in controls + ] + + dynopt = CorleoneDynamicOptProblem( + oc_problem, + [], + control_map...; + algorithm = Tsit5() + ) + + optprob = OptimizationProblem( + dynopt, + AutoForwardDiff(), + Val(:ComponentArrays) + ) + + sol = solve( + optprob, + Ipopt.Optimizer(), + max_iter = 1000, + tol = 5e-6, + hessian_approximation = "limited-memory" + ) + + return sol + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/types.jl b/lib/OptimalControlBenchmarks/src/types.jl new file mode 100644 index 00000000..797c295d --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/types.jl @@ -0,0 +1,5 @@ +struct OptimalControlBenchmark + name::Symbol + description::String + make_problem::Function +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/test.jl b/lib/OptimalControlBenchmarks/test.jl new file mode 100644 index 00000000..9e3493b2 --- /dev/null +++ b/lib/OptimalControlBenchmarks/test.jl @@ -0,0 +1,9 @@ +using OptimalControlBenchmarks +using Corleone +using ModelingToolkit + +benchmarks = load_benchmarks() + +df = run_all(benchmarks) + +println(df) \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/test/runtests.jl b/lib/OptimalControlBenchmarks/test/runtests.jl new file mode 100644 index 00000000..9c4307f6 --- /dev/null +++ b/lib/OptimalControlBenchmarks/test/runtests.jl @@ -0,0 +1,4 @@ +using OptimalControlBenchmarks +using Test + +@test 1==1 \ No newline at end of file From 2c91b589f8f82e8121d21af0d5428b7e1d711574 Mon Sep 17 00:00:00 2001 From: Robert Lampel Date: Mon, 9 Mar 2026 17:01:47 +0100 Subject: [PATCH 02/73] added first draft of all problems --- .../src/problems/electric_car.jl | 4 +- .../src/problems/f8.jl | 81 ++++++++++++++++ .../src/problems/fuller.jl | 63 +++++++++++++ .../src/problems/goddarts_rocket.jl | 81 ++++++++++++++++ .../src/problems/hang_glider.jl | 92 ++++++++++++++++++ .../src/problems/hanging_chain.jl | 77 +++++++++++++++ .../src/problems/jackson.jl | 74 +++++++++++++++ .../src/problems/lotka_competitive.jl | 64 +++++++++++++ .../src/problems/lotka_shared.jl | 65 +++++++++++++ .../src/problems/lotka_volterra.jl | 62 +++++++++++++ .../src/problems/lqr.jl | 60 ++++++++++++ .../src/problems/moon_landing.jl | 65 +++++++++++++ .../src/problems/mountain_car.jl | 65 +++++++++++++ .../src/problems/ocean.jl | 93 +++++++++++++++++++ .../src/problems/particle_steering.jl | 76 +++++++++++++++ .../src/problems/quadrotor.jl | 85 +++++++++++++++++ .../src/problems/rao_mease.jl | 60 ++++++++++++ .../src/problems/robbins.jl | 75 +++++++++++++++ .../src/problems/three_tank.jl | 79 ++++++++++++++++ .../src/problems/tubular_reactor.jl | 53 +++++++++++ .../src/problems/van_der_pol.jl | 65 +++++++++++++ 21 files changed, 1437 insertions(+), 2 deletions(-) create mode 100644 lib/OptimalControlBenchmarks/src/problems/f8.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/fuller.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/hang_glider.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/hanging_chain.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/jackson.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/lqr.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/moon_landing.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/mountain_car.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/ocean.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/particle_steering.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/quadrotor.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/rao_mease.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/robbins.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/three_tank.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl create mode 100644 lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl diff --git a/lib/OptimalControlBenchmarks/src/problems/electric_car.jl b/lib/OptimalControlBenchmarks/src/problems/electric_car.jl index ea7bfea7..64da184a 100644 --- a/lib/OptimalControlBenchmarks/src/problems/electric_car.jl +++ b/lib/OptimalControlBenchmarks/src/problems/electric_car.jl @@ -1,4 +1,4 @@ -module egerstedt +module electric_car using ModelingToolkit using ModelingToolkit: t_nounits as t, D_nounits as D @@ -81,7 +81,7 @@ end benchmark = OptimalControlBenchmark( - :egerstedt, + :electric_car, "Double integrator with quadratic control cost", make_problem ) diff --git a/lib/OptimalControlBenchmarks/src/problems/f8.jl b/lib/OptimalControlBenchmarks/src/problems/f8.jl new file mode 100644 index 00000000..46cce473 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/f8.jl @@ -0,0 +1,81 @@ +module f8 + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 4 + num_controls = 1 + + @variables begin + x_0(..) = 0.4655, [tunable = false] + x_1(..) = 0.0, [tunable = false] + x_2(..) = 0.0, [tunable = false] + obj(..) = 0.0, [tunable = false] + u(..) = 0.5, [bounds = (0., 1.0), input = true] + end + + @parameters begin + T = 0.5, [bounds =(1.e-3, Inf), tunable = true] + end + + @constants begin + ξ = 0.05236, [tunable = false] + end + + du_1 = -0.877 * x_0(t) + x_2(t) - 0.088 * x_0(t) * x_2(t) + 0.47 * x_0(t)^2 - 0.019 * x_1(t)^2 + du_1 += - x_0(t)^2 * x_2(t) + 3.846 * x_0(t)^3 + du_1 += 0.215 * ξ - 0.28 * x_0(t)^2 * ξ + 0.47 * x_0(t) * ξ^2 - 0.63 * ξ^3 + du_1 += - (0.215 * ξ - 0.28 * x_0(t)^2 * ξ - 0.63 * ξ^3) * 2 * u(t) + du_3 = -4.208 * x_0(t) - 0.396 * x_2(t) - 0.47 * x_0(t)^2 - 3.564 * x_0(t)^3 + du_3 += 20.967 * ξ - 6.265 * x_0(t)^2 * ξ + 46. * x_0(t) * ξ^2 - 61.4 * ξ^3 + du_3 += -(20.967 * ξ - 6.265 * x_0(t)^2 * ξ - 61.4 * ξ^3) * 2 * u(t) + + + eqs = [ + D(x_0(t)) ~ T * du_1 + D(x_1(t)) ~ T * x_2(t) + D(x_2(t)) ~ T * du_3 + D(obj(t)) ~ T + ] + + # Define control discretization + tspan = (0.,10.) + dt = 0.1 + cgrid = collect(0.0:dt:last(tspan)) + + cons = [ + x_0(last(tspan)) ~ 0., + x_1(last(tspan)) ~ 0., + x_2(last(tspan)) ~ 0., + ] + + costs = [obj(last(tspan))] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :f8, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/fuller.jl b/lib/OptimalControlBenchmarks/src/problems/fuller.jl new file mode 100644 index 00000000..1b2c7c6a --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/fuller.jl @@ -0,0 +1,63 @@ +module fuller + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 3 + num_controls = 1 + + @variables begin + x₀(..) = 0.01, [tunable = false] + x₁(..) = 0.0, [tunable = false] + u(..) = 0.5, [bounds = (0., 1.0), input = true] + end + + eqs = [ + D(x₀(t)) ~ x₁(t) + D(x₁(t)) ~ 1. - 2. * u(t) + ] + + # Define control discretization + tspan = (0.,1.) + dt = 0.02 + cgrid = collect(0.0:dt:last(tspan)) + + cons = [ + x₀(last(tspan)) ~ 0.01, + x₁(last(tspan)) ~ 0., + ] + + costs = [ + Symbolics.Integral(t in (0.0, last(tspan)))( + x₀(t)^2 + ), + ] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :fuller, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl b/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl new file mode 100644 index 00000000..9b23c850 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl @@ -0,0 +1,81 @@ +module goddarts_rocket + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 3 + num_controls = 1 + + @variables begin + r(..) = 1., [tunable = false] + v(..) = 0., [tunable = false] + m(..) = 1., [tunable = false] + u(..) = 0.5, [bounds = (0., 1.0), input = true] + end + + @parameters begin + T = 0.5, [bounds =(1.e-3, Inf), tunable = true] + end + + @constants begin + r_0 = 1., [tunable = false] + v_0 = 0., [tunable = false] + m_0 = 1., [tunable = false] + r_T = 1.01, [tunable = false] + b = 7., [tunable = false] + T_max = 3.5, [tunable = false] + A = 310., [tunable = false] + k = 500., [tunable = false] + C = 0.6, [tunable = false] + end + + Drag = A * v(t)^2 * exp(-k * (r(t) - r_0)) + + eqs = [ + D(r(t)) ~ T * v(t) + D(v(t)) ~ T * (-1 / r(t)^2 + 1 / m(t) * (T_max * u(t) - Drag)) + D(m(t)) ~ T * (-b * u(t)) + ] + + # Define control discretization + tspan = (0.,10.) + dt = 0.1 + cgrid = collect(0.0:dt:last(tspan)) + + grid_cons_u = [310. * v(tᵢ)^2 * 2.7182818^(-500. * (r(tᵢ) - 1.)) ≲ 0.6 for tᵢ in cgrid] + + cons = [ + r(last(tspan)) ~ 1.01, + grid_cons_u... + ] + + costs = [-m(last(tspan))] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :goddarts_rocket, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl b/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl new file mode 100644 index 00000000..28dfc2b0 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl @@ -0,0 +1,92 @@ +module hang_glider + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 4 + num_controls = 1 + + @variables begin + x(..) = 0., [tunable = false] + y(..) = 1.e3, [tunable = false] + v_x(..) = 13.23, [tunable = false] + v_y(..) = -1.288, [tunable = false] + c_L(..) = 0.5, [bounds = (0., 1.4), input = true] + end + + @parameters begin + T = 0.5, [bounds =(1.e-3, Inf), tunable = true] + end + + @constants begin + u_c = 2.5, [tunable = false] + r_c = 100., [tunable = false] + c_0 = 0.034, [tunable = false] + c_1 = 0.069662, [tunable = false] + S = 14., [tunable = false] + rho = 1.13, [tunable = false] + m = 100., [tunable = false] + g = 9.81, [tunable = false] + end + + # auxiliary functions + r = (x(t) / r_c - 2.5)^2 + U_up = u_c * (1 - r) * exp(-r) + w = v_y(t) - U_up + v = sqrt(v_x(t)^2 + w^2) + Dr = 0.5 * rho * S * (c_0 + c_1 * c_L(t)^2) * v^2 + L = 0.5 * rho * S * c_L(t) * v^2 + + eqs = [ + D(x(t)) ~ T * v_x(t) + D(y(t)) ~ T * v_y(t) + D(v_x(t)) ~ T * (-1.) * (L * w + Dr * v_x(t)) / (m * v) + D(v_y(t)) ~ T * ((L * v_x(t) - Dr * w) / (m * v) - g) + ] + + # Define control discretization + tspan = (0.,100.) + dt = 2. + cgrid = collect(0.0:dt:last(tspan))[1:end-1] + + grid_cons_x = [x(tᵢ) ≳ 0. for tᵢ in cgrid] + grid_cons_vx = [v_x(tᵢ) ≳ 0. for tᵢ in cgrid] + + cons = [ + y(last(tspan)) ~ 900., + v_x(last(tspan)) ~ 13.23, + v_y(last(tspan)) ~ -1.288, + grid_cons_x..., + grid_cons_vx... + ] + + costs = [-x(last(tspan))] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :hang_glider, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/hanging_chain.jl b/lib/OptimalControlBenchmarks/src/problems/hanging_chain.jl new file mode 100644 index 00000000..f65be7e2 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/hanging_chain.jl @@ -0,0 +1,77 @@ +module hanging_chain + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 3 + num_controls = 1 + + @variables begin + x₁(..) = 1., [tunable = false] + x₂(..) = 0., [tunable = false] + x₃(..) = 0., [tunable = false] + u(..) = 0.5, [bounds = (-10., 20.), input = true] + end + + @constants begin + a = 1., [tunable = false] + b = 3., [tunable = false] + L_p = 4., [tunable = false] + end + + eqs = [ + D(x₁(t)) ~ u(t) + D(x₂(t)) ~ x₁(t) * sqrt(1 + u(t)^2) + D(x₃(t)) ~ sqrt(1 + u(t)^2) + ] + + # Define control discretization + tspan = (0.,1.) + dt = 0.02 + cgrid = collect(0.0:dt:last(tspan))[1:end-1] + + # grid_cons_u = [A * v(tᵢ)^2 * exp(-k * (r(tᵢ) - r_0)) ≲ 0.6 for tᵢ in cgrid] + grid_cons_u1 = [x₁(tᵢ) ≳ 0. for tᵢ in cgrid] + grid_cons_u2 = [x₂(tᵢ) ≳ 0. for tᵢ in cgrid] + grid_cons_u3 = [x₃(tᵢ) ≳ 0. for tᵢ in cgrid] + grid_cons_l1 = [x₁(tᵢ) ≲ 10. for tᵢ in cgrid] + grid_cons_l2 = [x₂(tᵢ) ≲ 10. for tᵢ in cgrid] + grid_cons_l3 = [x₃(tᵢ) ≲ 10. for tᵢ in cgrid] + + cons = [ + x₁(last(tspan)) - 3. ~ 0., + x₃(last(tspan)) - 4. ~ 0., + grid_cons_u1..., grid_cons_u2..., grid_cons_u3..., + grid_cons_l1..., grid_cons_l2..., grid_cons_l3... + ] + + costs = [x₂(last(tspan))] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :hanging_chain, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/jackson.jl b/lib/OptimalControlBenchmarks/src/problems/jackson.jl new file mode 100644 index 00000000..a3e20550 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/jackson.jl @@ -0,0 +1,74 @@ +module jackson + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 3 + num_controls = 1 + + @variables begin + x₁(..) = 1., [tunable = false] + x₂(..) = 0., [tunable = false] + x₃(..) = 0., [tunable = false] + u(..) = 0.5, [bounds = (0., 1.), input = true] + end + + @constants begin + k₁ = 1., [tunable = false] + k₂ = 10., [tunable = false] + k₃ = 1., [tunable = false] + end + + eqs = [ + D(x₁(t)) ~ -u(t) * (k₁ * x₁(t) -k₂ * x₂(t)) + D(x₂(t)) ~ u(t) * (k₁ * x₁(t) - k₂ * x₂(t)) - (1 - u(t)) * k₃ * x₂(t) + D(x₃(t)) ~ (1- u(t)) * k₃ * x₂(t) + ] + + # Define control discretization + tspan = (0.,1.) + dt = 0.02 + cgrid = collect(0.0:dt:last(tspan)) + + grid_cons_u1 = [x₁(tᵢ) ≲ 1.1 for tᵢ in cgrid] + grid_cons_u2 = [x₂(tᵢ) ≲ 1.1 for tᵢ in cgrid] + grid_cons_u3 = [x₃(tᵢ) ≲ 1.1 for tᵢ in cgrid] + grid_cons_l1 = [x₁(tᵢ) ≳ 0. for tᵢ in cgrid] + grid_cons_l2 = [x₂(tᵢ) ≳ 0. for tᵢ in cgrid] + grid_cons_l3 = [x₃(tᵢ) ≳ 0. for tᵢ in cgrid] + + cons = [ + grid_cons_u1..., grid_cons_u2..., grid_cons_u3..., + grid_cons_l1..., grid_cons_l2..., grid_cons_l3... + ] + + costs = [x₃(last(tspan))] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :jackson, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl b/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl new file mode 100644 index 00000000..8fe57e44 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl @@ -0,0 +1,64 @@ +module lotka_competitive + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 3 + num_controls = 1 + + @variables begin + x₀(..) = 0.5, [tunable = false] + x₁(..) = 1.5, [tunable = false] + u(..) = 0.5, [bounds = (0., 1.), input = true] + end + + @constants begin + c₁ = 0.1, [tunable = false] + c₂ = 0.4, [tunable = false] + α = 1.2, [tunable = false] + K = 1.8, [tunable = false] + end + + eqs = [ + D(x₀(t)) ~ x₀(t) * (1 - (x₀(t) + α * x₁(t)) / K) - c₁ * x₀(t) * u(t) + D(x₁(t)) ~ x₁(t) * (1 - (x₀(t) + x₁(t)) / K) - c₂ * x₁(t) * u(t) + ] + + # Define control discretization + tspan = (0.,40.) + dt = 0.8 + cgrid = collect(0.0:dt:last(tspan)) + + costs = [ + Symbolics.Integral(t in (0.0, last(tspan)))( + (x₀(t) - 1.)^2 + (x₁(t) - 1.)^2 + ), + ] + + @named oc_problem = System( + eqs, + t; + costs = costs + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :lotka_competitive, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl b/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl new file mode 100644 index 00000000..4992ce62 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl @@ -0,0 +1,65 @@ +module lotka_shared + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 4 + num_controls = 1 + + @variables begin + x₀(..) = 1.5, [tunable = false] + x₁(..) = 1., [tunable = false] + x₂(..) = 0.5, [tunable = false] + u(..) = 0.5, [bounds = (0., 1.), input = true] + end + + @constants begin + c₁ = 0.1, [tunable = false] + c₂ = 0.4, [tunable = false] + α = 1.2, [tunable = false] + end + + eqs = [ + D(x₀(t)) ~ x₀(t) - x₀(t) * x₁(t) - x₀(t) * x₂(t) + D(x₁(t)) ~ -x₁(t) + x₀(t) * x₁(t) - c₁ * x₁(t) * u(t) + D(x₂(t)) ~ -x₂(t) + α * x₀(t) * x₂(t) - c₂ * x₂(t) * u(t) + ] + + # Define control discretization + tspan = (0.,40.) + dt = 0.8 + cgrid = collect(0.0:dt:last(tspan)) + + costs = [ + Symbolics.Integral(t in (0.0, last(tspan)))( + (x₀(t) - 1.5)^2 + (x₁(t) - 1.)^2 + (x₂(t) - 1.)^2 + ), + ] + + @named oc_problem = System( + eqs, + t; + costs = costs + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :lotka_shared, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl b/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl new file mode 100644 index 00000000..ef547634 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl @@ -0,0 +1,62 @@ +module goddarts_rocket + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 3 + num_controls = 1 + + @variables begin + x₀(..) = 0.5, [tunable = false] + x₁(..) = 0.7, [tunable = false] + u(..) = 0.5, [bounds = (0., 1.), input = true] + end + + @constants begin + c₀ = 0.4, [tunable = false] + c₁ = 0.2, [tunable = false] + end + + eqs = [ + D(x₀(t)) ~ x₀(t) - x₀(t) * x₁(t) - c₀ * x₀(t) * u(t) + D(x₁(t)) ~ -x₁(t) + x₀(t) * x₁(t) - c₁ * x₁(t) * u(t) + ] + + # Define control discretization + tspan = (0.,12.) + dt = 0.24 + cgrid = collect(0.0:dt:last(tspan)) + + costs = [ + Symbolics.Integral(t in (0.0, last(tspan)))( + (x₀(t) - 1.)^2 + (x₁(t) - 1.)^2 + ), + ] + + @named oc_problem = System( + eqs, + t; + costs = costs + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :goddarts_rocket, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/lqr.jl b/lib/OptimalControlBenchmarks/src/problems/lqr.jl new file mode 100644 index 00000000..415d2eb1 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/lqr.jl @@ -0,0 +1,60 @@ +module lqr + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 2 + num_controls = 1 + + @variables begin + x(..) = 1., [tunable = false] + u(..) = 0.5, [input = true] + end + + @constants begin + a = -1., [tunable = false] + b = 1., [tunable = false] + end + + eqs = [ + D(x(t)) ~ a * x(t) + b * u(t) + ] + + # Define control discretization + tspan = (0.,10.) + dt = 0.2 + cgrid = collect(0.0:dt:last(tspan)) + + costs = [ + Symbolics.Integral(t in (0.0, last(tspan)))( + 10. * (x(t) - 3.)^2 + 0.1 * u(t)^2 + ), + ] + + @named oc_problem = System( + eqs, + t; + costs = costs + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :lqr, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl b/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl new file mode 100644 index 00000000..1b765faa --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl @@ -0,0 +1,65 @@ +module moon_landing + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 3 + num_controls = 1 + + @variables begin + h(..) = 1., [tunable = false] + v(..) = -0.783, [tunable = false] + m(..) = 1., [tunable = false] + T(..) = 0.5, [bounds = (0., 1.227), input = true] + end + + @parameters begin + tₛ = 1., [bounds = (1.e-3, Inf), tunable = true] + end + + eqs = [ + D(h(t)) ~ tₛ * v(t) + D(v(t)) ~ tₛ * (-1 + T(t) / m(t)) + D(m(t)) ~ tₛ * (- T(t) / 2.349) + ] + + # Define control discretization + tspan = (0.,1.) + dt = 0.02 + cgrid = collect(0.0:dt:last(tspan)) + + cons = [ + h(last(tspan)) ~ 0., + v(last(tspan)) ~ 0. + ] + + costs = [-m(last(tspan))] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :moon_landing, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl b/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl new file mode 100644 index 00000000..aa09af33 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl @@ -0,0 +1,65 @@ +module mountain_car + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 3 + num_controls = 1 + + @variables begin + x(..) = -0.5, [tunable = false] + v(..) = 0., [tunable = false] + obj(..) = 0., [tunable = false] + u(..) = 0.5, [bounds = (-1., 1.), input = true] + end + + @parameters begin + tₛ = 1., [bounds = (1.e-3, Inf), tunable = true] + end + + eqs = [ + D(x(t)) ~ tₛ * v(t) + D(v(t)) ~ tₛ * (1.e-3 * u(t) - 2.5e-3 * cos(3 * x(t))) + D(obj(t)) ~ tₛ + ] + + # Define control discretization + tspan = (0.,100.) + dt = 2. + cgrid = collect(0.0:dt:last(tspan)) + + cons = [ + x(last(tspan)) ~ 0.5, + v(last(tspan)) ≳ 0. + ] + + costs = [obj(last(tspan))] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :mountain_car, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/ocean.jl b/lib/OptimalControlBenchmarks/src/problems/ocean.jl new file mode 100644 index 00000000..24ac3cf9 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/ocean.jl @@ -0,0 +1,93 @@ +module ocean + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 3 + num_controls = 2 + + @variables begin + S(..) = 2.e3, [tunable = false] + R(..) = 1.e4, [tunable = false] + u₁(..) = 7., [bounds = (0., 40.), input = true] + u₂(..) = 7., [bounds = (0., 40.), input = true] + end + + @constants begin + ρ = 3.e-2, [tunable = false] + γ = 1.e-3, [tunable = false] + ω = 0.1, [tunable = false] + b = 50., [tunable = false] + μ = 0.5, [tunable = false] + a₁ = 2., [tunable = false] + a₂ = 2., [tunable = false] + ν = 1., [tunable = false] + c₁ = 50., [tunable = false] + c₂ = 4.e-3, [tunable = false] + Sₚ = 6.e2, [tunable = false] + S₀ = 2.e3, [tunable = false] + R₀ = 1.e4, [tunable = false] + Dₗ₀ = 2.3e4, [tunable = false] + end + + # auxiliary functions + U = b * u₁(t) - μ * u₁(t)^2 + A = a₁ * u₂(t) + a₂ * u₂(t)^2 + C = c₁ - c₂ * R(t) + Df = ν * (0.3 * S(t) - Sₚ)^2 + Dₗ = Dₗ₀ + R₀ + S₀ - R(t) - S(t) + + eqs = [ + D(R(t)) ~ u₁(t) - u₂(t) - γ * (S(t) - ω * Dₗ) + D(S(t)) ~ -u₁(t) + ] + + # Define control discretization + tspan = (0.,400.) + dt = 4. + cgrid = collect(0.0:dt:last(tspan)) + + grid_cons_Su = [S(tᵢ) ≲ 1.e5 for tᵢ in cgrid] + grid_cons_Ru = [R(tᵢ) ≲ 1.e5 for tᵢ in cgrid] + grid_cons_Sl = [S(tᵢ) ≳ 0. for tᵢ in cgrid] + grid_cons_Rl = [R(tᵢ) ≳ 0. for tᵢ in cgrid] + + cons = [ + grid_cons_Su..., grid_cons_Ru..., + grid_cons_Sl..., grid_cons_Rl... + ] + + costs = [ + Symbolics.Integral(t in (0.0, last(tspan)))( + -exp(-ρ * t) * (U - A - u₁(t) * C - Df) + ), + ] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :ocean, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl b/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl new file mode 100644 index 00000000..6b110257 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl @@ -0,0 +1,76 @@ +module particle_steering + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 5 + num_controls = 1 + + @variables begin + x₁(..) = 0., [tunable = false] + x₂(..) = 0., [tunable = false] + dx₁(..) = 0., [tunable = false] + dx₂(..) = 0., [tunable = false] + obj(..) = 0., [tunable = false] + u(..) = 0., [bounds = (-pi / 2., pi / 2.), input = true] + end + + @parameters begin + tₛ = 3.e-2, [bounds = (1.e-3, Inf), tunable = true] + end + + @constants begin + a = 100., [tunable = false] + end + + eqs = [ + D(x₁(t)) ~ tₛ * (dx₁(t)) + D(x₂(t)) ~ tₛ * (dx₂(t)) + D(dx₁(t)) ~ tₛ * (a * cos(u(t))) + D(dx₂(t)) ~ tₛ * (a * sin(u(t))) + D(obj(t)) ~ tₛ + ] + + # Define control discretization + tspan = (0.,10.) + dt = 0.2 + cgrid = collect(0.0:dt:last(tspan))[1:end - 1] + + cons = [ + x₂(last(tspan)) ~ 5., + dx₁(last(tspan)) ~ 45., + dx₂(last(tspan)) ~ 0. + ] + + costs = [ + obj(last(tspan)) + ] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :particle_steering, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl b/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl new file mode 100644 index 00000000..5ff7971f --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl @@ -0,0 +1,85 @@ +module quadrotor + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 7 + num_controls = 4 + + @variables begin + x₁(..) = 0., [tunable = false] + x₂(..) = 0., [tunable = false] + x₃(..) = 1., [tunable = false] + x₄(..) = 0., [tunable = false] + x₅(..) = 0., [tunable = false] + x₆(..) = 0., [tunable = false] + w₁(..) = 1. / 3., [bounds = (0., 1.), input = true] + w₂(..) = 1. / 3., [bounds = (0., 1.), input = true] + w₃(..) = 1. / 3., [bounds = (0., 1.), input = true] + u(..) = 0., [bounds = (0., 1.e-3), input = true] + end + + @constants begin + g = 9.81, [tunable = false] + M = 1.3, [tunable = false] + L = 0.305, [tunable = false] + I = 0.0605, [tunable = false] + end + + eqs = [ + D(x₁(t)) ~ x₂(t) + D(x₂(t)) ~ g * sin(x₅(t)) + w₁(t) * u(t) * sin(x₅(t)) / M + D(x₃(t)) ~ x₄(t) + D(x₄(t)) ~ g * cos(x₅(t)) - g + w₁(t) * u(t) * cos(x₅(t)) / M + D(x₅(t)) ~ x₆(t) + D(x₆(t)) ~ -w₂(t) * L * u(t) / I + w₃(t) * L * u(t) / I + ] + + # Define control discretization + tspan = (0.,7.5) + dt = 0.15 + cgrid = collect(0.0:dt:last(tspan))[1:end - 1] + t_f = last(tspan) + + grid_cons_w = [w₁(tᵢ) + w₂(tᵢ) + w₃(tᵢ) ~ 1. for tᵢ in vcat(cgrid, t_f)] + grid_cons_x₃ = [x₃(tᵢ) ≳ 0. for tᵢ in vcat(cgrid, t_f)] + + cons = [ + grid_cons_w..., + grid_cons_x₃... + ] + + costs = [ + 5 * (x₁(t_f) - 6)^2 + 5 * (x₃(t_f) - 1)^2 + 5 * (sin(x₅(t_f) * 0.5))^2 + Symbolics.Integral(t in (0.0, last(tspan)))( + 5 * u(t)^2 + ), + ] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :quadrotor, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl b/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl new file mode 100644 index 00000000..0d7094a9 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl @@ -0,0 +1,60 @@ +module rao_mease + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 2 + num_controls = 1 + + @variables begin + x(..) = 1., [tunable = false] + u(..) = 0., [input = true] + end + + eqs = [ + D(x(t)) ~ -x(t)^3 + u(t) + ] + + # Define control discretization + tspan = (0.,10) + dt = 0.2 + cgrid = collect(0.0:dt:last(tspan))[1:end - 1] + + cons = [ + x(last(tspan)) ~ 1.5 + ] + + costs = [ + Symbolics.Integral(t in (0.0, last(tspan)))( + x(t)^2 + u(t)^2 + ), + ] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :rao_mease, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/robbins.jl b/lib/OptimalControlBenchmarks/src/problems/robbins.jl new file mode 100644 index 00000000..fe122fac --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/robbins.jl @@ -0,0 +1,75 @@ +module robbins + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 4 + num_controls = 1 + + @variables begin + x₁(..) = 1., [tunable = false] + x₂(..) = -2., [tunable = false] + x₃(..) = 0., [tunable = false] + u(..) = 0., [input = true] + end + + @constants begin + α = 3., [tunable = false] + β = 0., [tunable = false] + γ = 0.5, [tunable = false] + end + + eqs = [ + D(x₁(t)) ~ x₂(t) + D(x₂(t)) ~ x₃(t) + D(x₃(t)) ~ u(t) + ] + + # Define control discretization + tspan = (0.,10.) + dt = 0.2 + cgrid = collect(0.0:dt:last(tspan))[1:end - 1] + + grid_cons_x₁ = [x₃(tᵢ) ≳ 0. for tᵢ in cgrid] + + cons = [ + x₁(last(tspan)) ~ 0., + x₂(last(tspan)) ~ 0., + x₃(last(tspan)) ~ 0., + grid_cons_x₁... + ] + + costs = [ + Symbolics.Integral(t in (0.0, last(tspan)))( + α * x₁(t) + β * x₁(t)^2 + γ * u(t)^2 + ), + ] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :robbins, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/three_tank.jl b/lib/OptimalControlBenchmarks/src/problems/three_tank.jl new file mode 100644 index 00000000..3fcc9459 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/three_tank.jl @@ -0,0 +1,79 @@ +module three_tank + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 4 + num_controls = 3 + + @variables begin + x₁(..) = 2., [tunable = false] + x₂(..) = 2., [tunable = false] + x₃(..) = 2., [tunable = false] + w₁(..) = 1. / 3., [bounds = (0., 1.), input = true] + w₂(..) = 1. / 3., [bounds = (0., 1.), input = true] + w₃(..) = 1. / 3., [bounds = (0., 1.), input = true] + end + + @constants begin + c₁ = 1., [tunable = false] + c₂ = 2., [tunable = false] + c₃ = 0.8, [tunable = false] + k₁ = 2., [tunable = false] + k₂ = 3., [tunable = false] + k₃ = 1., [tunable = false] + k₄ = 3., [tunable = false] + end + + eqs = [ + D(x₁(t)) ~ -sqrt(x₂(t)) + c₁ * w₁(t) + c₂ * w₂(t) - w₃(t) * sqrt(c₃ * x₁(t)) + D(x₂(t)) ~ sqrt(x₁(t)) - sqrt(x₂(t)) + D(x₃(t)) ~ sqrt(x₂(t)) - sqrt(x₃(t)) + w₃(t) * sqrt(c₃ * x₁(t)) + ] + + # Define control discretization + tspan = (0.,12.) + dt = 0.24 + cgrid = collect(0.0:dt:last(tspan))[1:end - 1] + t_f = last(tspan) + + grid_cons_w = [w₁(tᵢ) + w₂(tᵢ) + w₃(tᵢ) ~ 1. for tᵢ in vcat(cgrid, t_f)] + + cons = [ + grid_cons_w... + ] + + costs = [ + Symbolics.Integral(t in (0.0, last(tspan)))( + k₁ * (x₂(t) - k₂)^2 + k₃ * (x₃(t) - k₄)^2 + ), + ] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :three_tank, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl b/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl new file mode 100644 index 00000000..806a4fd8 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl @@ -0,0 +1,53 @@ +module tubular_reactor + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 2 + num_controls = 1 + + @variables begin + x₁(..) = 1., [tunable = false] + x₂(..) = 0., [tunable = false] + u(..) = 0., [bounds = (0., 5.), input = true] + end + + eqs = [ + D(x₁(t)) ~ -(u(t) + 0.5 * u(t)^2) * x₁(t) + D(x₂(t)) ~ u(t) * x₁(t) + ] + + # Define control discretization + tspan = (0.,1.) + dt = 0.02 + cgrid = collect(0.0:dt:last(tspan))[1:end - 1] + + costs = [-x₂(last(tspan))] + + @named oc_problem = System( + eqs, + t; + costs = costs + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :tubular_reactor, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl b/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl new file mode 100644 index 00000000..b6b07044 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl @@ -0,0 +1,65 @@ +module van_der_pol + +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using ..OptimalControlBenchmarks: OptimalControlBenchmark + +function make_problem() + + num_states = 3 + num_controls = 1 + + @variables begin + x₁(..) = 0., [tunable = false] + x₂(..) = 1., [tunable = false] + u(..) = 0., [bounds = (-1., 1.), input = true] + end + + eqs = [ + D(x₁(t)) ~ (1 - x₂(t)^2) * x₁(t) - x₂(t) + u(t) + D(x₂(t)) ~ x₁(t) + ] + + # Define control discretization + tspan = (0.,10.) + dt = 0.2 + cgrid = collect(0.0:dt:last(tspan))[1:end - 1] + t_f = last(tspan) + + grid_cons_x₁ = [x₁(tᵢ) ≳ -0.25 for tᵢ in vcat(cgrid, t_f)] + + cons = [ + grid_cons_x₁... + ] + + costs = [ + Symbolics.Integral(t in (0.0, last(tspan)))( + x₁(t)^2 + x₂(t)^2 + u(t)^2 + ), + ] + + @named oc_problem = System( + eqs, + t; + costs = costs, + constraints = cons + ) + + return ( + system = oc_problem, + control_grid = cgrid, + num_states = num_states, + num_controls = num_controls + ) + +end + + +benchmark = OptimalControlBenchmark( + :van_der_pol, + "Double integrator with quadratic control cost", + make_problem +) + +end \ No newline at end of file From 02b1d36c88a53bd1abb6ba0b0d262370f8454821 Mon Sep 17 00:00:00 2001 From: Robert Lampel Date: Thu, 12 Mar 2026 08:16:04 +0100 Subject: [PATCH 03/73] updated problem formulation and added plotting functionality --- lib/OptimalControlBenchmarks/Project.toml | 3 ++ .../src/OptimalControlBenchmarks.jl | 1 + .../src/plot_solution.jl | 12 ++++++++ .../src/problems/batch_reactor.jl | 11 +++----- .../src/problems/bryson_denham.jl | 13 +++++---- .../src/problems/cart_pendulum.jl | 20 ++++++------- .../src/problems/catalyst_mixing.jl | 10 ++----- .../src/problems/cushioned_oscillation.jl | 12 +++----- .../src/problems/denbigh.jl | 10 ++----- .../problems/dielectrophoretic_particle.jl | 10 ++----- .../src/problems/double_oscillator.jl | 10 ++----- .../src/problems/ducted_fan.jl | 16 +++++------ .../src/problems/egerstedt.jl | 18 ++++++------ .../src/problems/electric_car.jl | 16 +++++------ .../src/problems/f8.jl | 10 ++----- .../src/problems/fuller.jl | 10 ++----- .../src/problems/goddarts_rocket.jl | 14 +++++----- .../src/problems/hang_glider.jl | 16 +++++------ .../src/problems/hanging_chain.jl | 25 ++++++++--------- .../src/problems/jackson.jl | 22 +++++++-------- .../src/problems/lotka_competitive.jl | 10 ++----- .../src/problems/lotka_shared.jl | 10 ++----- .../src/problems/lotka_volterra.jl | 14 ++++------ .../src/problems/lqr.jl | 10 ++----- .../src/problems/moon_landing.jl | 14 ++++------ .../src/problems/mountain_car.jl | 10 ++----- .../src/problems/ocean.jl | 18 ++++++------ .../src/problems/particle_steering.jl | 10 ++----- .../src/problems/quadrotor.jl | 17 ++++++----- .../src/problems/rao_mease.jl | 10 ++----- .../src/problems/robbins.jl | 14 +++++----- .../src/problems/three_tank.jl | 17 ++++++----- .../src/problems/tubular_reactor.jl | 10 ++----- .../src/problems/van_der_pol.jl | 15 +++++----- .../src/run_benchmarks.jl | 16 +++++++---- .../src/solver_corleone.jl | 28 ++++++++++++++----- lib/OptimalControlBenchmarks/test.jl | 14 +++++++++- 37 files changed, 238 insertions(+), 258 deletions(-) create mode 100644 lib/OptimalControlBenchmarks/src/plot_solution.jl diff --git a/lib/OptimalControlBenchmarks/Project.toml b/lib/OptimalControlBenchmarks/Project.toml index ce77c6f7..749db010 100644 --- a/lib/OptimalControlBenchmarks/Project.toml +++ b/lib/OptimalControlBenchmarks/Project.toml @@ -4,6 +4,8 @@ authors = ["Bertram Polle"] version = "0.0.7" [deps] +BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" Corleone = "2751e0db-33e9-4374-b36d-b7219e1e6b40" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" @@ -24,6 +26,7 @@ SciMLStructures = "53ae85a6-f571-4167-b2af-e1d143709226" StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5" Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" +UnoSolver = "1baa60ac-02f7-4b39-a7a8-2f4f58486b05" [compat] Aqua = "0.8" diff --git a/lib/OptimalControlBenchmarks/src/OptimalControlBenchmarks.jl b/lib/OptimalControlBenchmarks/src/OptimalControlBenchmarks.jl index ebbabbcc..666e4149 100644 --- a/lib/OptimalControlBenchmarks/src/OptimalControlBenchmarks.jl +++ b/lib/OptimalControlBenchmarks/src/OptimalControlBenchmarks.jl @@ -3,6 +3,7 @@ module OptimalControlBenchmarks using ModelingToolkit include("types.jl") +include("plot_solution.jl") include("problem_registry.jl") include("solver_corleone.jl") include("run_benchmarks.jl") diff --git a/lib/OptimalControlBenchmarks/src/plot_solution.jl b/lib/OptimalControlBenchmarks/src/plot_solution.jl new file mode 100644 index 00000000..b0741538 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/plot_solution.jl @@ -0,0 +1,12 @@ +using CairoMakie + +function plot_oc_problem(sol, num_states, num_controls) + f = Figure() + ax = CairoMakie.Axis(f[1, 1]) + scatterlines!(ax, sol, vars = collect(1:1:num_states)) + f[1, 2] = Legend(f, ax, "States", framevisible = false) + ax1 = CairoMakie.Axis(f[2, 1]) + stairs!(ax1, sol, vars = collect(num_states + 1:1:num_states + num_controls)) + f[2, 2] = Legend(f, ax1, "Controls", framevisible = false) + return f +end diff --git a/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl b/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl index 9c67748c..84a3672b 100644 --- a/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl +++ b/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl @@ -5,14 +5,15 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(contraint_grid=nothing) num_states = 2 num_controls = 1 + tspan = (0.,1.) @variables begin x₁(..) = 1.0, [tunable = false] - x₂(..) = 1.0, [tunable = false] + x₂(..) = 0.0, [tunable = false] u(..) = 298.0, [bounds = (298.0, 398.0), input = true] end @@ -21,10 +22,6 @@ function make_problem() D(x₂(t)) ~ 4.e3 * exp(-2500 / u(t)) * x₁(t)^2 - 62.e4 * exp(-5000 / u(t)) * x₂(t)^2 ] - tspan = (0.,1.) - dt = 0.01 - cgrid = collect(0.0:dt:last(tspan))[1:end-1] - costs = [-x₂(1.0)] @named oc_problem = System( @@ -35,7 +32,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl b/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl index a6b7392a..a6241e0b 100644 --- a/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl +++ b/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=collect(0.:0.1:1.)) num_states = 2 num_controls = 1 + tspan = (0.,1.) @variables begin x(..) = 0.0, [tunable = false] @@ -21,11 +22,11 @@ function make_problem() D(v(t)) ~ w(t) ] - tspan = (0.,1.) - dt = 0.01 - cgrid = collect(0.0:dt:last(tspan))[1:end-1] + # scale the constraint grid + constraint_grid = constraint_grid * (last(tspan) - first(tspan)) + constraint_grid = (constraint_grid .+ first(tspan))[1:end - 1] - grid_cons = [x(tᵢ) ≲ 1/9 for tᵢ in cgrid] + grid_cons = [x(tᵢ) ≲ 1/9 for tᵢ in constraint_grid] cons = [ x(last(tspan)) ~ 0., @@ -46,7 +47,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl b/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl index 1d303dc1..1d087902 100644 --- a/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl +++ b/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=collect(0.:0.1:1.)) num_states = 4 num_controls = 1 + tspan = (0.,4.) @variables begin x(..) = 0.0, [tunable = false] @@ -33,14 +34,13 @@ function make_problem() D(dx(t)) ~ (w(t) + m * g * sin(θ(t)) * cos(θ(t)) + m * dtheta(t)^2 * sin(θ(t))) / (M + m * (1 - cos(θ(t)))^2) D(dtheta(t)) ~ -g * sin(θ(t)) - ((w(t) + m * g * sin(θ(t)) * cos(θ(t)) + m * dtheta(t) * sin(θ(t))) / (M + m * (1 - cos(θ(t))^2))) * cos(θ(t)) ] - - # Define control discretization - tspan = (0.,4.) - dt = (last(tspan) - first(tspan)) / 40 - cgrid = collect(0.0:dt:last(tspan))[1:end-1] - - grid_cons_le = [x(tᵢ) ≲ 2. for tᵢ in vcat(cgrid, last(tspan))] - grid_cons_ge = [x(tᵢ) ≳ -2. for tᵢ in vcat(cgrid, last(tspan))] + + # scale the constraint grid + constraint_grid = constraint_grid * (last(tspan) - first(tspan)) + constraint_grid = (constraint_grid .+ first(tspan)) + + grid_cons_le = [x(tᵢ) ≲ 2. for tᵢ in constraint_grid] + grid_cons_ge = [x(tᵢ) ≳ -2. for tᵢ in constraint_grid] cons = [ grid_cons_le..., @@ -62,7 +62,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl b/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl index 590731c4..2ece608e 100644 --- a/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl +++ b/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=nothing) num_states = 2 num_controls = 1 + tspan = (0.,1.) @variables begin x₁(..) = 1.0, [tunable = false] @@ -21,11 +22,6 @@ function make_problem() D(x₂(t)) ~ w(t) * (x₁(t) - 10 * x₂(t)) - (1 - w(t)) * x₂(t) ] - # Define control discretization - tspan = (0.,1.) - dt = 0.02 - cgrid = collect(0.0:dt:last(tspan))[1:end-1] - costs = [-1 + x₁(last(tspan)) + x₂(last(tspan))] @named oc_problem = System( @@ -36,7 +32,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl b/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl index eb3f81ea..5c659bb4 100644 --- a/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl +++ b/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=nothing) num_states = 3 num_controls = 1 + tspan = (0.,10.) @variables begin x(..) = 2.0, [tunable = false] @@ -29,12 +30,7 @@ function make_problem() D(v(t)) ~ tₛ * (1 / m * (u(t) - c * x(t))) D(obj(t)) ~ tₛ ] - - # Define control discretization - tspan = (0.,10.) - dt = 0.2 - cgrid = collect(0.0:dt:last(tspan))[1:end-1] - + cons = [ x(last(tspan)) ~ 0., v(last(tspan)) ~ 0., @@ -51,7 +47,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/denbigh.jl b/lib/OptimalControlBenchmarks/src/problems/denbigh.jl index a472e731..94d79c81 100644 --- a/lib/OptimalControlBenchmarks/src/problems/denbigh.jl +++ b/lib/OptimalControlBenchmarks/src/problems/denbigh.jl @@ -5,11 +5,12 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=nothing) # We start by defining our system num_states = 3 num_controls = 1 + tspan = (0.,1000.) @variables begin x₁(..) = 1.0, [tunable = false] @@ -34,11 +35,6 @@ function make_problem() D(x₃(t)) ~ k[3] * x₂(t) ] - # Define control discretization - tspan = (0.,1000.) - dt = 10. - cgrid = collect(0.0:dt:last(tspan))[1:end-1] - costs = [-x₃(last(tspan))] @named oc_problem = System( @@ -49,7 +45,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl b/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl index 91735554..feac6789 100644 --- a/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl +++ b/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=nothing) num_states = 3 num_controls = 1 + tspan = (0.,5.) @variables begin x₀(..) = 1.0, [tunable = false] @@ -30,11 +31,6 @@ function make_problem() D(obj(t)) ~ tₛ ] - # Define control discretization - tspan = (0.,5.) - dt = 0.1 - cgrid = collect(0.0:dt:last(tspan))[1:end-1] - cons = [ x₀(last(tspan)) ~ 2., ] @@ -50,7 +46,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl b/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl index 0f2fa3a9..28328c47 100644 --- a/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl +++ b/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=nothing) num_states = 5 num_controls = 1 + tspan = (0.,2 * pi) @variables begin x₀(..) = 0.0, [tunable = false] @@ -34,11 +35,6 @@ function make_problem() D(x₃(t)) ~ k₂ / m₂ * x₀(t) - k₂ / m₂ * x₁(t) - c * (1 - u(t)) / m₂ * x₃(t) ] - # Define control discretization - tspan = (0.,2 * pi) - dt = 0.1 - cgrid = collect(0.0:dt:last(tspan))[1:end-1] - costs = [ Symbolics.Integral(t in (0.0, 1.0))( 0.5 * (x₀(t)^2 + x₁(t)^2 + u(t)^2) @@ -53,7 +49,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl b/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl index ec97a1ff..e8dd0bf4 100644 --- a/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl +++ b/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=collect(0.:0.1:1.)) num_states = 7 num_controls = 2 + tspan = (0.,10.) @variables begin x₁(..) = 0.0, [tunable = false] @@ -40,13 +41,12 @@ function make_problem() D(vα(t)) ~ tₛ * (r / J * u₁(t)) ] - # Define control discretization - tspan = (0.,10.) - dt = 0.1 - cgrid = collect(0.0:dt:last(tspan))[1:end-1] + # scale the constraint grid + constraint_grid = constraint_grid * (last(tspan) - first(tspan)) + constraint_grid = (constraint_grid .+ first(tspan))[1:end - 1] - grid_cons_u = [α(tᵢ) ≲ 30. for tᵢ in cgrid] - grid_cons_l = [α(tᵢ) ≳ -30. for tᵢ in cgrid] + grid_cons_u = [α(tᵢ) ≲ 30. for tᵢ in constraint_grid] + grid_cons_l = [α(tᵢ) ≳ -30. for tᵢ in constraint_grid] cons = [ x₁(last(tspan)) ~ 1., @@ -74,7 +74,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl b/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl index 9ea835fd..af8c7d2a 100644 --- a/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl +++ b/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=collect(0.:0.1:1.)) num_states = 3 num_controls = 3 + tspan = (0.,1.) @variables begin x(..) = 0.5, [tunable = false] @@ -23,15 +24,16 @@ function make_problem() D(y(t)) ~ (x(t) + 2 * y(t)) * u_1(t) + (x(t) - 2 * y(t)) * u_2(t) + (x(t) + y(t)) * u_3(t) ] - # Define control discretization - tspan = (0.,1.) - dt = 0.1 - cgrid = collect(0.0:dt:last(tspan))[1:end-1] + # scale the constraint grid + constraint_grid = constraint_grid * (last(tspan) - first(tspan)) + constraint_grid = (constraint_grid .+ first(tspan)) - grid_cons = [u_1(tᵢ) + u_2(tᵢ) + u_3(tᵢ) ~ 1. for tᵢ in cgrid] + grid_cons_x = [y(tᵢ) ≳ 0.4 for tᵢ in constraint_grid] + grid_cons_u = [u_1(tᵢ) + u_2(tᵢ) + u_3(tᵢ) ~ 1. for tᵢ in constraint_grid] cons = [ - grid_cons... + grid_cons_x..., + grid_cons_u... ] costs = [ @@ -49,7 +51,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/electric_car.jl b/lib/OptimalControlBenchmarks/src/problems/electric_car.jl index 64da184a..fbdb920c 100644 --- a/lib/OptimalControlBenchmarks/src/problems/electric_car.jl +++ b/lib/OptimalControlBenchmarks/src/problems/electric_car.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=collect(0.:0.1:1.)) num_states = 4 num_controls = 1 + tspan = (0.,10.) @variables begin x_1(..) = 0.0, [tunable = false] @@ -43,13 +44,12 @@ function make_problem() D(x_3(t)) ~ r / K_r * x_2(t) ] - # Define control discretization - tspan = (0.,10.) - dt = 0.2 - cgrid = collect(0.0:dt:last(tspan)) + # scale the constraint grid + constraint_grid = constraint_grid * (last(tspan) - first(tspan)) + constraint_grid = (constraint_grid .+ first(tspan)) - grid_cons_u = [x_1(tᵢ) ≲ 150. for tᵢ in cgrid] - grid_cons_l = [x_1(tᵢ) ≳ -150. for tᵢ in cgrid] + grid_cons_u = [x_1(tᵢ) ≲ 150. for tᵢ in constraint_grid] + grid_cons_l = [x_1(tᵢ) ≳ -150. for tᵢ in constraint_grid] cons = [ x_3(last(tspan)) ~ 100., @@ -72,7 +72,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/f8.jl b/lib/OptimalControlBenchmarks/src/problems/f8.jl index 46cce473..e9ed68ba 100644 --- a/lib/OptimalControlBenchmarks/src/problems/f8.jl +++ b/lib/OptimalControlBenchmarks/src/problems/f8.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=nothing) num_states = 4 num_controls = 1 + tspan = (0.,10.) @variables begin x_0(..) = 0.4655, [tunable = false] @@ -42,11 +43,6 @@ function make_problem() D(obj(t)) ~ T ] - # Define control discretization - tspan = (0.,10.) - dt = 0.1 - cgrid = collect(0.0:dt:last(tspan)) - cons = [ x_0(last(tspan)) ~ 0., x_1(last(tspan)) ~ 0., @@ -64,7 +60,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/fuller.jl b/lib/OptimalControlBenchmarks/src/problems/fuller.jl index 1b2c7c6a..afac42c3 100644 --- a/lib/OptimalControlBenchmarks/src/problems/fuller.jl +++ b/lib/OptimalControlBenchmarks/src/problems/fuller.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=nothing) num_states = 3 num_controls = 1 + tspan = (0.,1.) @variables begin x₀(..) = 0.01, [tunable = false] @@ -21,11 +22,6 @@ function make_problem() D(x₁(t)) ~ 1. - 2. * u(t) ] - # Define control discretization - tspan = (0.,1.) - dt = 0.02 - cgrid = collect(0.0:dt:last(tspan)) - cons = [ x₀(last(tspan)) ~ 0.01, x₁(last(tspan)) ~ 0., @@ -46,7 +42,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl b/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl index 9b23c850..10224623 100644 --- a/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl +++ b/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=collect(0.:0.1:1.)) num_states = 3 num_controls = 1 + tspan = (0.,10.) @variables begin r(..) = 1., [tunable = false] @@ -41,12 +42,11 @@ function make_problem() D(m(t)) ~ T * (-b * u(t)) ] - # Define control discretization - tspan = (0.,10.) - dt = 0.1 - cgrid = collect(0.0:dt:last(tspan)) + # scale the constraint grid + constraint_grid = constraint_grid * (last(tspan) - first(tspan)) + constraint_grid = (constraint_grid .+ first(tspan)) - grid_cons_u = [310. * v(tᵢ)^2 * 2.7182818^(-500. * (r(tᵢ) - 1.)) ≲ 0.6 for tᵢ in cgrid] + grid_cons_u = [310. * v(tᵢ)^2 * 2.7182818^(-500. * (r(tᵢ) - 1.)) ≲ 0.6 for tᵢ in constraint_grid] cons = [ r(last(tspan)) ~ 1.01, @@ -64,7 +64,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl b/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl index 28dfc2b0..8980d987 100644 --- a/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl +++ b/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=collect(0.:0.1:1.)) num_states = 4 num_controls = 1 + tspan = (0.,100.) @variables begin x(..) = 0., [tunable = false] @@ -48,13 +49,12 @@ function make_problem() D(v_y(t)) ~ T * ((L * v_x(t) - Dr * w) / (m * v) - g) ] - # Define control discretization - tspan = (0.,100.) - dt = 2. - cgrid = collect(0.0:dt:last(tspan))[1:end-1] + # scale the constraint grid + constraint_grid = constraint_grid * (last(tspan) - first(tspan)) + constraint_grid = (constraint_grid .+ first(tspan))[1:end - 1] - grid_cons_x = [x(tᵢ) ≳ 0. for tᵢ in cgrid] - grid_cons_vx = [v_x(tᵢ) ≳ 0. for tᵢ in cgrid] + grid_cons_x = [x(tᵢ) ≳ 0. for tᵢ in constraint_grid] + grid_cons_vx = [v_x(tᵢ) ≳ 0. for tᵢ in constraint_grid] cons = [ y(last(tspan)) ~ 900., @@ -75,7 +75,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/hanging_chain.jl b/lib/OptimalControlBenchmarks/src/problems/hanging_chain.jl index f65be7e2..21aa903a 100644 --- a/lib/OptimalControlBenchmarks/src/problems/hanging_chain.jl +++ b/lib/OptimalControlBenchmarks/src/problems/hanging_chain.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=collect(0.:0.1:1.)) num_states = 3 num_controls = 1 + tspan = (0.,1.) @variables begin x₁(..) = 1., [tunable = false] @@ -29,18 +30,16 @@ function make_problem() D(x₃(t)) ~ sqrt(1 + u(t)^2) ] - # Define control discretization - tspan = (0.,1.) - dt = 0.02 - cgrid = collect(0.0:dt:last(tspan))[1:end-1] + # scale the constraint grid + constraint_grid = constraint_grid * (last(tspan) - first(tspan)) + constraint_grid = (constraint_grid .+ first(tspan)) - # grid_cons_u = [A * v(tᵢ)^2 * exp(-k * (r(tᵢ) - r_0)) ≲ 0.6 for tᵢ in cgrid] - grid_cons_u1 = [x₁(tᵢ) ≳ 0. for tᵢ in cgrid] - grid_cons_u2 = [x₂(tᵢ) ≳ 0. for tᵢ in cgrid] - grid_cons_u3 = [x₃(tᵢ) ≳ 0. for tᵢ in cgrid] - grid_cons_l1 = [x₁(tᵢ) ≲ 10. for tᵢ in cgrid] - grid_cons_l2 = [x₂(tᵢ) ≲ 10. for tᵢ in cgrid] - grid_cons_l3 = [x₃(tᵢ) ≲ 10. for tᵢ in cgrid] + grid_cons_u1 = [x₁(tᵢ) ≳ 0. for tᵢ in constraint_grid[1:end - 1]] + grid_cons_u2 = [x₂(tᵢ) ≳ 0. for tᵢ in constraint_grid] + grid_cons_u3 = [x₃(tᵢ) ≳ 0. for tᵢ in constraint_grid[1:end - 1]] + grid_cons_l1 = [x₁(tᵢ) ≲ 10. for tᵢ in constraint_grid[1:end - 1]] + grid_cons_l2 = [x₂(tᵢ) ≲ 10. for tᵢ in constraint_grid] + grid_cons_l3 = [x₃(tᵢ) ≲ 10. for tᵢ in constraint_grid[1:end - 1]] cons = [ x₁(last(tspan)) - 3. ~ 0., @@ -60,7 +59,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/jackson.jl b/lib/OptimalControlBenchmarks/src/problems/jackson.jl index a3e20550..96d6e78f 100644 --- a/lib/OptimalControlBenchmarks/src/problems/jackson.jl +++ b/lib/OptimalControlBenchmarks/src/problems/jackson.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=collect(0.:0.1:1.)) num_states = 3 num_controls = 1 + tspan = (0.,1.) @variables begin x₁(..) = 1., [tunable = false] @@ -30,16 +31,15 @@ function make_problem() ] # Define control discretization - tspan = (0.,1.) - dt = 0.02 - cgrid = collect(0.0:dt:last(tspan)) + constraint_grid = constraint_grid * (last(tspan) - first(tspan)) + constraint_grid = (constraint_grid .+ first(tspan)) - grid_cons_u1 = [x₁(tᵢ) ≲ 1.1 for tᵢ in cgrid] - grid_cons_u2 = [x₂(tᵢ) ≲ 1.1 for tᵢ in cgrid] - grid_cons_u3 = [x₃(tᵢ) ≲ 1.1 for tᵢ in cgrid] - grid_cons_l1 = [x₁(tᵢ) ≳ 0. for tᵢ in cgrid] - grid_cons_l2 = [x₂(tᵢ) ≳ 0. for tᵢ in cgrid] - grid_cons_l3 = [x₃(tᵢ) ≳ 0. for tᵢ in cgrid] + grid_cons_u1 = [x₁(tᵢ) ≲ 1.1 for tᵢ in constraint_grid] + grid_cons_u2 = [x₂(tᵢ) ≲ 1.1 for tᵢ in constraint_grid] + grid_cons_u3 = [x₃(tᵢ) ≲ 1.1 for tᵢ in constraint_grid] + grid_cons_l1 = [x₁(tᵢ) ≳ 0. for tᵢ in constraint_grid] + grid_cons_l2 = [x₂(tᵢ) ≳ 0. for tᵢ in constraint_grid] + grid_cons_l3 = [x₃(tᵢ) ≳ 0. for tᵢ in constraint_grid] cons = [ grid_cons_u1..., grid_cons_u2..., grid_cons_u3..., @@ -57,7 +57,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl b/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl index 8fe57e44..17f7e88b 100644 --- a/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl +++ b/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=nothing) num_states = 3 num_controls = 1 + tspan = (0.,40.) @variables begin x₀(..) = 0.5, [tunable = false] @@ -27,11 +28,6 @@ function make_problem() D(x₀(t)) ~ x₀(t) * (1 - (x₀(t) + α * x₁(t)) / K) - c₁ * x₀(t) * u(t) D(x₁(t)) ~ x₁(t) * (1 - (x₀(t) + x₁(t)) / K) - c₂ * x₁(t) * u(t) ] - - # Define control discretization - tspan = (0.,40.) - dt = 0.8 - cgrid = collect(0.0:dt:last(tspan)) costs = [ Symbolics.Integral(t in (0.0, last(tspan)))( @@ -47,7 +43,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl b/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl index 4992ce62..918e08ef 100644 --- a/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl +++ b/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=nothing) num_states = 4 num_controls = 1 + tspan = (0.,40.) @variables begin x₀(..) = 1.5, [tunable = false] @@ -29,11 +30,6 @@ function make_problem() D(x₂(t)) ~ -x₂(t) + α * x₀(t) * x₂(t) - c₂ * x₂(t) * u(t) ] - # Define control discretization - tspan = (0.,40.) - dt = 0.8 - cgrid = collect(0.0:dt:last(tspan)) - costs = [ Symbolics.Integral(t in (0.0, last(tspan)))( (x₀(t) - 1.5)^2 + (x₁(t) - 1.)^2 + (x₂(t) - 1.)^2 @@ -48,7 +44,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl b/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl index ef547634..d5466d09 100644 --- a/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl +++ b/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl @@ -1,14 +1,15 @@ -module goddarts_rocket +module lotka_volterra using ModelingToolkit using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=nothing) num_states = 3 num_controls = 1 + tspan = (0.,12.) @variables begin x₀(..) = 0.5, [tunable = false] @@ -26,11 +27,6 @@ function make_problem() D(x₁(t)) ~ -x₁(t) + x₀(t) * x₁(t) - c₁ * x₁(t) * u(t) ] - # Define control discretization - tspan = (0.,12.) - dt = 0.24 - cgrid = collect(0.0:dt:last(tspan)) - costs = [ Symbolics.Integral(t in (0.0, last(tspan)))( (x₀(t) - 1.)^2 + (x₁(t) - 1.)^2 @@ -45,7 +41,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) @@ -54,7 +50,7 @@ end benchmark = OptimalControlBenchmark( - :goddarts_rocket, + :lotka_volterra, "Double integrator with quadratic control cost", make_problem ) diff --git a/lib/OptimalControlBenchmarks/src/problems/lqr.jl b/lib/OptimalControlBenchmarks/src/problems/lqr.jl index 415d2eb1..ab4d8105 100644 --- a/lib/OptimalControlBenchmarks/src/problems/lqr.jl +++ b/lib/OptimalControlBenchmarks/src/problems/lqr.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=nothing) num_states = 2 num_controls = 1 + tspan = (0.,10.) @variables begin x(..) = 1., [tunable = false] @@ -23,11 +24,6 @@ function make_problem() eqs = [ D(x(t)) ~ a * x(t) + b * u(t) ] - - # Define control discretization - tspan = (0.,10.) - dt = 0.2 - cgrid = collect(0.0:dt:last(tspan)) costs = [ Symbolics.Integral(t in (0.0, last(tspan)))( @@ -43,7 +39,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl b/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl index 1b765faa..2fbcb958 100644 --- a/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl +++ b/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=nothing) num_states = 3 num_controls = 1 + tspan = (0.,1.) @variables begin h(..) = 1., [tunable = false] @@ -26,17 +27,12 @@ function make_problem() D(v(t)) ~ tₛ * (-1 + T(t) / m(t)) D(m(t)) ~ tₛ * (- T(t) / 2.349) ] - - # Define control discretization - tspan = (0.,1.) - dt = 0.02 - cgrid = collect(0.0:dt:last(tspan)) - + cons = [ h(last(tspan)) ~ 0., v(last(tspan)) ~ 0. ] - + costs = [-m(last(tspan))] @named oc_problem = System( @@ -48,7 +44,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl b/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl index aa09af33..029031ef 100644 --- a/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl +++ b/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=nothing) num_states = 3 num_controls = 1 + tspan = (0.,100.) @variables begin x(..) = -0.5, [tunable = false] @@ -27,11 +28,6 @@ function make_problem() D(obj(t)) ~ tₛ ] - # Define control discretization - tspan = (0.,100.) - dt = 2. - cgrid = collect(0.0:dt:last(tspan)) - cons = [ x(last(tspan)) ~ 0.5, v(last(tspan)) ≳ 0. @@ -48,7 +44,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/ocean.jl b/lib/OptimalControlBenchmarks/src/problems/ocean.jl index 24ac3cf9..7d54fd76 100644 --- a/lib/OptimalControlBenchmarks/src/problems/ocean.jl +++ b/lib/OptimalControlBenchmarks/src/problems/ocean.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=collect(0.:0.1:1.)) num_states = 3 num_controls = 2 + tspan = (0.,400.) @variables begin S(..) = 2.e3, [tunable = false] @@ -47,14 +48,13 @@ function make_problem() ] # Define control discretization - tspan = (0.,400.) - dt = 4. - cgrid = collect(0.0:dt:last(tspan)) + constraint_grid = constraint_grid * (last(tspan) - first(tspan)) + constraint_grid = (constraint_grid .+ first(tspan)) - grid_cons_Su = [S(tᵢ) ≲ 1.e5 for tᵢ in cgrid] - grid_cons_Ru = [R(tᵢ) ≲ 1.e5 for tᵢ in cgrid] - grid_cons_Sl = [S(tᵢ) ≳ 0. for tᵢ in cgrid] - grid_cons_Rl = [R(tᵢ) ≳ 0. for tᵢ in cgrid] + grid_cons_Su = [S(tᵢ) ≲ 1.e5 for tᵢ in constraint_grid] + grid_cons_Ru = [R(tᵢ) ≲ 1.e5 for tᵢ in constraint_grid] + grid_cons_Sl = [S(tᵢ) ≳ 0. for tᵢ in constraint_grid] + grid_cons_Rl = [R(tᵢ) ≳ 0. for tᵢ in constraint_grid] cons = [ grid_cons_Su..., grid_cons_Ru..., @@ -76,7 +76,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl b/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl index 6b110257..11cdad4c 100644 --- a/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl +++ b/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=nothing) num_states = 5 num_controls = 1 + tspan = (0.,10.) @variables begin x₁(..) = 0., [tunable = false] @@ -35,11 +36,6 @@ function make_problem() D(obj(t)) ~ tₛ ] - # Define control discretization - tspan = (0.,10.) - dt = 0.2 - cgrid = collect(0.0:dt:last(tspan))[1:end - 1] - cons = [ x₂(last(tspan)) ~ 5., dx₁(last(tspan)) ~ 45., @@ -59,7 +55,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl b/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl index 5ff7971f..0e996b87 100644 --- a/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl +++ b/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=collect(0.:0.1:1.)) num_states = 7 num_controls = 4 + tspan = (0.,7.5) @variables begin x₁(..) = 0., [tunable = false] @@ -39,14 +40,12 @@ function make_problem() D(x₆(t)) ~ -w₂(t) * L * u(t) / I + w₃(t) * L * u(t) / I ] - # Define control discretization - tspan = (0.,7.5) - dt = 0.15 - cgrid = collect(0.0:dt:last(tspan))[1:end - 1] - t_f = last(tspan) + # scale the constraint grid + constraint_grid = constraint_grid * (last(tspan) - first(tspan)) + constraint_grid = (constraint_grid .+ first(tspan)) - grid_cons_w = [w₁(tᵢ) + w₂(tᵢ) + w₃(tᵢ) ~ 1. for tᵢ in vcat(cgrid, t_f)] - grid_cons_x₃ = [x₃(tᵢ) ≳ 0. for tᵢ in vcat(cgrid, t_f)] + grid_cons_w = [w₁(tᵢ) + w₂(tᵢ) + w₃(tᵢ) ~ 1. for tᵢ in constraint_grid] + grid_cons_x₃ = [x₃(tᵢ) ≳ 0. for tᵢ in constraint_grid] cons = [ grid_cons_w..., @@ -68,7 +67,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl b/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl index 0d7094a9..98b326fd 100644 --- a/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl +++ b/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=nothing) num_states = 2 num_controls = 1 + tspan = (0.,10) @variables begin x(..) = 1., [tunable = false] @@ -19,11 +20,6 @@ function make_problem() D(x(t)) ~ -x(t)^3 + u(t) ] - # Define control discretization - tspan = (0.,10) - dt = 0.2 - cgrid = collect(0.0:dt:last(tspan))[1:end - 1] - cons = [ x(last(tspan)) ~ 1.5 ] @@ -43,7 +39,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/robbins.jl b/lib/OptimalControlBenchmarks/src/problems/robbins.jl index fe122fac..161efd57 100644 --- a/lib/OptimalControlBenchmarks/src/problems/robbins.jl +++ b/lib/OptimalControlBenchmarks/src/problems/robbins.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=collect(0.:0.1:1.)) num_states = 4 num_controls = 1 + tspan = (0.,10.) @variables begin x₁(..) = 1., [tunable = false] @@ -29,12 +30,11 @@ function make_problem() D(x₃(t)) ~ u(t) ] - # Define control discretization - tspan = (0.,10.) - dt = 0.2 - cgrid = collect(0.0:dt:last(tspan))[1:end - 1] + # scale the constraint grid + constraint_grid = constraint_grid * (last(tspan) - first(tspan)) + constraint_grid = (constraint_grid .+ first(tspan))[1:end - 1] - grid_cons_x₁ = [x₃(tᵢ) ≳ 0. for tᵢ in cgrid] + grid_cons_x₁ = [x₃(tᵢ) ≳ 0. for tᵢ in constraint_grid] cons = [ x₁(last(tspan)) ~ 0., @@ -58,7 +58,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/three_tank.jl b/lib/OptimalControlBenchmarks/src/problems/three_tank.jl index 3fcc9459..44f53d40 100644 --- a/lib/OptimalControlBenchmarks/src/problems/three_tank.jl +++ b/lib/OptimalControlBenchmarks/src/problems/three_tank.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=collect(0.:0.1:1.)) num_states = 4 num_controls = 3 + tspan = (0.,12.) @variables begin x₁(..) = 2., [tunable = false] @@ -35,13 +36,11 @@ function make_problem() D(x₃(t)) ~ sqrt(x₂(t)) - sqrt(x₃(t)) + w₃(t) * sqrt(c₃ * x₁(t)) ] - # Define control discretization - tspan = (0.,12.) - dt = 0.24 - cgrid = collect(0.0:dt:last(tspan))[1:end - 1] - t_f = last(tspan) - - grid_cons_w = [w₁(tᵢ) + w₂(tᵢ) + w₃(tᵢ) ~ 1. for tᵢ in vcat(cgrid, t_f)] + # scale the constraint grid + constraint_grid = constraint_grid * (last(tspan) - first(tspan)) + constraint_grid = (constraint_grid .+ first(tspan)) + + grid_cons_w = [w₁(tᵢ) + w₂(tᵢ) + w₃(tᵢ) ~ 1. for tᵢ in constraint_grid] cons = [ grid_cons_w... @@ -62,7 +61,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl b/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl index 806a4fd8..22c4e94f 100644 --- a/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl +++ b/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=nothing) num_states = 2 num_controls = 1 + tspan = (0.,1.) @variables begin x₁(..) = 1., [tunable = false] @@ -20,11 +21,6 @@ function make_problem() D(x₁(t)) ~ -(u(t) + 0.5 * u(t)^2) * x₁(t) D(x₂(t)) ~ u(t) * x₁(t) ] - - # Define control discretization - tspan = (0.,1.) - dt = 0.02 - cgrid = collect(0.0:dt:last(tspan))[1:end - 1] costs = [-x₂(last(tspan))] @@ -36,7 +32,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl b/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl index b6b07044..8967209d 100644 --- a/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl +++ b/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl @@ -5,10 +5,11 @@ using ModelingToolkit: t_nounits as t, D_nounits as D using Symbolics using ..OptimalControlBenchmarks: OptimalControlBenchmark -function make_problem() +function make_problem(constraint_grid=collect(0.:0.1:1.)) num_states = 3 num_controls = 1 + tspan = (0.,10.) @variables begin x₁(..) = 0., [tunable = false] @@ -21,13 +22,11 @@ function make_problem() D(x₂(t)) ~ x₁(t) ] - # Define control discretization - tspan = (0.,10.) - dt = 0.2 - cgrid = collect(0.0:dt:last(tspan))[1:end - 1] - t_f = last(tspan) + # scale the constraint grid + constraint_grid = constraint_grid * (last(tspan) - first(tspan)) + constraint_grid = (constraint_grid .+ first(tspan)) - grid_cons_x₁ = [x₁(tᵢ) ≳ -0.25 for tᵢ in vcat(cgrid, t_f)] + grid_cons_x₁ = [x₁(tᵢ) ≳ -0.25 for tᵢ in constraint_grid] cons = [ grid_cons_x₁... @@ -48,7 +47,7 @@ function make_problem() return ( system = oc_problem, - control_grid = cgrid, + tspan = tspan, num_states = num_states, num_controls = num_controls ) diff --git a/lib/OptimalControlBenchmarks/src/run_benchmarks.jl b/lib/OptimalControlBenchmarks/src/run_benchmarks.jl index 189db2ff..887b58a1 100644 --- a/lib/OptimalControlBenchmarks/src/run_benchmarks.jl +++ b/lib/OptimalControlBenchmarks/src/run_benchmarks.jl @@ -1,20 +1,26 @@ -#using BenchmarkTools +using BenchmarkTools using OptimalControlBenchmarks -function run_all(benchmarks) +function run_all(benchmarks, optimizer, constraint_grid, control_grid, shooting_grid) results = [] for prob in benchmarks println("Running ", prob.name) - sol = solve_with_corleone(prob) + sol = solve_with_corleone( + prob, + optimizer, + constraint_grid, + control_grid, + shooting_grid + ) println(sol) - #t = @belapsed solve_with_corleone($prob) + t = 0 # @belapsed solve_with_corleone($prob) push!(results, ( name = prob.name, - #time = t + time = t )) end diff --git a/lib/OptimalControlBenchmarks/src/solver_corleone.jl b/lib/OptimalControlBenchmarks/src/solver_corleone.jl index 2de511d0..2d4d8501 100644 --- a/lib/OptimalControlBenchmarks/src/solver_corleone.jl +++ b/lib/OptimalControlBenchmarks/src/solver_corleone.jl @@ -1,33 +1,41 @@ +using OptimalControlBenchmarks using ModelingToolkit using ModelingToolkit: inputs using Corleone using OrdinaryDiffEqTsit5 using Optimization using OptimizationMOI -using Ipopt using ForwardDiff using ComponentArrays using LuxCore, Random -function solve_with_corleone(benchmark) +function solve_with_corleone(benchmark, optimizer, constraint_grid, control_grid, shooting_grid) - data = benchmark.make_problem() + data = benchmark.make_problem(constraint_grid) oc_problem = data.system - cgrid = data.control_grid + tspan = data.tspan + + # scale the control grid + control_grid = control_grid * (last(tspan) - first(tspan)) + control_grid = (control_grid .+ first(tspan)) + # scale the shooting grid + shooting_grid = shooting_grid * (last(tspan) - first(tspan)) + shooting_grid = (shooting_grid .+ first(tspan)) # Extract control variable controls = inputs(oc_problem) control_map = [ - c => cgrid for c in controls + c => control_grid for c in controls ] dynopt = CorleoneDynamicOptProblem( oc_problem, [], control_map...; - algorithm = Tsit5() + algorithm = Tsit5(), + shooting = shooting_grid ) optprob = OptimizationProblem( @@ -38,12 +46,18 @@ function solve_with_corleone(benchmark) sol = solve( optprob, - Ipopt.Optimizer(), + optimizer, max_iter = 1000, tol = 5e-6, hessian_approximation = "limited-memory" ) + # plotting + u_opt = ComponentVector(sol.u, optprob.f.f.ax) + opt_traj, _ = dynopt.layer(nothing, u_opt, LuxCore.initialstates(Random.default_rng(), dynopt.layer)) + + f = plot_oc_problem(opt_traj, data.num_states, data.num_controls) + display(f) return sol end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/test.jl b/lib/OptimalControlBenchmarks/test.jl index 9e3493b2..21473c94 100644 --- a/lib/OptimalControlBenchmarks/test.jl +++ b/lib/OptimalControlBenchmarks/test.jl @@ -1,9 +1,21 @@ using OptimalControlBenchmarks using Corleone +using UnoSolver +using Ipopt using ModelingToolkit benchmarks = load_benchmarks() -df = run_all(benchmarks) +# choose the optimizer +#optimizer = UnoSolver.Optimizer() +optimizer = Ipopt.Optimizer() + +# settings for discretization, given as grids in [0.,1.] +# the constraint grid is assumed to contain 0. and 1. +constraint_grid = collect(0.:0.05:1.) +control_grid = collect(0.:0.05:1.)[1:end-1] +shooting_grid = collect(0.:0.05:1.) + +df = run_all(benchmarks, optimizer, constraint_grid, control_grid, shooting_grid) println(df) \ No newline at end of file From 3ee5ebe81646124f431d004562a964c64515fe36 Mon Sep 17 00:00:00 2001 From: Robert Lampel Date: Thu, 12 Mar 2026 17:40:39 +0100 Subject: [PATCH 04/73] reworked problems as individual functions --- lib/OptimalControlBenchmarks/Project.toml | 6 ++- .../src/OptimalControlBenchmarks.jl | 17 ++++++- .../src/plot_solution.jl | 2 - .../src/problem_registry.jl | 36 ++++++++++----- .../src/problems/batch_reactor.jl | 25 +++-------- .../src/problems/bryson_denham.jl | 29 +++--------- .../src/problems/cart_pendulum.jl | 35 ++++----------- .../src/problems/catalyst_mixing.jl | 44 +++++++------------ .../src/problems/cushioned_oscillation.jl | 25 +++-------- .../src/problems/denbigh.jl | 27 +++--------- .../problems/dielectrophoretic_particle.jl | 25 +++-------- .../src/problems/double_oscillator.jl | 23 ++-------- .../src/problems/ducted_fan.jl | 30 ++++--------- .../src/problems/egerstedt.jl | 28 +++--------- .../src/problems/electric_car.jl | 29 +++--------- .../src/problems/f8.jl | 25 +++-------- .../src/problems/fuller.jl | 25 +++-------- .../src/problems/goddarts_rocket.jl | 28 +++--------- .../src/problems/hang_glider.jl | 28 +++--------- .../src/problems/hanging_chain.jl | 28 +++--------- .../src/problems/jackson.jl | 28 +++--------- .../src/problems/lotka_competitive.jl | 25 +++-------- .../src/problems/lotka_shared.jl | 25 +++-------- .../src/problems/lotka_volterra.jl | 25 +++-------- .../src/problems/lqr.jl | 25 +++-------- .../src/problems/moon_landing.jl | 25 +++-------- .../src/problems/mountain_car.jl | 25 +++-------- .../src/problems/ocean.jl | 28 +++--------- .../src/problems/particle_steering.jl | 25 +++-------- .../src/problems/quadrotor.jl | 32 +++----------- .../src/problems/rao_mease.jl | 25 +++-------- .../src/problems/robbins.jl | 28 +++--------- .../src/problems/three_tank.jl | 29 +++--------- .../src/problems/tubular_reactor.jl | 25 +++-------- .../src/problems/van_der_pol.jl | 29 +++--------- .../src/run_benchmarks.jl | 12 +++-- .../src/scale_grid.jl | 9 ++++ .../src/solver_corleone.jl | 33 ++++---------- lib/OptimalControlBenchmarks/src/types.jl | 8 +++- lib/OptimalControlBenchmarks/test.jl | 12 +++-- 40 files changed, 268 insertions(+), 720 deletions(-) create mode 100644 lib/OptimalControlBenchmarks/src/scale_grid.jl diff --git a/lib/OptimalControlBenchmarks/Project.toml b/lib/OptimalControlBenchmarks/Project.toml index 749db010..076ad0cf 100644 --- a/lib/OptimalControlBenchmarks/Project.toml +++ b/lib/OptimalControlBenchmarks/Project.toml @@ -1,7 +1,7 @@ name = "OptimalControlBenchmarks" uuid = "6590a4b1-d036-41fe-a5b4-03a980244102" -authors = ["Bertram Polle"] -version = "0.0.7" +authors = ["Robert Lampel", "Julius Martensen", "Christoph Plate", "Reinhold Wittmann"] +version = "0.0.3" [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" @@ -10,10 +10,12 @@ ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" Corleone = "2751e0db-33e9-4374-b36d-b7219e1e6b40" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" +IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LuxCore = "bb33d45b-7691-41d6-9220-0943567d0623" ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" +ModelingToolkitBase = "7771a370-6774-4173-bd38-47e70ca0b839" Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba" OptimizationLBFGSB = "22f7324a-a79d-40f2-bebe-3af60c77bd15" OptimizationMOI = "fd9f6733-72f4-499f-8506-86b2bdd0dea1" diff --git a/lib/OptimalControlBenchmarks/src/OptimalControlBenchmarks.jl b/lib/OptimalControlBenchmarks/src/OptimalControlBenchmarks.jl index 666e4149..cd8c7a4d 100644 --- a/lib/OptimalControlBenchmarks/src/OptimalControlBenchmarks.jl +++ b/lib/OptimalControlBenchmarks/src/OptimalControlBenchmarks.jl @@ -1,13 +1,26 @@ module OptimalControlBenchmarks using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D +using ModelingToolkit: inputs +using OptimalControlBenchmarks +using CairoMakie +using Corleone +using OrdinaryDiffEqTsit5 +using Optimization +using OptimizationMOI +using ForwardDiff +using ComponentArrays +using LuxCore, Random +using Symbolics include("types.jl") include("plot_solution.jl") include("problem_registry.jl") -include("solver_corleone.jl") include("run_benchmarks.jl") +include("scale_grid.jl") +include("solver_corleone.jl") export load_benchmarks, run_all -end \ No newline at end of file +end diff --git a/lib/OptimalControlBenchmarks/src/plot_solution.jl b/lib/OptimalControlBenchmarks/src/plot_solution.jl index b0741538..140e6933 100644 --- a/lib/OptimalControlBenchmarks/src/plot_solution.jl +++ b/lib/OptimalControlBenchmarks/src/plot_solution.jl @@ -1,5 +1,3 @@ -using CairoMakie - function plot_oc_problem(sol, num_states, num_controls) f = Figure() ax = CairoMakie.Axis(f[1, 1]) diff --git a/lib/OptimalControlBenchmarks/src/problem_registry.jl b/lib/OptimalControlBenchmarks/src/problem_registry.jl index cf0fe8d3..61ffb4e0 100644 --- a/lib/OptimalControlBenchmarks/src/problem_registry.jl +++ b/lib/OptimalControlBenchmarks/src/problem_registry.jl @@ -1,18 +1,32 @@ function load_benchmarks() - benchmarks = [] + problems_dir = joinpath(@__DIR__, "problems") - for f in readdir(joinpath(@__DIR__,"problems")) - endswith(f,".jl") || continue - - modname = Symbol(splitext(f)[1]) - - include(joinpath(@__DIR__,"problems",f)) + for f in readdir(problems_dir) + # Skip non-julia files + endswith(f, ".jl") || continue + + # Get the path and the expected function name (stripping .jl) + path = joinpath(problems_dir, f) + func_name_sym = Symbol(splitext(f)[1]) - mod = getfield(@__MODULE__, modname) + # Evaluate the file content into the current module + include(path) - push!(benchmarks, mod.benchmark) + # Retrieve the function object by its name + try + # Look up the symbol in the current module's scope + func_obj = getfield(@__MODULE__, func_name_sym) + + if func_obj isa Function + push!(benchmarks, func_obj) + else + @warn "File $f included, but $func_name_sym is not a function." + end + catch e + @error "Failed to load function $func_name_sym from $f" exception=e + end end - benchmarks -end \ No newline at end of file + return benchmarks +end diff --git a/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl b/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl index 84a3672b..7a367991 100644 --- a/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl +++ b/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl @@ -1,16 +1,11 @@ -module batch_reactor - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(contraint_grid=nothing) +function batch_reactor(grids) num_states = 2 num_controls = 1 tspan = (0.,1.) + scaled_grids = scale_grids!(tspan, grids) + @variables begin x₁(..) = 1.0, [tunable = false] x₂(..) = 0.0, [tunable = false] @@ -32,18 +27,8 @@ function make_problem(contraint_grid=nothing) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :batch_reactor, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl b/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl index a6241e0b..62bb6028 100644 --- a/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl +++ b/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl @@ -1,16 +1,11 @@ -module bryson_denham - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=collect(0.:0.1:1.)) +function bryson_denham(grids) num_states = 2 num_controls = 1 tspan = (0.,1.) + scaled_grids = scale_grids!(tspan, grids) + @variables begin x(..) = 0.0, [tunable = false] v(..) = 1.0, [tunable = false] @@ -22,9 +17,7 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) D(v(t)) ~ w(t) ] - # scale the constraint grid - constraint_grid = constraint_grid * (last(tspan) - first(tspan)) - constraint_grid = (constraint_grid .+ first(tspan))[1:end - 1] + constraint_grid = scaled_grids.constraint_grid[1:end - 1] grid_cons = [x(tᵢ) ≲ 1/9 for tᵢ in constraint_grid] @@ -47,18 +40,8 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :bryson_denham, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl b/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl index 1d087902..23ecfa96 100644 --- a/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl +++ b/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl @@ -1,16 +1,11 @@ -module cart_pendulum +function cart_pendulum(grids) -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=collect(0.:0.1:1.)) - - num_states = 4 + num_states = 5 num_controls = 1 tspan = (0.,4.) + scaled_grids = scale_grids!(tspan, grids) + @variables begin x(..) = 0.0, [tunable = false] θ(..) = 0.0, [tunable = false] @@ -32,12 +27,10 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) D(x(t)) ~ dx(t) D(θ(t)) ~ dtheta(t) D(dx(t)) ~ (w(t) + m * g * sin(θ(t)) * cos(θ(t)) + m * dtheta(t)^2 * sin(θ(t))) / (M + m * (1 - cos(θ(t)))^2) - D(dtheta(t)) ~ -g * sin(θ(t)) - ((w(t) + m * g * sin(θ(t)) * cos(θ(t)) + m * dtheta(t) * sin(θ(t))) / (M + m * (1 - cos(θ(t))^2))) * cos(θ(t)) + D(dtheta(t)) ~ -g * sin(θ(t)) - ((w(t) + m * g * sin(θ(t)) * cos(θ(t)) + m * dtheta(t)^2 * sin(θ(t))) / (M + m * (1 - cos(θ(t))^2))) * cos(θ(t)) ] - # scale the constraint grid - constraint_grid = constraint_grid * (last(tspan) - first(tspan)) - constraint_grid = (constraint_grid .+ first(tspan)) + constraint_grid = scaled_grids.constraint_grid grid_cons_le = [x(tᵢ) ≲ 2. for tᵢ in constraint_grid] grid_cons_ge = [x(tᵢ) ≳ -2. for tᵢ in constraint_grid] @@ -49,7 +42,7 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) costs = [ Symbolics.Integral(t in (0.0, 1.0))( - α * x(t)^2 + β * (θ(t) - pi)^2 + γ * w(t)^2 + 1.e-3 * (α * x(t)^2 + β * (θ(t) - pi)^2 + γ * w(t)^2) ), ] @@ -62,18 +55,8 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :cart_pendulum, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl b/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl index 2ece608e..48a2b26f 100644 --- a/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl +++ b/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl @@ -1,26 +1,21 @@ -module catalyst_mixing - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=nothing) +function catalyst_mixing(grids) num_states = 2 num_controls = 1 tspan = (0.,1.) - - @variables begin - x₁(..) = 1.0, [tunable = false] - x₂(..) = 0.0, [tunable = false] - w(..) = 0.0, [bounds = (0.0, 1.0), input = true] - end + + scaled_grids = scale_grids!(tspan, grids) + + @variables begin + x₁(..) = 1.0, [tunable = false] + x₂(..) = 0.0, [tunable = false] + w(..) = 0.0, [bounds = (0.0, 1.0), input = true] + end - eqs = [ - D(x₁(t)) ~ w(t) * (10 * x₂(t) - x₁(t)) - D(x₂(t)) ~ w(t) * (x₁(t) - 10 * x₂(t)) - (1 - w(t)) * x₂(t) - ] + eqs = [ + D(x₁(t)) ~ w(t) * (10 * x₂(t) - x₁(t)) + D(x₂(t)) ~ w(t) * (x₁(t) - 10 * x₂(t)) - (1 - w(t)) * x₂(t) + ] costs = [-1 + x₁(last(tspan)) + x₂(last(tspan))] @@ -32,17 +27,8 @@ function make_problem(constraint_grid=nothing) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - -benchmark = OptimalControlBenchmark( - :catalyst_mixing, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl b/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl index 5c659bb4..f39009d7 100644 --- a/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl +++ b/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl @@ -1,16 +1,11 @@ -module cushioned_oscillation - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=nothing) +function cushioned_oscillation(grids) num_states = 3 num_controls = 1 tspan = (0.,10.) + scaled_grids = scale_grids!(tspan, grids) + @variables begin x(..) = 2.0, [tunable = false] v(..) = 5.0, [tunable = false] @@ -47,18 +42,8 @@ function make_problem(constraint_grid=nothing) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :cushioned_oscillation, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/denbigh.jl b/lib/OptimalControlBenchmarks/src/problems/denbigh.jl index 94d79c81..9428ac67 100644 --- a/lib/OptimalControlBenchmarks/src/problems/denbigh.jl +++ b/lib/OptimalControlBenchmarks/src/problems/denbigh.jl @@ -1,26 +1,23 @@ -module denbigh - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=nothing) +function denbigh(grids) # We start by defining our system num_states = 3 num_controls = 1 tspan = (0.,1000.) + scaled_grids = scale_grids!(tspan, grids) + @variables begin x₁(..) = 1.0, [tunable = false] x₂(..) = 0.0, [tunable = false] x₃(..) = 0.0, [tunable = false] T(..) = 300.0, [bounds = (273.0, 415.0), input = true] end + @parameters begin tₛ = 1., [bounds = (1.e-3, Inf), tunable = true] end + @constants begin E[1:4] = [3.e3, 6.e3, 3.e3, 0.], [tunable = false] k⁰[1:4] = [1.e3, 1.e7, 1.e1, 1.e-3], [tunable = false] @@ -45,18 +42,8 @@ function make_problem(constraint_grid=nothing) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :denbigh, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl b/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl index feac6789..5c258786 100644 --- a/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl +++ b/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl @@ -1,16 +1,11 @@ -module dielectrophoretic_particle - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=nothing) +function dielectrophoretic_particle(grids) num_states = 3 num_controls = 1 tspan = (0.,5.) + scaled_grids = scale_grids!(tspan, grids) + @variables begin x₀(..) = 1.0, [tunable = false] x₁(..) = 0.0, [tunable = false] @@ -46,18 +41,8 @@ function make_problem(constraint_grid=nothing) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :dielectrophoretic_particle, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl b/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl index 28328c47..aaefc98a 100644 --- a/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl +++ b/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl @@ -1,11 +1,4 @@ -module double_oscillator - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=nothing) +function double_oscillator(grids) num_states = 5 num_controls = 1 @@ -49,18 +42,8 @@ function make_problem(constraint_grid=nothing) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :double_oscillator, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl b/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl index e8dd0bf4..faa39d7d 100644 --- a/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl +++ b/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl @@ -1,15 +1,10 @@ -module ducted_fan - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=collect(0.:0.1:1.)) +function ducted_fan(grids) num_states = 7 num_controls = 2 tspan = (0.,10.) + + scaled_grids = scale_grids!(tspan, grids) @variables begin x₁(..) = 0.0, [tunable = false] @@ -21,9 +16,11 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) u₁(..) = 0.0, [bounds = (-5.0, 5.0), input = true] u₂(..) = 8.5, [bounds = (0.0, 17.0), input = true] end + @parameters begin tₛ = 0.5, [bounds = (1.e-3, Inf), tunable = true] end + @constants begin m = 2.2, [tunable = false] J = 0.05, [tunable = false] @@ -42,8 +39,7 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) ] # scale the constraint grid - constraint_grid = constraint_grid * (last(tspan) - first(tspan)) - constraint_grid = (constraint_grid .+ first(tspan))[1:end - 1] + constraint_grid = scaled_grids.constraint_grid[1:end - 1] grid_cons_u = [α(tᵢ) ≲ 30. for tᵢ in constraint_grid] grid_cons_l = [α(tᵢ) ≳ -30. for tᵢ in constraint_grid] @@ -74,18 +70,8 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :ducted_fan, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl b/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl index af8c7d2a..b6e0adea 100644 --- a/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl +++ b/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl @@ -1,16 +1,11 @@ -module egerstedt - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=collect(0.:0.1:1.)) +function egerstedt(grids) num_states = 3 num_controls = 3 tspan = (0.,1.) + scaled_grids = scale_grids!(tspan, grids) + @variables begin x(..) = 0.5, [tunable = false] y(..) = 0.5, [tunable = false] @@ -25,8 +20,7 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) ] # scale the constraint grid - constraint_grid = constraint_grid * (last(tspan) - first(tspan)) - constraint_grid = (constraint_grid .+ first(tspan)) + constraint_grid = scaled_grids.constraint_grid grid_cons_x = [y(tᵢ) ≳ 0.4 for tᵢ in constraint_grid] grid_cons_u = [u_1(tᵢ) + u_2(tᵢ) + u_3(tᵢ) ~ 1. for tᵢ in constraint_grid] @@ -51,18 +45,8 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :egerstedt, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/electric_car.jl b/lib/OptimalControlBenchmarks/src/problems/electric_car.jl index fbdb920c..ce8239ab 100644 --- a/lib/OptimalControlBenchmarks/src/problems/electric_car.jl +++ b/lib/OptimalControlBenchmarks/src/problems/electric_car.jl @@ -1,15 +1,10 @@ -module electric_car - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=collect(0.:0.1:1.)) +function electric_car(grids) num_states = 4 num_controls = 1 tspan = (0.,10.) + + scaled_grids = scale_grids!(tspan, grids) @variables begin x_1(..) = 0.0, [tunable = false] @@ -44,9 +39,7 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) D(x_3(t)) ~ r / K_r * x_2(t) ] - # scale the constraint grid - constraint_grid = constraint_grid * (last(tspan) - first(tspan)) - constraint_grid = (constraint_grid .+ first(tspan)) + constraint_grid = scaled_grids.constraint_grid grid_cons_u = [x_1(tᵢ) ≲ 150. for tᵢ in constraint_grid] grid_cons_l = [x_1(tᵢ) ≳ -150. for tᵢ in constraint_grid] @@ -72,18 +65,8 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :electric_car, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/f8.jl b/lib/OptimalControlBenchmarks/src/problems/f8.jl index e9ed68ba..3960265d 100644 --- a/lib/OptimalControlBenchmarks/src/problems/f8.jl +++ b/lib/OptimalControlBenchmarks/src/problems/f8.jl @@ -1,16 +1,11 @@ -module f8 - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=nothing) +function f8(grids) num_states = 4 num_controls = 1 tspan = (0.,10.) + scaled_grids = scale_grids!(tspan, grids) + @variables begin x_0(..) = 0.4655, [tunable = false] x_1(..) = 0.0, [tunable = false] @@ -60,18 +55,8 @@ function make_problem(constraint_grid=nothing) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :f8, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/fuller.jl b/lib/OptimalControlBenchmarks/src/problems/fuller.jl index afac42c3..61d39514 100644 --- a/lib/OptimalControlBenchmarks/src/problems/fuller.jl +++ b/lib/OptimalControlBenchmarks/src/problems/fuller.jl @@ -1,16 +1,11 @@ -module fuller - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=nothing) +function fuller(grids) num_states = 3 num_controls = 1 tspan = (0.,1.) + scaled_grids = scale_grids!(tspan, grids) + @variables begin x₀(..) = 0.01, [tunable = false] x₁(..) = 0.0, [tunable = false] @@ -42,18 +37,8 @@ function make_problem(constraint_grid=nothing) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :fuller, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl b/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl index 10224623..03f87ad9 100644 --- a/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl +++ b/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl @@ -1,15 +1,10 @@ -module goddarts_rocket - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=collect(0.:0.1:1.)) +function goddarts_rocket(grids) num_states = 3 num_controls = 1 tspan = (0.,10.) + + scaled_grids = scale_grids!(tspan, grids) @variables begin r(..) = 1., [tunable = false] @@ -43,8 +38,7 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) ] # scale the constraint grid - constraint_grid = constraint_grid * (last(tspan) - first(tspan)) - constraint_grid = (constraint_grid .+ first(tspan)) + constraint_grid = scaled_grids.constraint_grid grid_cons_u = [310. * v(tᵢ)^2 * 2.7182818^(-500. * (r(tᵢ) - 1.)) ≲ 0.6 for tᵢ in constraint_grid] @@ -64,18 +58,8 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :goddarts_rocket, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl b/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl index 8980d987..3c43ad0b 100644 --- a/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl +++ b/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl @@ -1,16 +1,11 @@ -module hang_glider - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=collect(0.:0.1:1.)) +function hang_glider(grids) num_states = 4 num_controls = 1 tspan = (0.,100.) + scaled_grids = scale_grids!(tspan, grids) + @variables begin x(..) = 0., [tunable = false] y(..) = 1.e3, [tunable = false] @@ -50,8 +45,7 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) ] # scale the constraint grid - constraint_grid = constraint_grid * (last(tspan) - first(tspan)) - constraint_grid = (constraint_grid .+ first(tspan))[1:end - 1] + constraint_grid = scaled_grids.constraint_grid[1:end - 1] grid_cons_x = [x(tᵢ) ≳ 0. for tᵢ in constraint_grid] grid_cons_vx = [v_x(tᵢ) ≳ 0. for tᵢ in constraint_grid] @@ -75,18 +69,8 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :hang_glider, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/hanging_chain.jl b/lib/OptimalControlBenchmarks/src/problems/hanging_chain.jl index 21aa903a..31484b67 100644 --- a/lib/OptimalControlBenchmarks/src/problems/hanging_chain.jl +++ b/lib/OptimalControlBenchmarks/src/problems/hanging_chain.jl @@ -1,16 +1,11 @@ -module hanging_chain - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=collect(0.:0.1:1.)) +function hanging_chain(grids) num_states = 3 num_controls = 1 tspan = (0.,1.) + scaled_grids = scale_grids!(tspan, grids) + @variables begin x₁(..) = 1., [tunable = false] x₂(..) = 0., [tunable = false] @@ -31,8 +26,7 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) ] # scale the constraint grid - constraint_grid = constraint_grid * (last(tspan) - first(tspan)) - constraint_grid = (constraint_grid .+ first(tspan)) + constraint_grid = scaled_grids.constraint_grid grid_cons_u1 = [x₁(tᵢ) ≳ 0. for tᵢ in constraint_grid[1:end - 1]] grid_cons_u2 = [x₂(tᵢ) ≳ 0. for tᵢ in constraint_grid] @@ -59,18 +53,8 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :hanging_chain, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/jackson.jl b/lib/OptimalControlBenchmarks/src/problems/jackson.jl index 96d6e78f..47cd58f3 100644 --- a/lib/OptimalControlBenchmarks/src/problems/jackson.jl +++ b/lib/OptimalControlBenchmarks/src/problems/jackson.jl @@ -1,16 +1,11 @@ -module jackson - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=collect(0.:0.1:1.)) +function jackson(grids) num_states = 3 num_controls = 1 tspan = (0.,1.) + scaled_grids = scale_grids!(tspan, grids) + @variables begin x₁(..) = 1., [tunable = false] x₂(..) = 0., [tunable = false] @@ -31,8 +26,7 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) ] # Define control discretization - constraint_grid = constraint_grid * (last(tspan) - first(tspan)) - constraint_grid = (constraint_grid .+ first(tspan)) + constraint_grid = scaled_grids.constraint_grid grid_cons_u1 = [x₁(tᵢ) ≲ 1.1 for tᵢ in constraint_grid] grid_cons_u2 = [x₂(tᵢ) ≲ 1.1 for tᵢ in constraint_grid] @@ -57,18 +51,8 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :jackson, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl b/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl index 17f7e88b..90a280cf 100644 --- a/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl +++ b/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl @@ -1,15 +1,10 @@ -module lotka_competitive - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=nothing) +function lotka_competitive(grids) num_states = 3 num_controls = 1 tspan = (0.,40.) + + scaled_grids = scale_grids!(tspan, grids) @variables begin x₀(..) = 0.5, [tunable = false] @@ -43,18 +38,8 @@ function make_problem(constraint_grid=nothing) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :lotka_competitive, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl b/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl index 918e08ef..d72b309f 100644 --- a/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl +++ b/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl @@ -1,16 +1,11 @@ -module lotka_shared - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=nothing) +function lotka_shared(grids) num_states = 4 num_controls = 1 tspan = (0.,40.) + scaled_grids = scale_grids!(tspan, grids) + @variables begin x₀(..) = 1.5, [tunable = false] x₁(..) = 1., [tunable = false] @@ -44,18 +39,8 @@ function make_problem(constraint_grid=nothing) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :lotka_shared, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl b/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl index d5466d09..ad7d5d4d 100644 --- a/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl +++ b/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl @@ -1,16 +1,11 @@ -module lotka_volterra - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=nothing) +function lotka_volterra(grids) num_states = 3 num_controls = 1 tspan = (0.,12.) + scaled_grids = scale_grids!(tspan, grids) + @variables begin x₀(..) = 0.5, [tunable = false] x₁(..) = 0.7, [tunable = false] @@ -41,18 +36,8 @@ function make_problem(constraint_grid=nothing) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :lotka_volterra, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/lqr.jl b/lib/OptimalControlBenchmarks/src/problems/lqr.jl index ab4d8105..b8c17529 100644 --- a/lib/OptimalControlBenchmarks/src/problems/lqr.jl +++ b/lib/OptimalControlBenchmarks/src/problems/lqr.jl @@ -1,15 +1,10 @@ -module lqr - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=nothing) +function lqr(grids) num_states = 2 num_controls = 1 tspan = (0.,10.) + + scaled_grids = scale_grids!(tspan, grids) @variables begin x(..) = 1., [tunable = false] @@ -39,18 +34,8 @@ function make_problem(constraint_grid=nothing) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :lqr, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl b/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl index 2fbcb958..86c9b12f 100644 --- a/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl +++ b/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl @@ -1,15 +1,10 @@ -module moon_landing - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=nothing) +function moon_landing(grids) num_states = 3 num_controls = 1 tspan = (0.,1.) + + scaled_grids = scale_grids!(tspan, grids) @variables begin h(..) = 1., [tunable = false] @@ -44,18 +39,8 @@ function make_problem(constraint_grid=nothing) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :moon_landing, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl b/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl index 029031ef..2b2ae51a 100644 --- a/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl +++ b/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl @@ -1,15 +1,10 @@ -module mountain_car - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=nothing) +function mountain_car(grids) num_states = 3 num_controls = 1 tspan = (0.,100.) + + scaled_grids = scale_grids!(tspan, grids) @variables begin x(..) = -0.5, [tunable = false] @@ -44,18 +39,8 @@ function make_problem(constraint_grid=nothing) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :mountain_car, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/ocean.jl b/lib/OptimalControlBenchmarks/src/problems/ocean.jl index 7d54fd76..1ca35fcd 100644 --- a/lib/OptimalControlBenchmarks/src/problems/ocean.jl +++ b/lib/OptimalControlBenchmarks/src/problems/ocean.jl @@ -1,16 +1,11 @@ -module ocean - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=collect(0.:0.1:1.)) +function ocean(grids) num_states = 3 num_controls = 2 tspan = (0.,400.) + scaled_grids = scale_grids!(tspan, grids) + @variables begin S(..) = 2.e3, [tunable = false] R(..) = 1.e4, [tunable = false] @@ -48,8 +43,7 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) ] # Define control discretization - constraint_grid = constraint_grid * (last(tspan) - first(tspan)) - constraint_grid = (constraint_grid .+ first(tspan)) + constraint_grid = scaled_grids.constraint_grid grid_cons_Su = [S(tᵢ) ≲ 1.e5 for tᵢ in constraint_grid] grid_cons_Ru = [R(tᵢ) ≲ 1.e5 for tᵢ in constraint_grid] @@ -76,18 +70,8 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :ocean, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl b/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl index 11cdad4c..8429c704 100644 --- a/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl +++ b/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl @@ -1,16 +1,11 @@ -module particle_steering - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=nothing) +function particle_steering(grids) num_states = 5 num_controls = 1 tspan = (0.,10.) + scaled_grids = scale_grids!(tspan, grids) + @variables begin x₁(..) = 0., [tunable = false] x₂(..) = 0., [tunable = false] @@ -55,18 +50,8 @@ function make_problem(constraint_grid=nothing) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :particle_steering, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl b/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl index 0e996b87..85d82bdd 100644 --- a/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl +++ b/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl @@ -1,15 +1,10 @@ -module quadrotor - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=collect(0.:0.1:1.)) +function quadrotor(grids) num_states = 7 num_controls = 4 tspan = (0.,7.5) + + scaled_grids = scale_grids!(tspan, grids) @variables begin x₁(..) = 0., [tunable = false] @@ -28,7 +23,7 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) g = 9.81, [tunable = false] M = 1.3, [tunable = false] L = 0.305, [tunable = false] - I = 0.0605, [tunable = false] + I = 0.0605, [tunable = false] end eqs = [ @@ -40,10 +35,7 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) D(x₆(t)) ~ -w₂(t) * L * u(t) / I + w₃(t) * L * u(t) / I ] - # scale the constraint grid - constraint_grid = constraint_grid * (last(tspan) - first(tspan)) - constraint_grid = (constraint_grid .+ first(tspan)) - + constraint_grid = scaled_grids.constraint_grid grid_cons_w = [w₁(tᵢ) + w₂(tᵢ) + w₃(tᵢ) ~ 1. for tᵢ in constraint_grid] grid_cons_x₃ = [x₃(tᵢ) ≳ 0. for tᵢ in constraint_grid] @@ -67,18 +59,8 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :quadrotor, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl b/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl index 98b326fd..a2bff554 100644 --- a/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl +++ b/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl @@ -1,16 +1,11 @@ -module rao_mease - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=nothing) +function rao_mease(grids) num_states = 2 num_controls = 1 tspan = (0.,10) + scaled_grids = scale_grids!(tspan, grids) + @variables begin x(..) = 1., [tunable = false] u(..) = 0., [input = true] @@ -39,18 +34,8 @@ function make_problem(constraint_grid=nothing) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :rao_mease, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/robbins.jl b/lib/OptimalControlBenchmarks/src/problems/robbins.jl index 161efd57..257b1b4b 100644 --- a/lib/OptimalControlBenchmarks/src/problems/robbins.jl +++ b/lib/OptimalControlBenchmarks/src/problems/robbins.jl @@ -1,16 +1,11 @@ -module robbins - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=collect(0.:0.1:1.)) +function robbins(grids) num_states = 4 num_controls = 1 tspan = (0.,10.) + scaled_grids = scale_grids!(tspan, grids) + @variables begin x₁(..) = 1., [tunable = false] x₂(..) = -2., [tunable = false] @@ -31,8 +26,7 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) ] # scale the constraint grid - constraint_grid = constraint_grid * (last(tspan) - first(tspan)) - constraint_grid = (constraint_grid .+ first(tspan))[1:end - 1] + constraint_grid = scaled_grids.constraint_grid[1:end - 1] grid_cons_x₁ = [x₃(tᵢ) ≳ 0. for tᵢ in constraint_grid] @@ -58,18 +52,8 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :robbins, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/three_tank.jl b/lib/OptimalControlBenchmarks/src/problems/three_tank.jl index 44f53d40..f5df5b47 100644 --- a/lib/OptimalControlBenchmarks/src/problems/three_tank.jl +++ b/lib/OptimalControlBenchmarks/src/problems/three_tank.jl @@ -1,15 +1,10 @@ -module three_tank - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=collect(0.:0.1:1.)) +function three_tank(grids) num_states = 4 num_controls = 3 tspan = (0.,12.) + + scaled_grids = scale_grids!(tspan, grids) @variables begin x₁(..) = 2., [tunable = false] @@ -36,9 +31,7 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) D(x₃(t)) ~ sqrt(x₂(t)) - sqrt(x₃(t)) + w₃(t) * sqrt(c₃ * x₁(t)) ] - # scale the constraint grid - constraint_grid = constraint_grid * (last(tspan) - first(tspan)) - constraint_grid = (constraint_grid .+ first(tspan)) + constraint_grid = scaled_grids.constraint_grid grid_cons_w = [w₁(tᵢ) + w₂(tᵢ) + w₃(tᵢ) ~ 1. for tᵢ in constraint_grid] @@ -61,18 +54,8 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :three_tank, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl b/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl index 22c4e94f..a0935c5d 100644 --- a/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl +++ b/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl @@ -1,15 +1,10 @@ -module tubular_reactor - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=nothing) +function tubular_reactor(grids) num_states = 2 num_controls = 1 tspan = (0.,1.) + + scaled_grids = scale_grids!(tspan, grids) @variables begin x₁(..) = 1., [tunable = false] @@ -32,18 +27,8 @@ function make_problem(constraint_grid=nothing) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :tubular_reactor, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl b/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl index 8967209d..3fe2b9b6 100644 --- a/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl +++ b/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl @@ -1,15 +1,10 @@ -module van_der_pol - -using ModelingToolkit -using ModelingToolkit: t_nounits as t, D_nounits as D -using Symbolics -using ..OptimalControlBenchmarks: OptimalControlBenchmark - -function make_problem(constraint_grid=collect(0.:0.1:1.)) +function van_der_pol(grids) num_states = 3 num_controls = 1 tspan = (0.,10.) + + scaled_grids = scale_grids!(tspan, grids) @variables begin x₁(..) = 0., [tunable = false] @@ -22,9 +17,7 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) D(x₂(t)) ~ x₁(t) ] - # scale the constraint grid - constraint_grid = constraint_grid * (last(tspan) - first(tspan)) - constraint_grid = (constraint_grid .+ first(tspan)) + constraint_grid = scaled_grids.constraint_grid grid_cons_x₁ = [x₁(tᵢ) ≳ -0.25 for tᵢ in constraint_grid] @@ -47,18 +40,8 @@ function make_problem(constraint_grid=collect(0.:0.1:1.)) return ( system = oc_problem, - tspan = tspan, - num_states = num_states, - num_controls = num_controls + grids = scaled_grids, + dims = (num_states, num_controls) ) end - - -benchmark = OptimalControlBenchmark( - :van_der_pol, - "Double integrator with quadratic control cost", - make_problem -) - -end \ No newline at end of file diff --git a/lib/OptimalControlBenchmarks/src/run_benchmarks.jl b/lib/OptimalControlBenchmarks/src/run_benchmarks.jl index 887b58a1..76c666bb 100644 --- a/lib/OptimalControlBenchmarks/src/run_benchmarks.jl +++ b/lib/OptimalControlBenchmarks/src/run_benchmarks.jl @@ -1,29 +1,27 @@ using BenchmarkTools using OptimalControlBenchmarks -function run_all(benchmarks, optimizer, constraint_grid, control_grid, shooting_grid) +function run_all(benchmarks, optimizer, grids) results = [] for prob in benchmarks - println("Running ", prob.name) + # println("Running ", prob.name) sol = solve_with_corleone( prob, optimizer, - constraint_grid, - control_grid, - shooting_grid + grids ) println(sol) t = 0 # @belapsed solve_with_corleone($prob) push!(results, ( - name = prob.name, + name = "", # prob.name, time = t )) end return results -end \ No newline at end of file +end diff --git a/lib/OptimalControlBenchmarks/src/scale_grid.jl b/lib/OptimalControlBenchmarks/src/scale_grid.jl new file mode 100644 index 00000000..4980a6c4 --- /dev/null +++ b/lib/OptimalControlBenchmarks/src/scale_grid.jl @@ -0,0 +1,9 @@ +function scale_grids!(tspan::Tuple{Float64, Float64}, grids::BenchmarkGrids) + scale = last(tspan) - first(tspan) + shift = first(tspan) + + grids.control_grid .*= scale .+ shift + grids.shooting_grid .*= scale .+ shift + grids.constraint_grid .*= scale .+ shift + return grids +end diff --git a/lib/OptimalControlBenchmarks/src/solver_corleone.jl b/lib/OptimalControlBenchmarks/src/solver_corleone.jl index 2d4d8501..6a905e46 100644 --- a/lib/OptimalControlBenchmarks/src/solver_corleone.jl +++ b/lib/OptimalControlBenchmarks/src/solver_corleone.jl @@ -1,30 +1,15 @@ -using OptimalControlBenchmarks -using ModelingToolkit -using ModelingToolkit: inputs -using Corleone -using OrdinaryDiffEqTsit5 -using Optimization -using OptimizationMOI -using ForwardDiff -using ComponentArrays -using LuxCore, Random +function solve_with_corleone(benchmark, optimizer, grids) -function solve_with_corleone(benchmark, optimizer, constraint_grid, control_grid, shooting_grid) - - data = benchmark.make_problem(constraint_grid) + data = benchmark(grids) oc_problem = data.system - tspan = data.tspan - - # scale the control grid - control_grid = control_grid * (last(tspan) - first(tspan)) - control_grid = (control_grid .+ first(tspan)) - # scale the shooting grid - shooting_grid = shooting_grid * (last(tspan) - first(tspan)) - shooting_grid = (shooting_grid .+ first(tspan)) + scaled_grids = data.grids + num_states, num_controls = data.dims # Extract control variable controls = inputs(oc_problem) + control_grid = scaled_grids.control_grid + shooting_grid = scaled_grids.shooting_grid control_map = [ c => control_grid for c in controls @@ -47,7 +32,7 @@ function solve_with_corleone(benchmark, optimizer, constraint_grid, control_grid sol = solve( optprob, optimizer, - max_iter = 1000, + max_iter = 100, tol = 5e-6, hessian_approximation = "limited-memory" ) @@ -56,8 +41,8 @@ function solve_with_corleone(benchmark, optimizer, constraint_grid, control_grid u_opt = ComponentVector(sol.u, optprob.f.f.ax) opt_traj, _ = dynopt.layer(nothing, u_opt, LuxCore.initialstates(Random.default_rng(), dynopt.layer)) - f = plot_oc_problem(opt_traj, data.num_states, data.num_controls) + f = plot_oc_problem(opt_traj, num_states, num_controls) display(f) return sol -end \ No newline at end of file +end diff --git a/lib/OptimalControlBenchmarks/src/types.jl b/lib/OptimalControlBenchmarks/src/types.jl index 797c295d..b1f562af 100644 --- a/lib/OptimalControlBenchmarks/src/types.jl +++ b/lib/OptimalControlBenchmarks/src/types.jl @@ -2,4 +2,10 @@ struct OptimalControlBenchmark name::Symbol description::String make_problem::Function -end \ No newline at end of file +end + +struct BenchmarkGrids + control_grid::Vector{Float64} + shooting_grid::Vector{Float64} + constraint_grid::Vector{Float64} +end diff --git a/lib/OptimalControlBenchmarks/test.jl b/lib/OptimalControlBenchmarks/test.jl index 21473c94..cb891d68 100644 --- a/lib/OptimalControlBenchmarks/test.jl +++ b/lib/OptimalControlBenchmarks/test.jl @@ -7,7 +7,7 @@ using ModelingToolkit benchmarks = load_benchmarks() # choose the optimizer -#optimizer = UnoSolver.Optimizer() +# optimizer = UnoSolver.Optimizer() optimizer = Ipopt.Optimizer() # settings for discretization, given as grids in [0.,1.] @@ -16,6 +16,12 @@ constraint_grid = collect(0.:0.05:1.) control_grid = collect(0.:0.05:1.)[1:end-1] shooting_grid = collect(0.:0.05:1.) -df = run_all(benchmarks, optimizer, constraint_grid, control_grid, shooting_grid) +grids = OptimalControlBenchmarks.BenchmarkGrids( + constraint_grid, + control_grid, + shooting_grid +) -println(df) \ No newline at end of file +df = run_all(benchmarks, optimizer, grids) + +println(df) From b3ec42bb7c1a6e0ab1bd4e791c93a0c062493eb8 Mon Sep 17 00:00:00 2001 From: Robert Lampel Date: Fri, 13 Mar 2026 10:24:26 +0100 Subject: [PATCH 05/73] major rework of problem definitions --- lib/OptimalControlBenchmarks/Project.toml | 2 +- .../{test.jl => main.jl} | 2 ++ .../src/problems/batch_reactor.jl | 3 ++- .../src/problems/bryson_denham.jl | 3 ++- .../src/problems/cart_pendulum.jl | 3 ++- .../src/problems/catalyst_mixing.jl | 3 ++- .../src/problems/cushioned_oscillation.jl | 3 ++- .../src/problems/denbigh.jl | 3 ++- .../src/problems/dielectrophoretic_particle.jl | 3 ++- .../src/problems/double_oscillator.jl | 3 ++- .../src/problems/ducted_fan.jl | 3 ++- .../src/problems/egerstedt.jl | 3 ++- .../src/problems/electric_car.jl | 3 ++- .../src/problems/f8.jl | 3 ++- .../src/problems/fuller.jl | 3 ++- .../src/problems/goddarts_rocket.jl | 3 ++- .../src/problems/hang_glider.jl | 3 ++- .../src/problems/jackson.jl | 3 ++- .../src/problems/lotka_competitive.jl | 3 ++- .../src/problems/lotka_shared.jl | 3 ++- .../src/problems/lotka_volterra.jl | 3 ++- .../src/problems/lqr.jl | 3 ++- .../src/problems/moon_landing.jl | 3 ++- .../src/problems/mountain_car.jl | 3 ++- .../src/problems/ocean.jl | 3 ++- .../src/problems/particle_steering.jl | 3 ++- .../src/problems/quadrotor.jl | 3 ++- .../src/problems/rao_mease.jl | 3 ++- .../src/problems/robbins.jl | 3 ++- .../src/problems/three_tank.jl | 3 ++- .../src/problems/tubular_reactor.jl | 3 ++- .../src/problems/van_der_pol.jl | 3 ++- .../src/run_benchmarks.jl | 6 +++--- .../src/solver_corleone.jl | 18 ++++++++++++------ 34 files changed, 78 insertions(+), 40 deletions(-) rename lib/OptimalControlBenchmarks/{test.jl => main.jl} (92%) diff --git a/lib/OptimalControlBenchmarks/Project.toml b/lib/OptimalControlBenchmarks/Project.toml index 076ad0cf..8880a171 100644 --- a/lib/OptimalControlBenchmarks/Project.toml +++ b/lib/OptimalControlBenchmarks/Project.toml @@ -1,5 +1,5 @@ name = "OptimalControlBenchmarks" -uuid = "6590a4b1-d036-41fe-a5b4-03a980244102" +uuid = "d147904a-1eb9-11f1-01e7-bdf88e0f9445" authors = ["Robert Lampel", "Julius Martensen", "Christoph Plate", "Reinhold Wittmann"] version = "0.0.3" diff --git a/lib/OptimalControlBenchmarks/test.jl b/lib/OptimalControlBenchmarks/main.jl similarity index 92% rename from lib/OptimalControlBenchmarks/test.jl rename to lib/OptimalControlBenchmarks/main.jl index cb891d68..1f7f2c1d 100644 --- a/lib/OptimalControlBenchmarks/test.jl +++ b/lib/OptimalControlBenchmarks/main.jl @@ -11,6 +11,7 @@ benchmarks = load_benchmarks() optimizer = Ipopt.Optimizer() # settings for discretization, given as grids in [0.,1.] + # the constraint grid is assumed to contain 0. and 1. constraint_grid = collect(0.:0.05:1.) control_grid = collect(0.:0.05:1.)[1:end-1] @@ -22,6 +23,7 @@ grids = OptimalControlBenchmarks.BenchmarkGrids( shooting_grid ) +# run all benchmarks from the `problems` folder df = run_all(benchmarks, optimizer, grids) println(df) diff --git a/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl b/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl index 7a367991..f101e99f 100644 --- a/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl +++ b/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl @@ -28,7 +28,8 @@ function batch_reactor(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Batch Reactor" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl b/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl index 62bb6028..b41bfb33 100644 --- a/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl +++ b/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl @@ -41,7 +41,8 @@ function bryson_denham(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Bryson Denham" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl b/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl index 23ecfa96..31906fc0 100644 --- a/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl +++ b/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl @@ -56,7 +56,8 @@ function cart_pendulum(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Cart Pendulum" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl b/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl index 48a2b26f..98ccbcc0 100644 --- a/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl +++ b/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl @@ -28,7 +28,8 @@ function catalyst_mixing(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Catalyst Mixing" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl b/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl index f39009d7..31fe8a54 100644 --- a/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl +++ b/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl @@ -43,7 +43,8 @@ function cushioned_oscillation(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Cushioned Oscillation" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/denbigh.jl b/lib/OptimalControlBenchmarks/src/problems/denbigh.jl index 9428ac67..746d719e 100644 --- a/lib/OptimalControlBenchmarks/src/problems/denbigh.jl +++ b/lib/OptimalControlBenchmarks/src/problems/denbigh.jl @@ -43,7 +43,8 @@ function denbigh(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Denbigh Reaction" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl b/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl index 5c258786..14541990 100644 --- a/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl +++ b/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl @@ -42,7 +42,8 @@ function dielectrophoretic_particle(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Dielectrophoretic Particle" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl b/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl index aaefc98a..275b13c9 100644 --- a/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl +++ b/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl @@ -43,7 +43,8 @@ function double_oscillator(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Double Oscillator" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl b/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl index faa39d7d..1887a8b1 100644 --- a/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl +++ b/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl @@ -71,7 +71,8 @@ function ducted_fan(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Ducted Fan" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl b/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl index b6e0adea..dc290384 100644 --- a/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl +++ b/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl @@ -46,7 +46,8 @@ function egerstedt(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Egerstedt standard problem" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/electric_car.jl b/lib/OptimalControlBenchmarks/src/problems/electric_car.jl index ce8239ab..089f5e58 100644 --- a/lib/OptimalControlBenchmarks/src/problems/electric_car.jl +++ b/lib/OptimalControlBenchmarks/src/problems/electric_car.jl @@ -66,7 +66,8 @@ function electric_car(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Electric Car" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/f8.jl b/lib/OptimalControlBenchmarks/src/problems/f8.jl index 3960265d..a3b78656 100644 --- a/lib/OptimalControlBenchmarks/src/problems/f8.jl +++ b/lib/OptimalControlBenchmarks/src/problems/f8.jl @@ -56,7 +56,8 @@ function f8(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "F-8 aircraft" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/fuller.jl b/lib/OptimalControlBenchmarks/src/problems/fuller.jl index 61d39514..7e7b4f24 100644 --- a/lib/OptimalControlBenchmarks/src/problems/fuller.jl +++ b/lib/OptimalControlBenchmarks/src/problems/fuller.jl @@ -38,7 +38,8 @@ function fuller(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Fuller's problem" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl b/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl index 03f87ad9..2f26e253 100644 --- a/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl +++ b/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl @@ -59,7 +59,8 @@ function goddarts_rocket(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Goddart's rocket problem" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl b/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl index 3c43ad0b..12ad823d 100644 --- a/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl +++ b/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl @@ -70,7 +70,8 @@ function hang_glider(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Hang Glider" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/jackson.jl b/lib/OptimalControlBenchmarks/src/problems/jackson.jl index 47cd58f3..555bb57d 100644 --- a/lib/OptimalControlBenchmarks/src/problems/jackson.jl +++ b/lib/OptimalControlBenchmarks/src/problems/jackson.jl @@ -52,7 +52,8 @@ function jackson(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Jackson" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl b/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl index 90a280cf..06caa53c 100644 --- a/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl +++ b/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl @@ -39,7 +39,8 @@ function lotka_competitive(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Lotka Volterra Competitive" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl b/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl index d72b309f..7e9b5d5b 100644 --- a/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl +++ b/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl @@ -40,7 +40,8 @@ function lotka_shared(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Lotka Volterra Shared" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl b/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl index ad7d5d4d..663ab46e 100644 --- a/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl +++ b/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl @@ -37,7 +37,8 @@ function lotka_volterra(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Lotka Volterra fishing problem" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/lqr.jl b/lib/OptimalControlBenchmarks/src/problems/lqr.jl index b8c17529..663cc7cc 100644 --- a/lib/OptimalControlBenchmarks/src/problems/lqr.jl +++ b/lib/OptimalControlBenchmarks/src/problems/lqr.jl @@ -35,7 +35,8 @@ function lqr(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Linear Quadratic Regulator" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl b/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl index 86c9b12f..2542ebe8 100644 --- a/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl +++ b/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl @@ -40,7 +40,8 @@ function moon_landing(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Moon Landing" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl b/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl index 2b2ae51a..704c69c5 100644 --- a/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl +++ b/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl @@ -40,7 +40,8 @@ function mountain_car(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Mountain Car" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/ocean.jl b/lib/OptimalControlBenchmarks/src/problems/ocean.jl index 1ca35fcd..dc31b240 100644 --- a/lib/OptimalControlBenchmarks/src/problems/ocean.jl +++ b/lib/OptimalControlBenchmarks/src/problems/ocean.jl @@ -71,7 +71,8 @@ function ocean(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Ocean" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl b/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl index 8429c704..cd6386b1 100644 --- a/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl +++ b/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl @@ -51,7 +51,8 @@ function particle_steering(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Particle steering problem" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl b/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl index 85d82bdd..cba206e5 100644 --- a/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl +++ b/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl @@ -60,7 +60,8 @@ function quadrotor(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Quadrotor helicopter control problem" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl b/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl index a2bff554..4c1e9ee6 100644 --- a/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl +++ b/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl @@ -35,7 +35,8 @@ function rao_mease(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Rao Mease" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/robbins.jl b/lib/OptimalControlBenchmarks/src/problems/robbins.jl index 257b1b4b..86e8ea63 100644 --- a/lib/OptimalControlBenchmarks/src/problems/robbins.jl +++ b/lib/OptimalControlBenchmarks/src/problems/robbins.jl @@ -53,7 +53,8 @@ function robbins(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Robbins" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/three_tank.jl b/lib/OptimalControlBenchmarks/src/problems/three_tank.jl index f5df5b47..dcfe9c2a 100644 --- a/lib/OptimalControlBenchmarks/src/problems/three_tank.jl +++ b/lib/OptimalControlBenchmarks/src/problems/three_tank.jl @@ -55,7 +55,8 @@ function three_tank(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Three Tank multimode problem" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl b/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl index a0935c5d..9f780ec2 100644 --- a/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl +++ b/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl @@ -28,7 +28,8 @@ function tubular_reactor(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Tubular Reactor" ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl b/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl index 3fe2b9b6..de7e40c9 100644 --- a/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl +++ b/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl @@ -41,7 +41,8 @@ function van_der_pol(grids) return ( system = oc_problem, grids = scaled_grids, - dims = (num_states, num_controls) + dims = (num_states, num_controls), + name = "Van der Pol Oscillator" ) end diff --git a/lib/OptimalControlBenchmarks/src/run_benchmarks.jl b/lib/OptimalControlBenchmarks/src/run_benchmarks.jl index 76c666bb..a10e9aa9 100644 --- a/lib/OptimalControlBenchmarks/src/run_benchmarks.jl +++ b/lib/OptimalControlBenchmarks/src/run_benchmarks.jl @@ -6,8 +6,8 @@ function run_all(benchmarks, optimizer, grids) results = [] for prob in benchmarks - - # println("Running ", prob.name) + name = nameof(prob) + println("Running ", name) sol = solve_with_corleone( prob, optimizer, @@ -17,7 +17,7 @@ function run_all(benchmarks, optimizer, grids) t = 0 # @belapsed solve_with_corleone($prob) push!(results, ( - name = "", # prob.name, + name = name, time = t )) diff --git a/lib/OptimalControlBenchmarks/src/solver_corleone.jl b/lib/OptimalControlBenchmarks/src/solver_corleone.jl index 6a905e46..248d284e 100644 --- a/lib/OptimalControlBenchmarks/src/solver_corleone.jl +++ b/lib/OptimalControlBenchmarks/src/solver_corleone.jl @@ -1,4 +1,4 @@ -function solve_with_corleone(benchmark, optimizer, grids) +function solve_with_corleone(benchmark, optimizer, grids, plot_res=true) data = benchmark(grids) @@ -37,12 +37,18 @@ function solve_with_corleone(benchmark, optimizer, grids) hessian_approximation = "limited-memory" ) - # plotting - u_opt = ComponentVector(sol.u, optprob.f.f.ax) - opt_traj, _ = dynopt.layer(nothing, u_opt, LuxCore.initialstates(Random.default_rng(), dynopt.layer)) + if plot_res + # plotting + u_opt = ComponentVector(sol.u, optprob.f.f.ax) + opt_traj, _ = dynopt.layer( + nothing, u_opt, + LuxCore.initialstates(Random.default_rng(), dynopt.layer) + ) + + f = plot_oc_problem(opt_traj, num_states, num_controls) + display(f) + end - f = plot_oc_problem(opt_traj, num_states, num_controls) - display(f) return sol end From 84e2bad4bb7073454a1ab96464efd5c08a26b679 Mon Sep 17 00:00:00 2001 From: Robert Lampel Date: Fri, 13 Mar 2026 10:44:15 +0100 Subject: [PATCH 06/73] fixed missing grid --- lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl b/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl index 275b13c9..c624dabc 100644 --- a/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl +++ b/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl @@ -4,6 +4,8 @@ function double_oscillator(grids) num_controls = 1 tspan = (0.,2 * pi) + scaled_grids = scale_grids!(tspan, grids) + @variables begin x₀(..) = 0.0, [tunable = false] x₁(..) = 0.0, [tunable = false] From b6a6b030e9f02135c77fc4708b453ef16e509853 Mon Sep 17 00:00:00 2001 From: Robert Lampel Date: Fri, 13 Mar 2026 12:49:21 +0100 Subject: [PATCH 07/73] runic formatting --- lib/OptimalControlBenchmarks/Project.toml | 1 + lib/OptimalControlBenchmarks/main.jl | 12 ++-- .../src/plot_solution.jl | 2 +- .../src/problem_registry.jl | 7 ++- .../src/problems/batch_reactor.jl | 12 ++-- .../src/problems/bryson_denham.jl | 20 +++--- .../src/problems/cart_pendulum.jl | 28 ++++----- .../src/problems/catalyst_mixing.jl | 22 +++---- .../src/problems/cushioned_oscillation.jl | 28 ++++----- .../src/problems/denbigh.jl | 16 ++--- .../problems/dielectrophoretic_particle.jl | 16 ++--- .../src/problems/double_oscillator.jl | 16 ++--- .../src/problems/ducted_fan.jl | 52 ++++++++-------- .../src/problems/egerstedt.jl | 12 ++-- .../src/problems/electric_car.jl | 62 +++++++++---------- .../src/problems/f8.jl | 20 +++--- .../src/problems/fuller.jl | 14 ++--- .../src/problems/goddarts_rocket.jl | 53 ++++++++-------- .../src/problems/hang_glider.jl | 34 +++++----- .../src/problems/hanging_chain.jl | 38 ++++++------ .../src/problems/jackson.jl | 34 +++++----- .../src/problems/lotka_competitive.jl | 20 +++--- .../src/problems/lotka_shared.jl | 14 ++--- .../src/problems/lotka_volterra.jl | 12 ++-- .../src/problems/lqr.jl | 26 ++++---- .../src/problems/moon_landing.jl | 26 ++++---- .../src/problems/mountain_car.jl | 30 ++++----- .../src/problems/ocean.jl | 48 +++++++------- .../src/problems/particle_steering.jl | 32 +++++----- .../src/problems/quadrotor.jl | 50 +++++++-------- .../src/problems/rao_mease.jl | 16 ++--- .../src/problems/robbins.jl | 32 +++++----- .../src/problems/three_tank.jl | 48 +++++++------- .../src/problems/tubular_reactor.jl | 18 +++--- .../src/problems/van_der_pol.jl | 28 ++++----- .../src/run_benchmarks.jl | 16 ++--- .../src/solver_corleone.jl | 24 +++---- lib/OptimalControlBenchmarks/test/runtests.jl | 2 +- 38 files changed, 472 insertions(+), 469 deletions(-) diff --git a/lib/OptimalControlBenchmarks/Project.toml b/lib/OptimalControlBenchmarks/Project.toml index 8880a171..7ecd7ce5 100644 --- a/lib/OptimalControlBenchmarks/Project.toml +++ b/lib/OptimalControlBenchmarks/Project.toml @@ -23,6 +23,7 @@ OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" +Runic = "62bfec6d-59d7-401d-8490-b29ee721c001" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" SciMLStructures = "53ae85a6-f571-4167-b2af-e1d143709226" StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" diff --git a/lib/OptimalControlBenchmarks/main.jl b/lib/OptimalControlBenchmarks/main.jl index 1f7f2c1d..bed2fa0e 100644 --- a/lib/OptimalControlBenchmarks/main.jl +++ b/lib/OptimalControlBenchmarks/main.jl @@ -13,14 +13,14 @@ optimizer = Ipopt.Optimizer() # settings for discretization, given as grids in [0.,1.] # the constraint grid is assumed to contain 0. and 1. -constraint_grid = collect(0.:0.05:1.) -control_grid = collect(0.:0.05:1.)[1:end-1] -shooting_grid = collect(0.:0.05:1.) +constraint_grid = collect(0.0:0.05:1.0) +control_grid = collect(0.0:0.05:1.0)[1:(end - 1)] +shooting_grid = collect(0.0:0.05:1.0) grids = OptimalControlBenchmarks.BenchmarkGrids( - constraint_grid, - control_grid, - shooting_grid + constraint_grid, + control_grid, + shooting_grid ) # run all benchmarks from the `problems` folder diff --git a/lib/OptimalControlBenchmarks/src/plot_solution.jl b/lib/OptimalControlBenchmarks/src/plot_solution.jl index 140e6933..38e6bb4c 100644 --- a/lib/OptimalControlBenchmarks/src/plot_solution.jl +++ b/lib/OptimalControlBenchmarks/src/plot_solution.jl @@ -4,7 +4,7 @@ function plot_oc_problem(sol, num_states, num_controls) scatterlines!(ax, sol, vars = collect(1:1:num_states)) f[1, 2] = Legend(f, ax, "States", framevisible = false) ax1 = CairoMakie.Axis(f[2, 1]) - stairs!(ax1, sol, vars = collect(num_states + 1:1:num_states + num_controls)) + stairs!(ax1, sol, vars = collect((num_states + 1):1:(num_states + num_controls))) f[2, 2] = Legend(f, ax1, "Controls", framevisible = false) return f end diff --git a/lib/OptimalControlBenchmarks/src/problem_registry.jl b/lib/OptimalControlBenchmarks/src/problem_registry.jl index 61ffb4e0..675c1f5a 100644 --- a/lib/OptimalControlBenchmarks/src/problem_registry.jl +++ b/lib/OptimalControlBenchmarks/src/problem_registry.jl @@ -5,7 +5,8 @@ function load_benchmarks() for f in readdir(problems_dir) # Skip non-julia files endswith(f, ".jl") || continue - + + # Get the path and the expected function name (stripping .jl) path = joinpath(problems_dir, f) func_name_sym = Symbol(splitext(f)[1]) @@ -17,14 +18,14 @@ function load_benchmarks() try # Look up the symbol in the current module's scope func_obj = getfield(@__MODULE__, func_name_sym) - + if func_obj isa Function push!(benchmarks, func_obj) else @warn "File $f included, but $func_name_sym is not a function." end catch e - @error "Failed to load function $func_name_sym from $f" exception=e + @error "Failed to load function $func_name_sym from $f" exception = e end end diff --git a/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl b/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl index f101e99f..93d02988 100644 --- a/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl +++ b/lib/OptimalControlBenchmarks/src/problems/batch_reactor.jl @@ -2,7 +2,7 @@ function batch_reactor(grids) num_states = 2 num_controls = 1 - tspan = (0.,1.) + tspan = (0.0, 1.0) scaled_grids = scale_grids!(tspan, grids) @@ -13,8 +13,8 @@ function batch_reactor(grids) end eqs = [ - D(x₁(t)) ~ -4.e3 * exp(-2500 / u(t)) * x₁(t)^2 - D(x₂(t)) ~ 4.e3 * exp(-2500 / u(t)) * x₁(t)^2 - 62.e4 * exp(-5000 / u(t)) * x₂(t)^2 + D(x₁(t)) ~ -4.0e3 * exp(-2500 / u(t)) * x₁(t)^2 + D(x₂(t)) ~ 4.0e3 * exp(-2500 / u(t)) * x₁(t)^2 - 62.0e4 * exp(-5000 / u(t)) * x₂(t)^2 ] costs = [-x₂(1.0)] @@ -27,9 +27,9 @@ function batch_reactor(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Batch Reactor" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Batch Reactor", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl b/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl index b41bfb33..d9c7e858 100644 --- a/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl +++ b/lib/OptimalControlBenchmarks/src/problems/bryson_denham.jl @@ -2,7 +2,7 @@ function bryson_denham(grids) num_states = 2 num_controls = 1 - tspan = (0.,1.) + tspan = (0.0, 1.0) scaled_grids = scale_grids!(tspan, grids) @@ -17,18 +17,18 @@ function bryson_denham(grids) D(v(t)) ~ w(t) ] - constraint_grid = scaled_grids.constraint_grid[1:end - 1] + constraint_grid = scaled_grids.constraint_grid[1:(end - 1)] - grid_cons = [x(tᵢ) ≲ 1/9 for tᵢ in constraint_grid] + grid_cons = [x(tᵢ) ≲ 1 / 9 for tᵢ in constraint_grid] cons = [ - x(last(tspan)) ~ 0., - v(last(tspan)) ~ -1., - grid_cons... + x(last(tspan)) ~ 0.0, + v(last(tspan)) ~ -1.0, + grid_cons..., ] costs = [ - Symbolics.Integral(t in (0.0, 1.0))(w(t)^2) + Symbolics.Integral(t in (0.0, 1.0))(w(t)^2), ] @named oc_problem = System( @@ -40,9 +40,9 @@ function bryson_denham(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Bryson Denham" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Bryson Denham", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl b/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl index 31906fc0..fd98b6a4 100644 --- a/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl +++ b/lib/OptimalControlBenchmarks/src/problems/cart_pendulum.jl @@ -2,7 +2,7 @@ function cart_pendulum(grids) num_states = 5 num_controls = 1 - tspan = (0.,4.) + tspan = (0.0, 4.0) scaled_grids = scale_grids!(tspan, grids) @@ -11,14 +11,14 @@ function cart_pendulum(grids) θ(..) = 0.0, [tunable = false] dx(..) = 0.0, [tunable = false] dtheta(..) = 0.0, [tunable = false] - w(..) = 0.0, [bounds=(-30,30), input = true] + w(..) = 0.0, [bounds = (-30, 30), input = true] end - + @parameters begin - α = 10., [tunable = false] - β = 50., [tunable = false] + α = 10.0, [tunable = false] + β = 50.0, [tunable = false] γ = 0.5, [tunable = false] - M = 1., [tunable = false] + M = 1.0, [tunable = false] m = 0.1, [tunable = false] g = 9.81, [tunable = false] end @@ -31,18 +31,18 @@ function cart_pendulum(grids) ] constraint_grid = scaled_grids.constraint_grid - - grid_cons_le = [x(tᵢ) ≲ 2. for tᵢ in constraint_grid] - grid_cons_ge = [x(tᵢ) ≳ -2. for tᵢ in constraint_grid] + + grid_cons_le = [x(tᵢ) ≲ 2.0 for tᵢ in constraint_grid] + grid_cons_ge = [x(tᵢ) ≳ -2.0 for tᵢ in constraint_grid] cons = [ grid_cons_le..., - grid_cons_ge... + grid_cons_ge..., ] costs = [ Symbolics.Integral(t in (0.0, 1.0))( - 1.e-3 * (α * x(t)^2 + β * (θ(t) - pi)^2 + γ * w(t)^2) + 1.0e-3 * (α * x(t)^2 + β * (θ(t) - pi)^2 + γ * w(t)^2) ), ] @@ -55,9 +55,9 @@ function cart_pendulum(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Cart Pendulum" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Cart Pendulum", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl b/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl index 98ccbcc0..7cde346c 100644 --- a/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl +++ b/lib/OptimalControlBenchmarks/src/problems/catalyst_mixing.jl @@ -2,19 +2,19 @@ function catalyst_mixing(grids) num_states = 2 num_controls = 1 - tspan = (0.,1.) - + tspan = (0.0, 1.0) + scaled_grids = scale_grids!(tspan, grids) @variables begin - x₁(..) = 1.0, [tunable = false] - x₂(..) = 0.0, [tunable = false] - w(..) = 0.0, [bounds = (0.0, 1.0), input = true] + x₁(..) = 1.0, [tunable = false] + x₂(..) = 0.0, [tunable = false] + w(..) = 0.0, [bounds = (0.0, 1.0), input = true] end - + eqs = [ - D(x₁(t)) ~ w(t) * (10 * x₂(t) - x₁(t)) - D(x₂(t)) ~ w(t) * (x₁(t) - 10 * x₂(t)) - (1 - w(t)) * x₂(t) + D(x₁(t)) ~ w(t) * (10 * x₂(t) - x₁(t)) + D(x₂(t)) ~ w(t) * (x₁(t) - 10 * x₂(t)) - (1 - w(t)) * x₂(t) ] costs = [-1 + x₁(last(tspan)) + x₂(last(tspan))] @@ -27,9 +27,9 @@ function catalyst_mixing(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Catalyst Mixing" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Catalyst Mixing", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl b/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl index 31fe8a54..c6b54125 100644 --- a/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl +++ b/lib/OptimalControlBenchmarks/src/problems/cushioned_oscillation.jl @@ -2,8 +2,8 @@ function cushioned_oscillation(grids) num_states = 3 num_controls = 1 - tspan = (0.,10.) - + tspan = (0.0, 10.0) + scaled_grids = scale_grids!(tspan, grids) @variables begin @@ -13,25 +13,25 @@ function cushioned_oscillation(grids) u(..) = 0.0, [bounds = (-5.0, 5.0), input = true] end @parameters begin - tₛ = 1., [bounds = (1.e-3, Inf), tunable = true] + tₛ = 1.0, [bounds = (1.0e-3, Inf), tunable = true] end @constants begin - m = 5., [tunable = false] - c = 10., [tunable = false] + m = 5.0, [tunable = false] + c = 10.0, [tunable = false] end - + eqs = [ D(x(t)) ~ tₛ * v(t) D(v(t)) ~ tₛ * (1 / m * (u(t) - c * x(t))) D(obj(t)) ~ tₛ ] - + cons = [ - x(last(tspan)) ~ 0., - v(last(tspan)) ~ 0., + x(last(tspan)) ~ 0.0, + v(last(tspan)) ~ 0.0, ] - - costs = [obj(1.)] + + costs = [obj(1.0)] @named oc_problem = System( eqs, @@ -42,9 +42,9 @@ function cushioned_oscillation(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Cushioned Oscillation" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Cushioned Oscillation", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/denbigh.jl b/lib/OptimalControlBenchmarks/src/problems/denbigh.jl index 746d719e..be466fd8 100644 --- a/lib/OptimalControlBenchmarks/src/problems/denbigh.jl +++ b/lib/OptimalControlBenchmarks/src/problems/denbigh.jl @@ -3,7 +3,7 @@ function denbigh(grids) # We start by defining our system num_states = 3 num_controls = 1 - tspan = (0.,1000.) + tspan = (0.0, 1000.0) scaled_grids = scale_grids!(tspan, grids) @@ -15,16 +15,16 @@ function denbigh(grids) end @parameters begin - tₛ = 1., [bounds = (1.e-3, Inf), tunable = true] + tₛ = 1.0, [bounds = (1.0e-3, Inf), tunable = true] end @constants begin - E[1:4] = [3.e3, 6.e3, 3.e3, 0.], [tunable = false] - k⁰[1:4] = [1.e3, 1.e7, 1.e1, 1.e-3], [tunable = false] + E[1:4] = [3.0e3, 6.0e3, 3.0e3, 0.0], [tunable = false] + k⁰[1:4] = [1.0e3, 1.0e7, 1.0e1, 1.0e-3], [tunable = false] end # auxiliary equations for the kᵢ - k = [k⁰[i] * exp(-E[i] / T(t)) for i in [1,2,3,4]] + k = [k⁰[i] * exp(-E[i] / T(t)) for i in [1, 2, 3, 4]] eqs = [ D(x₁(t)) ~ -k[1] * x₁(t) - k[2] * x₁(t) @@ -42,9 +42,9 @@ function denbigh(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Denbigh Reaction" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Denbigh Reaction", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl b/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl index 14541990..be457c59 100644 --- a/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl +++ b/lib/OptimalControlBenchmarks/src/problems/dielectrophoretic_particle.jl @@ -2,7 +2,7 @@ function dielectrophoretic_particle(grids) num_states = 3 num_controls = 1 - tspan = (0.,5.) + tspan = (0.0, 5.0) scaled_grids = scale_grids!(tspan, grids) @@ -13,11 +13,11 @@ function dielectrophoretic_particle(grids) u(..) = 1.0, [bounds = (-1.0, 1.0), input = true] end @parameters begin - tₛ = 1., [bounds = (1.e-1, Inf), tunable = true] + tₛ = 1.0, [bounds = (1.0e-1, Inf), tunable = true] end @constants begin α = -0.75, [tunable = false] - c = 1., [tunable = false] + c = 1.0, [tunable = false] end eqs = [ @@ -27,10 +27,10 @@ function dielectrophoretic_particle(grids) ] cons = [ - x₀(last(tspan)) ~ 2., + x₀(last(tspan)) ~ 2.0, ] - costs = [obj(1.)] + costs = [obj(1.0)] @named oc_problem = System( eqs, @@ -41,9 +41,9 @@ function dielectrophoretic_particle(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Dielectrophoretic Particle" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Dielectrophoretic Particle", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl b/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl index c624dabc..2f8ab0c1 100644 --- a/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl +++ b/lib/OptimalControlBenchmarks/src/problems/double_oscillator.jl @@ -2,7 +2,7 @@ function double_oscillator(grids) num_states = 5 num_controls = 1 - tspan = (0.,2 * pi) + tspan = (0.0, 2 * pi) scaled_grids = scale_grids!(tspan, grids) @@ -15,10 +15,10 @@ function double_oscillator(grids) end @constants begin - m₁ = 200., [tunable = false] - m₂ = 2., [tunable = false] - k₁ = 100., [tunable = false] - k₂ = 3., [tunable = false] + m₁ = 200.0, [tunable = false] + m₂ = 2.0, [tunable = false] + k₁ = 100.0, [tunable = false] + k₂ = 3.0, [tunable = false] c = 0.5, [tunable = false] T = 2 * pi, [tunable = false] end @@ -44,9 +44,9 @@ function double_oscillator(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Double Oscillator" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Double Oscillator", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl b/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl index 1887a8b1..6509d76c 100644 --- a/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl +++ b/lib/OptimalControlBenchmarks/src/problems/ducted_fan.jl @@ -2,10 +2,10 @@ function ducted_fan(grids) num_states = 7 num_controls = 2 - tspan = (0.,10.) + tspan = (0.0, 10.0) scaled_grids = scale_grids!(tspan, grids) - + @variables begin x₁(..) = 0.0, [tunable = false] v₁(..) = 0.0, [tunable = false] @@ -18,17 +18,17 @@ function ducted_fan(grids) end @parameters begin - tₛ = 0.5, [bounds = (1.e-3, Inf), tunable = true] + tₛ = 0.5, [bounds = (1.0e-3, Inf), tunable = true] end @constants begin - m = 2.2, [tunable = false] - J = 0.05, [tunable = false] - r = 0.2, [tunable = false] - mg = 4., [tunable = false] - μ = 1., [tunable = false] + m = 2.2, [tunable = false] + J = 0.05, [tunable = false] + r = 0.2, [tunable = false] + mg = 4.0, [tunable = false] + μ = 1.0, [tunable = false] end - + eqs = [ D(x₁(t)) ~ tₛ * (v₁(t)) D(v₁(t)) ~ tₛ * (1 / m * (u₁(t) * cos(α(t)) - u₂(t) * sin(α(t)))) @@ -37,24 +37,24 @@ function ducted_fan(grids) D(α(t)) ~ tₛ * (vα(t)) D(vα(t)) ~ tₛ * (r / J * u₁(t)) ] - + # scale the constraint grid - constraint_grid = scaled_grids.constraint_grid[1:end - 1] - - grid_cons_u = [α(tᵢ) ≲ 30. for tᵢ in constraint_grid] - grid_cons_l = [α(tᵢ) ≳ -30. for tᵢ in constraint_grid] - + constraint_grid = scaled_grids.constraint_grid[1:(end - 1)] + + grid_cons_u = [α(tᵢ) ≲ 30.0 for tᵢ in constraint_grid] + grid_cons_l = [α(tᵢ) ≳ -30.0 for tᵢ in constraint_grid] + cons = [ - x₁(last(tspan)) ~ 1., - v₁(last(tspan)) ~ 0., - x₂(last(tspan)) ~ 0., - v₂(last(tspan)) ~ 0., - α(last(tspan)) ~ 0., - vα(last(tspan)) ~ 0., + x₁(last(tspan)) ~ 1.0, + v₁(last(tspan)) ~ 0.0, + x₂(last(tspan)) ~ 0.0, + v₂(last(tspan)) ~ 0.0, + α(last(tspan)) ~ 0.0, + vα(last(tspan)) ~ 0.0, grid_cons_u..., - grid_cons_l... + grid_cons_l..., ] - + costs = [ Symbolics.Integral(t in (0.0, last(tspan)))( (2 * u₁(t)^2 + u₂(t)^2) + μ * tₛ @@ -70,9 +70,9 @@ function ducted_fan(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Ducted Fan" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Ducted Fan", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl b/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl index dc290384..c51a0bd0 100644 --- a/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl +++ b/lib/OptimalControlBenchmarks/src/problems/egerstedt.jl @@ -2,7 +2,7 @@ function egerstedt(grids) num_states = 3 num_controls = 3 - tspan = (0.,1.) + tspan = (0.0, 1.0) scaled_grids = scale_grids!(tspan, grids) @@ -23,11 +23,11 @@ function egerstedt(grids) constraint_grid = scaled_grids.constraint_grid grid_cons_x = [y(tᵢ) ≳ 0.4 for tᵢ in constraint_grid] - grid_cons_u = [u_1(tᵢ) + u_2(tᵢ) + u_3(tᵢ) ~ 1. for tᵢ in constraint_grid] + grid_cons_u = [u_1(tᵢ) + u_2(tᵢ) + u_3(tᵢ) ~ 1.0 for tᵢ in constraint_grid] cons = [ grid_cons_x..., - grid_cons_u... + grid_cons_u..., ] costs = [ @@ -45,9 +45,9 @@ function egerstedt(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Egerstedt standard problem" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Egerstedt standard problem", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/electric_car.jl b/lib/OptimalControlBenchmarks/src/problems/electric_car.jl index 089f5e58..65829e09 100644 --- a/lib/OptimalControlBenchmarks/src/problems/electric_car.jl +++ b/lib/OptimalControlBenchmarks/src/problems/electric_car.jl @@ -2,54 +2,54 @@ function electric_car(grids) num_states = 4 num_controls = 1 - tspan = (0.,10.) + tspan = (0.0, 10.0) scaled_grids = scale_grids!(tspan, grids) - + @variables begin x_1(..) = 0.0, [tunable = false] x_2(..) = 0.0, [tunable = false] x_3(..) = 0.0, [tunable = false] u(..) = 0.0, [bounds = (-5.0, 5.0), input = true] end - + @constants begin - K_r = 10., [tunable = false] - rho = 1.293, [tunable = false] - C_x = 0.4, [tunable = false] - S = 2., [tunable = false] - r = 0.33, [tunable = false] - K_f = 0.03, [tunable = false] - K_m = 0.27, [tunable = false] - R_m = 0.03, [tunable = false] - L_m = 0.05, [tunable = false] - M = 250., [tunable = false] - g = 9.81, [tunable = false] - V_alim = 150., [tunable = false] - R_bat = 0.05, [tunable = false] + K_r = 10.0, [tunable = false] + rho = 1.293, [tunable = false] + C_x = 0.4, [tunable = false] + S = 2.0, [tunable = false] + r = 0.33, [tunable = false] + K_f = 0.03, [tunable = false] + K_m = 0.27, [tunable = false] + R_m = 0.03, [tunable = false] + L_m = 0.05, [tunable = false] + M = 250.0, [tunable = false] + g = 9.81, [tunable = false] + V_alim = 150.0, [tunable = false] + R_bat = 0.05, [tunable = false] end - + temp = K_m * x_1(t) - temp += - r / K_r * (M * g * K_f + 0.5 * rho * S * C_x * r^2 / K_r^2 * x_2(t)^2) + temp += - r / K_r * (M * g * K_f + 0.5 * rho * S * C_x * r^2 / K_r^2 * x_2(t)^2) temp *= K_r^2 / (M * r^2) - + eqs = [ - D(x_1(t)) ~ (V_alim * u(t) - R_m * x_1(t) - K_m * x_2(t)) / L_m + D(x_1(t)) ~ (V_alim * u(t) - R_m * x_1(t) - K_m * x_2(t)) / L_m D(x_2(t)) ~ temp D(x_3(t)) ~ r / K_r * x_2(t) ] - + constraint_grid = scaled_grids.constraint_grid - - grid_cons_u = [x_1(tᵢ) ≲ 150. for tᵢ in constraint_grid] - grid_cons_l = [x_1(tᵢ) ≳ -150. for tᵢ in constraint_grid] - + + grid_cons_u = [x_1(tᵢ) ≲ 150.0 for tᵢ in constraint_grid] + grid_cons_l = [x_1(tᵢ) ≳ -150.0 for tᵢ in constraint_grid] + cons = [ - x_3(last(tspan)) ~ 100., + x_3(last(tspan)) ~ 100.0, grid_cons_u..., - grid_cons_l... + grid_cons_l..., ] - + costs = [ Symbolics.Integral(t in (0.0, last(tspan)))( V_alim * u(t) * x_1(t) + R_bat * x_1(t)^2 @@ -65,9 +65,9 @@ function electric_car(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Electric Car" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Electric Car", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/f8.jl b/lib/OptimalControlBenchmarks/src/problems/f8.jl index a3b78656..38a1471e 100644 --- a/lib/OptimalControlBenchmarks/src/problems/f8.jl +++ b/lib/OptimalControlBenchmarks/src/problems/f8.jl @@ -2,7 +2,7 @@ function f8(grids) num_states = 4 num_controls = 1 - tspan = (0.,10.) + tspan = (0.0, 10.0) scaled_grids = scale_grids!(tspan, grids) @@ -11,11 +11,11 @@ function f8(grids) x_1(..) = 0.0, [tunable = false] x_2(..) = 0.0, [tunable = false] obj(..) = 0.0, [tunable = false] - u(..) = 0.5, [bounds = (0., 1.0), input = true] + u(..) = 0.5, [bounds = (0.0, 1.0), input = true] end @parameters begin - T = 0.5, [bounds =(1.e-3, Inf), tunable = true] + T = 0.5, [bounds = (1.0e-3, Inf), tunable = true] end @constants begin @@ -27,7 +27,7 @@ function f8(grids) du_1 += 0.215 * ξ - 0.28 * x_0(t)^2 * ξ + 0.47 * x_0(t) * ξ^2 - 0.63 * ξ^3 du_1 += - (0.215 * ξ - 0.28 * x_0(t)^2 * ξ - 0.63 * ξ^3) * 2 * u(t) du_3 = -4.208 * x_0(t) - 0.396 * x_2(t) - 0.47 * x_0(t)^2 - 3.564 * x_0(t)^3 - du_3 += 20.967 * ξ - 6.265 * x_0(t)^2 * ξ + 46. * x_0(t) * ξ^2 - 61.4 * ξ^3 + du_3 += 20.967 * ξ - 6.265 * x_0(t)^2 * ξ + 46.0 * x_0(t) * ξ^2 - 61.4 * ξ^3 du_3 += -(20.967 * ξ - 6.265 * x_0(t)^2 * ξ - 61.4 * ξ^3) * 2 * u(t) @@ -39,9 +39,9 @@ function f8(grids) ] cons = [ - x_0(last(tspan)) ~ 0., - x_1(last(tspan)) ~ 0., - x_2(last(tspan)) ~ 0., + x_0(last(tspan)) ~ 0.0, + x_1(last(tspan)) ~ 0.0, + x_2(last(tspan)) ~ 0.0, ] costs = [obj(last(tspan))] @@ -55,9 +55,9 @@ function f8(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "F-8 aircraft" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "F-8 aircraft", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/fuller.jl b/lib/OptimalControlBenchmarks/src/problems/fuller.jl index 7e7b4f24..7b1209af 100644 --- a/lib/OptimalControlBenchmarks/src/problems/fuller.jl +++ b/lib/OptimalControlBenchmarks/src/problems/fuller.jl @@ -2,24 +2,24 @@ function fuller(grids) num_states = 3 num_controls = 1 - tspan = (0.,1.) + tspan = (0.0, 1.0) scaled_grids = scale_grids!(tspan, grids) @variables begin x₀(..) = 0.01, [tunable = false] x₁(..) = 0.0, [tunable = false] - u(..) = 0.5, [bounds = (0., 1.0), input = true] + u(..) = 0.5, [bounds = (0.0, 1.0), input = true] end eqs = [ D(x₀(t)) ~ x₁(t) - D(x₁(t)) ~ 1. - 2. * u(t) + D(x₁(t)) ~ 1.0 - 2.0 * u(t) ] cons = [ x₀(last(tspan)) ~ 0.01, - x₁(last(tspan)) ~ 0., + x₁(last(tspan)) ~ 0.0, ] costs = [ @@ -37,9 +37,9 @@ function fuller(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Fuller's problem" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Fuller's problem", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl b/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl index 2f26e253..05c841de 100644 --- a/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl +++ b/lib/OptimalControlBenchmarks/src/problems/goddarts_rocket.jl @@ -2,51 +2,50 @@ function goddarts_rocket(grids) num_states = 3 num_controls = 1 - tspan = (0.,10.) + tspan = (0.0, 10.0) scaled_grids = scale_grids!(tspan, grids) - + @variables begin - r(..) = 1., [tunable = false] - v(..) = 0., [tunable = false] - m(..) = 1., [tunable = false] - u(..) = 0.5, [bounds = (0., 1.0), input = true] + r(..) = 1.0, [tunable = false] + v(..) = 0.0, [tunable = false] + m(..) = 1.0, [tunable = false] + u(..) = 0.5, [bounds = (0.0, 1.0), input = true] end - + @parameters begin - T = 0.5, [bounds =(1.e-3, Inf), tunable = true] + T = 0.5, [bounds = (1.0e-3, Inf), tunable = true] end - + @constants begin - r_0 = 1., [tunable = false] - v_0 = 0., [tunable = false] - m_0 = 1., [tunable = false] + r_0 = 1.0, [tunable = false] + v_0 = 0.0, [tunable = false] + m_0 = 1.0, [tunable = false] r_T = 1.01, [tunable = false] - b = 7., [tunable = false] + b = 7.0, [tunable = false] T_max = 3.5, [tunable = false] - A = 310., [tunable = false] - k = 500., [tunable = false] + A = 310.0, [tunable = false] + k = 500.0, [tunable = false] C = 0.6, [tunable = false] end - + Drag = A * v(t)^2 * exp(-k * (r(t) - r_0)) - + eqs = [ D(r(t)) ~ T * v(t) D(v(t)) ~ T * (-1 / r(t)^2 + 1 / m(t) * (T_max * u(t) - Drag)) D(m(t)) ~ T * (-b * u(t)) ] - - # scale the constraint grid + constraint_grid = scaled_grids.constraint_grid - - grid_cons_u = [310. * v(tᵢ)^2 * 2.7182818^(-500. * (r(tᵢ) - 1.)) ≲ 0.6 for tᵢ in constraint_grid] - + + grid_cons_u = [310.0 * v(tᵢ)^2 * 2.7182818^(-500.0 * (r(tᵢ) - 1.0)) ≲ 0.6 for tᵢ in constraint_grid] + cons = [ r(last(tspan)) ~ 1.01, - grid_cons_u... + grid_cons_u..., ] - + costs = [-m(last(tspan))] @named oc_problem = System( @@ -58,9 +57,9 @@ function goddarts_rocket(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Goddart's rocket problem" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Goddart's rocket problem", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl b/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl index 12ad823d..25e5a825 100644 --- a/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl +++ b/lib/OptimalControlBenchmarks/src/problems/hang_glider.jl @@ -2,30 +2,30 @@ function hang_glider(grids) num_states = 4 num_controls = 1 - tspan = (0.,100.) + tspan = (0.0, 100.0) scaled_grids = scale_grids!(tspan, grids) @variables begin - x(..) = 0., [tunable = false] - y(..) = 1.e3, [tunable = false] + x(..) = 0.0, [tunable = false] + y(..) = 1.0e3, [tunable = false] v_x(..) = 13.23, [tunable = false] v_y(..) = -1.288, [tunable = false] - c_L(..) = 0.5, [bounds = (0., 1.4), input = true] + c_L(..) = 0.5, [bounds = (0.0, 1.4), input = true] end @parameters begin - T = 0.5, [bounds =(1.e-3, Inf), tunable = true] + T = 0.5, [bounds = (1.0e-3, Inf), tunable = true] end @constants begin u_c = 2.5, [tunable = false] - r_c = 100., [tunable = false] + r_c = 100.0, [tunable = false] c_0 = 0.034, [tunable = false] c_1 = 0.069662, [tunable = false] - S = 14., [tunable = false] + S = 14.0, [tunable = false] rho = 1.13, [tunable = false] - m = 100., [tunable = false] + m = 100.0, [tunable = false] g = 9.81, [tunable = false] end @@ -40,22 +40,22 @@ function hang_glider(grids) eqs = [ D(x(t)) ~ T * v_x(t) D(y(t)) ~ T * v_y(t) - D(v_x(t)) ~ T * (-1.) * (L * w + Dr * v_x(t)) / (m * v) + D(v_x(t)) ~ T * (-1.0) * (L * w + Dr * v_x(t)) / (m * v) D(v_y(t)) ~ T * ((L * v_x(t) - Dr * w) / (m * v) - g) ] # scale the constraint grid - constraint_grid = scaled_grids.constraint_grid[1:end - 1] + constraint_grid = scaled_grids.constraint_grid[1:(end - 1)] - grid_cons_x = [x(tᵢ) ≳ 0. for tᵢ in constraint_grid] - grid_cons_vx = [v_x(tᵢ) ≳ 0. for tᵢ in constraint_grid] + grid_cons_x = [x(tᵢ) ≳ 0.0 for tᵢ in constraint_grid] + grid_cons_vx = [v_x(tᵢ) ≳ 0.0 for tᵢ in constraint_grid] cons = [ - y(last(tspan)) ~ 900., + y(last(tspan)) ~ 900.0, v_x(last(tspan)) ~ 13.23, v_y(last(tspan)) ~ -1.288, grid_cons_x..., - grid_cons_vx... + grid_cons_vx..., ] costs = [-x(last(tspan))] @@ -69,9 +69,9 @@ function hang_glider(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Hang Glider" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Hang Glider", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/hanging_chain.jl b/lib/OptimalControlBenchmarks/src/problems/hanging_chain.jl index 31484b67..c8d8d3f3 100644 --- a/lib/OptimalControlBenchmarks/src/problems/hanging_chain.jl +++ b/lib/OptimalControlBenchmarks/src/problems/hanging_chain.jl @@ -2,21 +2,21 @@ function hanging_chain(grids) num_states = 3 num_controls = 1 - tspan = (0.,1.) + tspan = (0.0, 1.0) scaled_grids = scale_grids!(tspan, grids) @variables begin - x₁(..) = 1., [tunable = false] - x₂(..) = 0., [tunable = false] - x₃(..) = 0., [tunable = false] - u(..) = 0.5, [bounds = (-10., 20.), input = true] + x₁(..) = 1.0, [tunable = false] + x₂(..) = 0.0, [tunable = false] + x₃(..) = 0.0, [tunable = false] + u(..) = 0.5, [bounds = (-10.0, 20.0), input = true] end @constants begin - a = 1., [tunable = false] - b = 3., [tunable = false] - L_p = 4., [tunable = false] + a = 1.0, [tunable = false] + b = 3.0, [tunable = false] + L_p = 4.0, [tunable = false] end eqs = [ @@ -28,18 +28,18 @@ function hanging_chain(grids) # scale the constraint grid constraint_grid = scaled_grids.constraint_grid - grid_cons_u1 = [x₁(tᵢ) ≳ 0. for tᵢ in constraint_grid[1:end - 1]] - grid_cons_u2 = [x₂(tᵢ) ≳ 0. for tᵢ in constraint_grid] - grid_cons_u3 = [x₃(tᵢ) ≳ 0. for tᵢ in constraint_grid[1:end - 1]] - grid_cons_l1 = [x₁(tᵢ) ≲ 10. for tᵢ in constraint_grid[1:end - 1]] - grid_cons_l2 = [x₂(tᵢ) ≲ 10. for tᵢ in constraint_grid] - grid_cons_l3 = [x₃(tᵢ) ≲ 10. for tᵢ in constraint_grid[1:end - 1]] + grid_cons_u1 = [x₁(tᵢ) ≳ 0.0 for tᵢ in constraint_grid[1:(end - 1)]] + grid_cons_u2 = [x₂(tᵢ) ≳ 0.0 for tᵢ in constraint_grid] + grid_cons_u3 = [x₃(tᵢ) ≳ 0.0 for tᵢ in constraint_grid[1:(end - 1)]] + grid_cons_l1 = [x₁(tᵢ) ≲ 10.0 for tᵢ in constraint_grid[1:(end - 1)]] + grid_cons_l2 = [x₂(tᵢ) ≲ 10.0 for tᵢ in constraint_grid] + grid_cons_l3 = [x₃(tᵢ) ≲ 10.0 for tᵢ in constraint_grid[1:(end - 1)]] cons = [ - x₁(last(tspan)) - 3. ~ 0., - x₃(last(tspan)) - 4. ~ 0., + x₁(last(tspan)) - 3.0 ~ 0.0, + x₃(last(tspan)) - 4.0 ~ 0.0, grid_cons_u1..., grid_cons_u2..., grid_cons_u3..., - grid_cons_l1..., grid_cons_l2..., grid_cons_l3... + grid_cons_l1..., grid_cons_l2..., grid_cons_l3..., ] costs = [x₂(last(tspan))] @@ -53,8 +53,8 @@ function hanging_chain(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls) + grids = scaled_grids, + dims = (num_states, num_controls), ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/jackson.jl b/lib/OptimalControlBenchmarks/src/problems/jackson.jl index 555bb57d..50c60660 100644 --- a/lib/OptimalControlBenchmarks/src/problems/jackson.jl +++ b/lib/OptimalControlBenchmarks/src/problems/jackson.jl @@ -2,27 +2,27 @@ function jackson(grids) num_states = 3 num_controls = 1 - tspan = (0.,1.) + tspan = (0.0, 1.0) scaled_grids = scale_grids!(tspan, grids) @variables begin - x₁(..) = 1., [tunable = false] - x₂(..) = 0., [tunable = false] - x₃(..) = 0., [tunable = false] - u(..) = 0.5, [bounds = (0., 1.), input = true] + x₁(..) = 1.0, [tunable = false] + x₂(..) = 0.0, [tunable = false] + x₃(..) = 0.0, [tunable = false] + u(..) = 0.5, [bounds = (0.0, 1.0), input = true] end @constants begin - k₁ = 1., [tunable = false] - k₂ = 10., [tunable = false] - k₃ = 1., [tunable = false] + k₁ = 1.0, [tunable = false] + k₂ = 10.0, [tunable = false] + k₃ = 1.0, [tunable = false] end eqs = [ - D(x₁(t)) ~ -u(t) * (k₁ * x₁(t) -k₂ * x₂(t)) + D(x₁(t)) ~ -u(t) * (k₁ * x₁(t) - k₂ * x₂(t)) D(x₂(t)) ~ u(t) * (k₁ * x₁(t) - k₂ * x₂(t)) - (1 - u(t)) * k₃ * x₂(t) - D(x₃(t)) ~ (1- u(t)) * k₃ * x₂(t) + D(x₃(t)) ~ (1 - u(t)) * k₃ * x₂(t) ] # Define control discretization @@ -31,13 +31,13 @@ function jackson(grids) grid_cons_u1 = [x₁(tᵢ) ≲ 1.1 for tᵢ in constraint_grid] grid_cons_u2 = [x₂(tᵢ) ≲ 1.1 for tᵢ in constraint_grid] grid_cons_u3 = [x₃(tᵢ) ≲ 1.1 for tᵢ in constraint_grid] - grid_cons_l1 = [x₁(tᵢ) ≳ 0. for tᵢ in constraint_grid] - grid_cons_l2 = [x₂(tᵢ) ≳ 0. for tᵢ in constraint_grid] - grid_cons_l3 = [x₃(tᵢ) ≳ 0. for tᵢ in constraint_grid] + grid_cons_l1 = [x₁(tᵢ) ≳ 0.0 for tᵢ in constraint_grid] + grid_cons_l2 = [x₂(tᵢ) ≳ 0.0 for tᵢ in constraint_grid] + grid_cons_l3 = [x₃(tᵢ) ≳ 0.0 for tᵢ in constraint_grid] cons = [ grid_cons_u1..., grid_cons_u2..., grid_cons_u3..., - grid_cons_l1..., grid_cons_l2..., grid_cons_l3... + grid_cons_l1..., grid_cons_l2..., grid_cons_l3..., ] costs = [x₃(last(tspan))] @@ -51,9 +51,9 @@ function jackson(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Jackson" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Jackson", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl b/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl index 06caa53c..708a963d 100644 --- a/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl +++ b/lib/OptimalControlBenchmarks/src/problems/lotka_competitive.jl @@ -2,31 +2,31 @@ function lotka_competitive(grids) num_states = 3 num_controls = 1 - tspan = (0.,40.) + tspan = (0.0, 40.0) scaled_grids = scale_grids!(tspan, grids) - + @variables begin x₀(..) = 0.5, [tunable = false] x₁(..) = 1.5, [tunable = false] - u(..) = 0.5, [bounds = (0., 1.), input = true] + u(..) = 0.5, [bounds = (0.0, 1.0), input = true] end - + @constants begin c₁ = 0.1, [tunable = false] c₂ = 0.4, [tunable = false] α = 1.2, [tunable = false] K = 1.8, [tunable = false] end - + eqs = [ D(x₀(t)) ~ x₀(t) * (1 - (x₀(t) + α * x₁(t)) / K) - c₁ * x₀(t) * u(t) D(x₁(t)) ~ x₁(t) * (1 - (x₀(t) + x₁(t)) / K) - c₂ * x₁(t) * u(t) ] - + costs = [ Symbolics.Integral(t in (0.0, last(tspan)))( - (x₀(t) - 1.)^2 + (x₁(t) - 1.)^2 + (x₀(t) - 1.0)^2 + (x₁(t) - 1.0)^2 ), ] @@ -38,9 +38,9 @@ function lotka_competitive(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Lotka Volterra Competitive" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Lotka Volterra Competitive", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl b/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl index 7e9b5d5b..9ec49a87 100644 --- a/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl +++ b/lib/OptimalControlBenchmarks/src/problems/lotka_shared.jl @@ -2,15 +2,15 @@ function lotka_shared(grids) num_states = 4 num_controls = 1 - tspan = (0.,40.) + tspan = (0.0, 40.0) scaled_grids = scale_grids!(tspan, grids) @variables begin x₀(..) = 1.5, [tunable = false] - x₁(..) = 1., [tunable = false] + x₁(..) = 1.0, [tunable = false] x₂(..) = 0.5, [tunable = false] - u(..) = 0.5, [bounds = (0., 1.), input = true] + u(..) = 0.5, [bounds = (0.0, 1.0), input = true] end @constants begin @@ -27,7 +27,7 @@ function lotka_shared(grids) costs = [ Symbolics.Integral(t in (0.0, last(tspan)))( - (x₀(t) - 1.5)^2 + (x₁(t) - 1.)^2 + (x₂(t) - 1.)^2 + (x₀(t) - 1.5)^2 + (x₁(t) - 1.0)^2 + (x₂(t) - 1.0)^2 ), ] @@ -39,9 +39,9 @@ function lotka_shared(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Lotka Volterra Shared" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Lotka Volterra Shared", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl b/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl index 663ab46e..5a22beda 100644 --- a/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl +++ b/lib/OptimalControlBenchmarks/src/problems/lotka_volterra.jl @@ -2,14 +2,14 @@ function lotka_volterra(grids) num_states = 3 num_controls = 1 - tspan = (0.,12.) + tspan = (0.0, 12.0) scaled_grids = scale_grids!(tspan, grids) @variables begin x₀(..) = 0.5, [tunable = false] x₁(..) = 0.7, [tunable = false] - u(..) = 0.5, [bounds = (0., 1.), input = true] + u(..) = 0.5, [bounds = (0.0, 1.0), input = true] end @constants begin @@ -24,7 +24,7 @@ function lotka_volterra(grids) costs = [ Symbolics.Integral(t in (0.0, last(tspan)))( - (x₀(t) - 1.)^2 + (x₁(t) - 1.)^2 + (x₀(t) - 1.0)^2 + (x₁(t) - 1.0)^2 ), ] @@ -36,9 +36,9 @@ function lotka_volterra(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Lotka Volterra fishing problem" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Lotka Volterra fishing problem", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/lqr.jl b/lib/OptimalControlBenchmarks/src/problems/lqr.jl index 663cc7cc..a32ae9d1 100644 --- a/lib/OptimalControlBenchmarks/src/problems/lqr.jl +++ b/lib/OptimalControlBenchmarks/src/problems/lqr.jl @@ -2,27 +2,27 @@ function lqr(grids) num_states = 2 num_controls = 1 - tspan = (0.,10.) + tspan = (0.0, 10.0) scaled_grids = scale_grids!(tspan, grids) - + @variables begin - x(..) = 1., [tunable = false] + x(..) = 1.0, [tunable = false] u(..) = 0.5, [input = true] end - + @constants begin - a = -1., [tunable = false] - b = 1., [tunable = false] + a = -1.0, [tunable = false] + b = 1.0, [tunable = false] end - + eqs = [ - D(x(t)) ~ a * x(t) + b * u(t) + D(x(t)) ~ a * x(t) + b * u(t), ] - + costs = [ Symbolics.Integral(t in (0.0, last(tspan)))( - 10. * (x(t) - 3.)^2 + 0.1 * u(t)^2 + 10.0 * (x(t) - 3.0)^2 + 0.1 * u(t)^2 ), ] @@ -34,9 +34,9 @@ function lqr(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Linear Quadratic Regulator" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Linear Quadratic Regulator", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl b/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl index 2542ebe8..cbc2f80c 100644 --- a/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl +++ b/lib/OptimalControlBenchmarks/src/problems/moon_landing.jl @@ -2,21 +2,21 @@ function moon_landing(grids) num_states = 3 num_controls = 1 - tspan = (0.,1.) + tspan = (0.0, 1.0) scaled_grids = scale_grids!(tspan, grids) - + @variables begin - h(..) = 1., [tunable = false] + h(..) = 1.0, [tunable = false] v(..) = -0.783, [tunable = false] - m(..) = 1., [tunable = false] - T(..) = 0.5, [bounds = (0., 1.227), input = true] + m(..) = 1.0, [tunable = false] + T(..) = 0.5, [bounds = (0.0, 1.227), input = true] end - + @parameters begin - tₛ = 1., [bounds = (1.e-3, Inf), tunable = true] + tₛ = 1.0, [bounds = (1.0e-3, Inf), tunable = true] end - + eqs = [ D(h(t)) ~ tₛ * v(t) D(v(t)) ~ tₛ * (-1 + T(t) / m(t)) @@ -24,8 +24,8 @@ function moon_landing(grids) ] cons = [ - h(last(tspan)) ~ 0., - v(last(tspan)) ~ 0. + h(last(tspan)) ~ 0.0, + v(last(tspan)) ~ 0.0, ] costs = [-m(last(tspan))] @@ -39,9 +39,9 @@ function moon_landing(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Moon Landing" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Moon Landing", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl b/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl index 704c69c5..cff90166 100644 --- a/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl +++ b/lib/OptimalControlBenchmarks/src/problems/mountain_car.jl @@ -2,32 +2,32 @@ function mountain_car(grids) num_states = 3 num_controls = 1 - tspan = (0.,100.) + tspan = (0.0, 100.0) scaled_grids = scale_grids!(tspan, grids) - + @variables begin x(..) = -0.5, [tunable = false] - v(..) = 0., [tunable = false] - obj(..) = 0., [tunable = false] - u(..) = 0.5, [bounds = (-1., 1.), input = true] + v(..) = 0.0, [tunable = false] + obj(..) = 0.0, [tunable = false] + u(..) = 0.5, [bounds = (-1.0, 1.0), input = true] end - + @parameters begin - tₛ = 1., [bounds = (1.e-3, Inf), tunable = true] + tₛ = 1.0, [bounds = (1.0e-3, Inf), tunable = true] end - + eqs = [ D(x(t)) ~ tₛ * v(t) - D(v(t)) ~ tₛ * (1.e-3 * u(t) - 2.5e-3 * cos(3 * x(t))) + D(v(t)) ~ tₛ * (1.0e-3 * u(t) - 2.5e-3 * cos(3 * x(t))) D(obj(t)) ~ tₛ ] - + cons = [ x(last(tspan)) ~ 0.5, - v(last(tspan)) ≳ 0. + v(last(tspan)) ≳ 0.0, ] - + costs = [obj(last(tspan))] @named oc_problem = System( @@ -39,9 +39,9 @@ function mountain_car(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Mountain Car" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Mountain Car", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/ocean.jl b/lib/OptimalControlBenchmarks/src/problems/ocean.jl index dc31b240..53be0771 100644 --- a/lib/OptimalControlBenchmarks/src/problems/ocean.jl +++ b/lib/OptimalControlBenchmarks/src/problems/ocean.jl @@ -2,31 +2,31 @@ function ocean(grids) num_states = 3 num_controls = 2 - tspan = (0.,400.) + tspan = (0.0, 400.0) scaled_grids = scale_grids!(tspan, grids) @variables begin - S(..) = 2.e3, [tunable = false] - R(..) = 1.e4, [tunable = false] - u₁(..) = 7., [bounds = (0., 40.), input = true] - u₂(..) = 7., [bounds = (0., 40.), input = true] + S(..) = 2.0e3, [tunable = false] + R(..) = 1.0e4, [tunable = false] + u₁(..) = 7.0, [bounds = (0.0, 40.0), input = true] + u₂(..) = 7.0, [bounds = (0.0, 40.0), input = true] end @constants begin - ρ = 3.e-2, [tunable = false] - γ = 1.e-3, [tunable = false] + ρ = 3.0e-2, [tunable = false] + γ = 1.0e-3, [tunable = false] ω = 0.1, [tunable = false] - b = 50., [tunable = false] + b = 50.0, [tunable = false] μ = 0.5, [tunable = false] - a₁ = 2., [tunable = false] - a₂ = 2., [tunable = false] - ν = 1., [tunable = false] - c₁ = 50., [tunable = false] - c₂ = 4.e-3, [tunable = false] - Sₚ = 6.e2, [tunable = false] - S₀ = 2.e3, [tunable = false] - R₀ = 1.e4, [tunable = false] + a₁ = 2.0, [tunable = false] + a₂ = 2.0, [tunable = false] + ν = 1.0, [tunable = false] + c₁ = 50.0, [tunable = false] + c₂ = 4.0e-3, [tunable = false] + Sₚ = 6.0e2, [tunable = false] + S₀ = 2.0e3, [tunable = false] + R₀ = 1.0e4, [tunable = false] Dₗ₀ = 2.3e4, [tunable = false] end @@ -45,14 +45,14 @@ function ocean(grids) # Define control discretization constraint_grid = scaled_grids.constraint_grid - grid_cons_Su = [S(tᵢ) ≲ 1.e5 for tᵢ in constraint_grid] - grid_cons_Ru = [R(tᵢ) ≲ 1.e5 for tᵢ in constraint_grid] - grid_cons_Sl = [S(tᵢ) ≳ 0. for tᵢ in constraint_grid] - grid_cons_Rl = [R(tᵢ) ≳ 0. for tᵢ in constraint_grid] + grid_cons_Su = [S(tᵢ) ≲ 1.0e5 for tᵢ in constraint_grid] + grid_cons_Ru = [R(tᵢ) ≲ 1.0e5 for tᵢ in constraint_grid] + grid_cons_Sl = [S(tᵢ) ≳ 0.0 for tᵢ in constraint_grid] + grid_cons_Rl = [R(tᵢ) ≳ 0.0 for tᵢ in constraint_grid] cons = [ grid_cons_Su..., grid_cons_Ru..., - grid_cons_Sl..., grid_cons_Rl... + grid_cons_Sl..., grid_cons_Rl..., ] costs = [ @@ -70,9 +70,9 @@ function ocean(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Ocean" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Ocean", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl b/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl index cd6386b1..9eeab754 100644 --- a/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl +++ b/lib/OptimalControlBenchmarks/src/problems/particle_steering.jl @@ -2,25 +2,25 @@ function particle_steering(grids) num_states = 5 num_controls = 1 - tspan = (0.,10.) + tspan = (0.0, 10.0) scaled_grids = scale_grids!(tspan, grids) @variables begin - x₁(..) = 0., [tunable = false] - x₂(..) = 0., [tunable = false] - dx₁(..) = 0., [tunable = false] - dx₂(..) = 0., [tunable = false] - obj(..) = 0., [tunable = false] - u(..) = 0., [bounds = (-pi / 2., pi / 2.), input = true] + x₁(..) = 0.0, [tunable = false] + x₂(..) = 0.0, [tunable = false] + dx₁(..) = 0.0, [tunable = false] + dx₂(..) = 0.0, [tunable = false] + obj(..) = 0.0, [tunable = false] + u(..) = 0.0, [bounds = (-pi / 2.0, pi / 2.0), input = true] end @parameters begin - tₛ = 3.e-2, [bounds = (1.e-3, Inf), tunable = true] + tₛ = 3.0e-2, [bounds = (1.0e-3, Inf), tunable = true] end @constants begin - a = 100., [tunable = false] + a = 100.0, [tunable = false] end eqs = [ @@ -32,13 +32,13 @@ function particle_steering(grids) ] cons = [ - x₂(last(tspan)) ~ 5., - dx₁(last(tspan)) ~ 45., - dx₂(last(tspan)) ~ 0. + x₂(last(tspan)) ~ 5.0, + dx₁(last(tspan)) ~ 45.0, + dx₂(last(tspan)) ~ 0.0, ] costs = [ - obj(last(tspan)) + obj(last(tspan)), ] @named oc_problem = System( @@ -50,9 +50,9 @@ function particle_steering(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Particle steering problem" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Particle steering problem", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl b/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl index cba206e5..8b62ba59 100644 --- a/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl +++ b/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl @@ -2,30 +2,30 @@ function quadrotor(grids) num_states = 7 num_controls = 4 - tspan = (0.,7.5) + tspan = (0.0, 7.5) scaled_grids = scale_grids!(tspan, grids) - + @variables begin - x₁(..) = 0., [tunable = false] - x₂(..) = 0., [tunable = false] - x₃(..) = 1., [tunable = false] - x₄(..) = 0., [tunable = false] - x₅(..) = 0., [tunable = false] - x₆(..) = 0., [tunable = false] - w₁(..) = 1. / 3., [bounds = (0., 1.), input = true] - w₂(..) = 1. / 3., [bounds = (0., 1.), input = true] - w₃(..) = 1. / 3., [bounds = (0., 1.), input = true] - u(..) = 0., [bounds = (0., 1.e-3), input = true] + x₁(..) = 0.0, [tunable = false] + x₂(..) = 0.0, [tunable = false] + x₃(..) = 1.0, [tunable = false] + x₄(..) = 0.0, [tunable = false] + x₅(..) = 0.0, [tunable = false] + x₆(..) = 0.0, [tunable = false] + w₁(..) = 1.0 / 3.0, [bounds = (0.0, 1.0), input = true] + w₂(..) = 1.0 / 3.0, [bounds = (0.0, 1.0), input = true] + w₃(..) = 1.0 / 3.0, [bounds = (0.0, 1.0), input = true] + u(..) = 0.0, [bounds = (0.0, 1.0e-3), input = true] end - + @constants begin g = 9.81, [tunable = false] M = 1.3, [tunable = false] L = 0.305, [tunable = false] - I = 0.0605, [tunable = false] + I = 0.0605, [tunable = false] end - + eqs = [ D(x₁(t)) ~ x₂(t) D(x₂(t)) ~ g * sin(x₅(t)) + w₁(t) * u(t) * sin(x₅(t)) / M @@ -34,16 +34,16 @@ function quadrotor(grids) D(x₅(t)) ~ x₆(t) D(x₆(t)) ~ -w₂(t) * L * u(t) / I + w₃(t) * L * u(t) / I ] - - constraint_grid = scaled_grids.constraint_grid - grid_cons_w = [w₁(tᵢ) + w₂(tᵢ) + w₃(tᵢ) ~ 1. for tᵢ in constraint_grid] - grid_cons_x₃ = [x₃(tᵢ) ≳ 0. for tᵢ in constraint_grid] - + + constraint_grid = scaled_grids.constraint_grid + grid_cons_w = [w₁(tᵢ) + w₂(tᵢ) + w₃(tᵢ) ~ 1.0 for tᵢ in constraint_grid] + grid_cons_x₃ = [x₃(tᵢ) ≳ 0.0 for tᵢ in constraint_grid] + cons = [ grid_cons_w..., - grid_cons_x₃... + grid_cons_x₃..., ] - + costs = [ 5 * (x₁(t_f) - 6)^2 + 5 * (x₃(t_f) - 1)^2 + 5 * (sin(x₅(t_f) * 0.5))^2 + Symbolics.Integral(t in (0.0, last(tspan)))( 5 * u(t)^2 @@ -59,9 +59,9 @@ function quadrotor(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Quadrotor helicopter control problem" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Quadrotor helicopter control problem", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl b/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl index 4c1e9ee6..46d3d46a 100644 --- a/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl +++ b/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl @@ -2,21 +2,21 @@ function rao_mease(grids) num_states = 2 num_controls = 1 - tspan = (0.,10) + tspan = (0.0, 10) scaled_grids = scale_grids!(tspan, grids) @variables begin - x(..) = 1., [tunable = false] - u(..) = 0., [input = true] + x(..) = 1.0, [tunable = false] + u(..) = 0.0, [input = true] end eqs = [ - D(x(t)) ~ -x(t)^3 + u(t) + D(x(t)) ~ -x(t)^3 + u(t), ] cons = [ - x(last(tspan)) ~ 1.5 + x(last(tspan)) ~ 1.5, ] costs = [ @@ -34,9 +34,9 @@ function rao_mease(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Rao Mease" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Rao Mease", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/robbins.jl b/lib/OptimalControlBenchmarks/src/problems/robbins.jl index 86e8ea63..34b8fe57 100644 --- a/lib/OptimalControlBenchmarks/src/problems/robbins.jl +++ b/lib/OptimalControlBenchmarks/src/problems/robbins.jl @@ -2,20 +2,20 @@ function robbins(grids) num_states = 4 num_controls = 1 - tspan = (0.,10.) + tspan = (0.0, 10.0) scaled_grids = scale_grids!(tspan, grids) @variables begin - x₁(..) = 1., [tunable = false] - x₂(..) = -2., [tunable = false] - x₃(..) = 0., [tunable = false] - u(..) = 0., [input = true] + x₁(..) = 1.0, [tunable = false] + x₂(..) = -2.0, [tunable = false] + x₃(..) = 0.0, [tunable = false] + u(..) = 0.0, [input = true] end @constants begin - α = 3., [tunable = false] - β = 0., [tunable = false] + α = 3.0, [tunable = false] + β = 0.0, [tunable = false] γ = 0.5, [tunable = false] end @@ -26,15 +26,15 @@ function robbins(grids) ] # scale the constraint grid - constraint_grid = scaled_grids.constraint_grid[1:end - 1] + constraint_grid = scaled_grids.constraint_grid[1:(end - 1)] - grid_cons_x₁ = [x₃(tᵢ) ≳ 0. for tᵢ in constraint_grid] + grid_cons_x₁ = [x₃(tᵢ) ≳ 0.0 for tᵢ in constraint_grid] cons = [ - x₁(last(tspan)) ~ 0., - x₂(last(tspan)) ~ 0., - x₃(last(tspan)) ~ 0., - grid_cons_x₁... + x₁(last(tspan)) ~ 0.0, + x₂(last(tspan)) ~ 0.0, + x₃(last(tspan)) ~ 0.0, + grid_cons_x₁..., ] costs = [ @@ -52,9 +52,9 @@ function robbins(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Robbins" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Robbins", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/three_tank.jl b/lib/OptimalControlBenchmarks/src/problems/three_tank.jl index dcfe9c2a..7527f0aa 100644 --- a/lib/OptimalControlBenchmarks/src/problems/three_tank.jl +++ b/lib/OptimalControlBenchmarks/src/problems/three_tank.jl @@ -2,43 +2,43 @@ function three_tank(grids) num_states = 4 num_controls = 3 - tspan = (0.,12.) + tspan = (0.0, 12.0) scaled_grids = scale_grids!(tspan, grids) - + @variables begin - x₁(..) = 2., [tunable = false] - x₂(..) = 2., [tunable = false] - x₃(..) = 2., [tunable = false] - w₁(..) = 1. / 3., [bounds = (0., 1.), input = true] - w₂(..) = 1. / 3., [bounds = (0., 1.), input = true] - w₃(..) = 1. / 3., [bounds = (0., 1.), input = true] + x₁(..) = 2.0, [tunable = false] + x₂(..) = 2.0, [tunable = false] + x₃(..) = 2.0, [tunable = false] + w₁(..) = 1.0 / 3.0, [bounds = (0.0, 1.0), input = true] + w₂(..) = 1.0 / 3.0, [bounds = (0.0, 1.0), input = true] + w₃(..) = 1.0 / 3.0, [bounds = (0.0, 1.0), input = true] end - + @constants begin - c₁ = 1., [tunable = false] - c₂ = 2., [tunable = false] + c₁ = 1.0, [tunable = false] + c₂ = 2.0, [tunable = false] c₃ = 0.8, [tunable = false] - k₁ = 2., [tunable = false] - k₂ = 3., [tunable = false] - k₃ = 1., [tunable = false] - k₄ = 3., [tunable = false] + k₁ = 2.0, [tunable = false] + k₂ = 3.0, [tunable = false] + k₃ = 1.0, [tunable = false] + k₄ = 3.0, [tunable = false] end - + eqs = [ D(x₁(t)) ~ -sqrt(x₂(t)) + c₁ * w₁(t) + c₂ * w₂(t) - w₃(t) * sqrt(c₃ * x₁(t)) D(x₂(t)) ~ sqrt(x₁(t)) - sqrt(x₂(t)) D(x₃(t)) ~ sqrt(x₂(t)) - sqrt(x₃(t)) + w₃(t) * sqrt(c₃ * x₁(t)) ] - + constraint_grid = scaled_grids.constraint_grid - grid_cons_w = [w₁(tᵢ) + w₂(tᵢ) + w₃(tᵢ) ~ 1. for tᵢ in constraint_grid] - + grid_cons_w = [w₁(tᵢ) + w₂(tᵢ) + w₃(tᵢ) ~ 1.0 for tᵢ in constraint_grid] + cons = [ - grid_cons_w... + grid_cons_w..., ] - + costs = [ Symbolics.Integral(t in (0.0, last(tspan)))( k₁ * (x₂(t) - k₂)^2 + k₃ * (x₃(t) - k₄)^2 @@ -54,9 +54,9 @@ function three_tank(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Three Tank multimode problem" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Three Tank multimode problem", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl b/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl index 9f780ec2..9f38ccae 100644 --- a/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl +++ b/lib/OptimalControlBenchmarks/src/problems/tubular_reactor.jl @@ -2,16 +2,16 @@ function tubular_reactor(grids) num_states = 2 num_controls = 1 - tspan = (0.,1.) + tspan = (0.0, 1.0) scaled_grids = scale_grids!(tspan, grids) - + @variables begin - x₁(..) = 1., [tunable = false] - x₂(..) = 0., [tunable = false] - u(..) = 0., [bounds = (0., 5.), input = true] + x₁(..) = 1.0, [tunable = false] + x₂(..) = 0.0, [tunable = false] + u(..) = 0.0, [bounds = (0.0, 5.0), input = true] end - + eqs = [ D(x₁(t)) ~ -(u(t) + 0.5 * u(t)^2) * x₁(t) D(x₂(t)) ~ u(t) * x₁(t) @@ -27,9 +27,9 @@ function tubular_reactor(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Tubular Reactor" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Tubular Reactor", ) end diff --git a/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl b/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl index de7e40c9..62d1a298 100644 --- a/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl +++ b/lib/OptimalControlBenchmarks/src/problems/van_der_pol.jl @@ -2,29 +2,29 @@ function van_der_pol(grids) num_states = 3 num_controls = 1 - tspan = (0.,10.) + tspan = (0.0, 10.0) scaled_grids = scale_grids!(tspan, grids) - + @variables begin - x₁(..) = 0., [tunable = false] - x₂(..) = 1., [tunable = false] - u(..) = 0., [bounds = (-1., 1.), input = true] + x₁(..) = 0.0, [tunable = false] + x₂(..) = 1.0, [tunable = false] + u(..) = 0.0, [bounds = (-1.0, 1.0), input = true] end - + eqs = [ D(x₁(t)) ~ (1 - x₂(t)^2) * x₁(t) - x₂(t) + u(t) D(x₂(t)) ~ x₁(t) ] - + constraint_grid = scaled_grids.constraint_grid - + grid_cons_x₁ = [x₁(tᵢ) ≳ -0.25 for tᵢ in constraint_grid] - + cons = [ - grid_cons_x₁... + grid_cons_x₁..., ] - + costs = [ Symbolics.Integral(t in (0.0, last(tspan)))( x₁(t)^2 + x₂(t)^2 + u(t)^2 @@ -40,9 +40,9 @@ function van_der_pol(grids) return ( system = oc_problem, - grids = scaled_grids, - dims = (num_states, num_controls), - name = "Van der Pol Oscillator" + grids = scaled_grids, + dims = (num_states, num_controls), + name = "Van der Pol Oscillator", ) end diff --git a/lib/OptimalControlBenchmarks/src/run_benchmarks.jl b/lib/OptimalControlBenchmarks/src/run_benchmarks.jl index a10e9aa9..d6dc1ce1 100644 --- a/lib/OptimalControlBenchmarks/src/run_benchmarks.jl +++ b/lib/OptimalControlBenchmarks/src/run_benchmarks.jl @@ -6,20 +6,22 @@ function run_all(benchmarks, optimizer, grids) results = [] for prob in benchmarks - name = nameof(prob) - println("Running ", name) + name = nameof(prob) + println("Running ", name) sol = solve_with_corleone( prob, optimizer, - grids + grids ) println(sol) t = 0 # @belapsed solve_with_corleone($prob) - push!(results, ( - name = name, - time = t - )) + push!( + results, ( + name = name, + time = t, + ) + ) end diff --git a/lib/OptimalControlBenchmarks/src/solver_corleone.jl b/lib/OptimalControlBenchmarks/src/solver_corleone.jl index 248d284e..e67b5986 100644 --- a/lib/OptimalControlBenchmarks/src/solver_corleone.jl +++ b/lib/OptimalControlBenchmarks/src/solver_corleone.jl @@ -1,4 +1,4 @@ -function solve_with_corleone(benchmark, optimizer, grids, plot_res=true) +function solve_with_corleone(benchmark, optimizer, grids, plot_res = true) data = benchmark(grids) @@ -14,7 +14,7 @@ function solve_with_corleone(benchmark, optimizer, grids, plot_res=true) control_map = [ c => control_grid for c in controls ] - + dynopt = CorleoneDynamicOptProblem( oc_problem, [], @@ -33,20 +33,20 @@ function solve_with_corleone(benchmark, optimizer, grids, plot_res=true) optprob, optimizer, max_iter = 100, - tol = 5e-6, + tol = 5.0e-6, hessian_approximation = "limited-memory" ) if plot_res - # plotting - u_opt = ComponentVector(sol.u, optprob.f.f.ax) - opt_traj, _ = dynopt.layer( - nothing, u_opt, - LuxCore.initialstates(Random.default_rng(), dynopt.layer) - ) - - f = plot_oc_problem(opt_traj, num_states, num_controls) - display(f) + # plotting + u_opt = ComponentVector(sol.u, optprob.f.f.ax) + opt_traj, _ = dynopt.layer( + nothing, u_opt, + LuxCore.initialstates(Random.default_rng(), dynopt.layer) + ) + + f = plot_oc_problem(opt_traj, num_states, num_controls) + display(f) end return sol diff --git a/lib/OptimalControlBenchmarks/test/runtests.jl b/lib/OptimalControlBenchmarks/test/runtests.jl index 9c4307f6..d3ee58d7 100644 --- a/lib/OptimalControlBenchmarks/test/runtests.jl +++ b/lib/OptimalControlBenchmarks/test/runtests.jl @@ -1,4 +1,4 @@ using OptimalControlBenchmarks using Test -@test 1==1 \ No newline at end of file +@test 1 == 1 From d2836754ed6fe2adc6c1b98352bd11923356a8c6 Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Tue, 17 Mar 2026 15:02:09 +0100 Subject: [PATCH 08/73] update fim --- lib/CorleoneOED/src/augmentation.jl | 1 - lib/CorleoneOED/src/oed.jl | 29 ++++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/CorleoneOED/src/augmentation.jl b/lib/CorleoneOED/src/augmentation.jl index b04cd054..17f329b6 100644 --- a/lib/CorleoneOED/src/augmentation.jl +++ b/lib/CorleoneOED/src/augmentation.jl @@ -82,7 +82,6 @@ function compute_initial_F(prob::Union{ODEProblem, DAEProblem}, alg, config, par return F_tf_sens end - function compute_svd_of_F(prob, alg, config, params; ns = nothing, threshold_singular_values = 0.95, threshold_singular_vectors = 0.1, kwargs...) F = compute_initial_F(prob, alg, config, params) svdF = svd(F) diff --git a/lib/CorleoneOED/src/oed.jl b/lib/CorleoneOED/src/oed.jl index 20470aac..a59a90a1 100644 --- a/lib/CorleoneOED/src/oed.jl +++ b/lib/CorleoneOED/src/oed.jl @@ -64,7 +64,6 @@ function OEDLayer{DISCRETE}(prob::DEProblem, alg::DEAlgorithm, shooting_points.. return OEDLayer{DISCRETE}(layer; params = params, measurements = measurements, observed = observed, kwargs...) end - function OEDLayer{DISCRETE}(layer::MultipleShootingLayer, args...; measurements = [], kwargs...) where {DISCRETE} (; problem, algorithm, controls, control_indices, tunable_ic, bounds_ic, state_initialization, bounds_p, parameter_initialization, quadrature_indices) = layer.layer @@ -142,6 +141,34 @@ function OEDLayer{DISCRETE}(layer::SingleShootingLayer, args...; measurements = return OEDLayer{DISCRETE, SAMPLED, FIXED, typeof(newlayer), typeof(observed)}(newlayer, observed, samplings) end +function get_idxs_fisher(oed::OEDLayer, symcache::SymbolicIndexingInterface.SymbolCache) + ps, st = LuxCore.setup(Random.default_rng(), oed) + fim = first(fisher_information(oed, nothing, ps, st)) + F_states = Symbol.(Symbolics.variables(:F, 1:size(fim,1), 1:size(fim, 2))[triu(ones(Bool, size(fim)))]) + + idxs = map(F_states) do F + for (k,v) in symcache.variables + if isequal(k,F) + return v + end + end + end + + return idxs +end + +function update_fim!(oed::OEDLayer{DISCRETE, SAMPLED, FIXED}, experiments) where {DISCRETE, SAMPLED, FIXED} + ps, st = LuxCore.setup(Random.default_rng(), oed) + FIM = sum(map(experiments) do experiment + fisher_information(oed, nothing, experiment, st)[1] + end) + u0 = oed.layer.problem.u0 + idxs_fim = get_idxs_fisher(oed, st.symcache) + u0[idxs_fim] .= FIM[triu(ones(Bool, size(FIM)))] + remake(oed.layer.problem, u0 = u0) + return nothing +end + n_observed(layer::OEDLayer) = length(layer.sampling_indices) Corleone.get_number_of_shooting_constraints(oed::OEDLayer{<:Any, <:Any, <:Any, <:MultipleShootingLayer}) = Corleone.get_number_of_shooting_constraints(oed.layer) Corleone.get_number_of_shooting_constraints(oed::OEDLayer{<:Any, <:Any, <:Any, <:SingleShootingLayer}) = 0 From f19f49a1804a78e12368ae9769af99273e444b94 Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Tue, 17 Mar 2026 16:04:32 +0100 Subject: [PATCH 09/73] put initial F in state --- lib/CorleoneOED/src/oed.jl | 51 ++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/lib/CorleoneOED/src/oed.jl b/lib/CorleoneOED/src/oed.jl index a59a90a1..ba791aad 100644 --- a/lib/CorleoneOED/src/oed.jl +++ b/lib/CorleoneOED/src/oed.jl @@ -157,16 +157,13 @@ function get_idxs_fisher(oed::OEDLayer, symcache::SymbolicIndexingInterface.Symb return idxs end -function update_fim!(oed::OEDLayer{DISCRETE, SAMPLED, FIXED}, experiments) where {DISCRETE, SAMPLED, FIXED} - ps, st = LuxCore.setup(Random.default_rng(), oed) +function update_fim(oed::OEDLayer{DISCRETE, SAMPLED, FIXED}, experiments, st::NamedTuple) where {DISCRETE, SAMPLED, FIXED} + _ps, _st = LuxCore.setup(Random.default_rng(), oed) FIM = sum(map(experiments) do experiment - fisher_information(oed, nothing, experiment, st)[1] + fisher_information(oed, nothing, experiment, _st)[1] end) - u0 = oed.layer.problem.u0 - idxs_fim = get_idxs_fisher(oed, st.symcache) - u0[idxs_fim] .= FIM[triu(ones(Bool, size(FIM)))] - remake(oed.layer.problem, u0 = u0) - return nothing + + return merge(st, (; F_init = FIM)) end n_observed(layer::OEDLayer) = length(layer.sampling_indices) @@ -208,10 +205,14 @@ function LuxCore.initialstates(rng::Random.AbstractRNG, oed::Union{OEDLayer{true end end : measurement_indices + T = eltype(problem.u0) + F_init = zeros(T, size(oed.observed.fisher.getters)) + return merge( st, (; observation_grid = WeightedObservation(weighting_grid), active_controls = measurement_indices, + F_init = F_init ) ) end @@ -222,6 +223,8 @@ function LuxCore.initialstates(rng::Random.AbstractRNG, oed::OEDLayer{true, true st = LuxCore.initialstates(rng, layer) # Our goal is to build a weigthing matrix similar to the indexgrid grids = Corleone.get_timegrid.(controls) + T = eltype(problem.u0) + F_init = zeros(T, size(oed.observed.fisher.getters)) return map(st) do sti tspan = (first(first(sti.tspans)), last(last(sti.tspans))) @@ -257,11 +260,25 @@ function LuxCore.initialstates(rng::Random.AbstractRNG, oed::OEDLayer{true, true sti, (; observation_grid = WeightedObservation(weighting_grid), active_controls = measurement_indices, + F_init = F_init ) ) end end +get_problem(oed::OEDLayer{<:Any,<:Any,<:Any,<:SingleShootingLayer}) = oed.layer.problem +get_problem(oed::OEDLayer{<:Any,<:Any,<:Any,<:MultipleShootingLayer}) = oed.layer.layer.problem + +function LuxCore.initialstates(rng::Random.AbstractRNG, oed::OEDLayer) + (; layer, sampling_indices) = oed + st = LuxCore.initialstates(rng, layer) + problem = get_problem(oed) + T = eltype(problem.u0) + F_init = zeros(T, size(oed.observed.fisher.getters)) + + return merge(st, (; F_init = F_init)) +end + __fisher_information(oed::OEDLayer, traj::Trajectory) = oed.observed.fisher(traj) function __fisher_information(oed::OEDLayer{true, true, false, <:MultipleShootingLayer}, traj::Trajectory, ps, st::NamedTuple) @@ -298,13 +315,13 @@ end fisher_information(oed::OEDLayer, x, ps, st::NamedTuple) = begin traj, st = oed(x, ps, st) - sum(__fisher_information(oed, traj)), st + st.F_init + sum(__fisher_information(oed, traj)), st end # Continuous ALWAYS last FIM fisher_information(oed::OEDLayer{false}, x, ps, st::NamedTuple) = begin traj, st = oed(x, ps, st) - last(__fisher_information(oed, traj)), st + st.F_init + last(__fisher_information(oed, traj)), st end # DISCRETE and SAMPLING -> weighted sum @@ -312,24 +329,26 @@ fisher_information(oed::OEDLayer{true, true, false, <:SingleShootingLayer}, x, p (; observation_grid) = st traj, st = oed(x, ps, st) Gs = __fisher_information(oed, traj) - observation_grid(ps.controls, Gs), st + st.F_init + observation_grid(ps.controls, Gs), st end # FIXED DISCRETE and SAMPLING -> use helper function fisher_information(oed::OEDLayer{true, true, true}, x, ps, st::NamedTuple) = begin traj, st = oed(x, ps, st) - __fisher_information(oed, traj, ps, st), st + st.F_init + __fisher_information(oed, traj, ps, st), st end fisher_information(oed::OEDLayer{true, true, false, <:MultipleShootingLayer}, x, ps, st::NamedTuple) = begin traj, st = oed(x, ps, st) Gs = __fisher_information(oed, traj, ps, st) - sum( + F = sum( map(eachindex(Gs)) do i psi, sti = getproperty(ps, Symbol("interval_$i")), getproperty(st, Symbol("interval_$i")) sti.observation_grid(psi.controls, Gs[i]) end - ), st + ) + + return st.F_init + F, st end @@ -338,7 +357,7 @@ fisher_information(oed::OEDLayer{true, false}, x, ps, st::NamedTuple) = begin (; sampling_indices, layer) = oed (; observation_grid) = st traj, st = oed(x, ps, st) - sum(__fisher_information(oed, traj)), st + st.F_init + sum(__fisher_information(oed, traj)), st end # FIXED + CONTINUOUS @@ -346,7 +365,7 @@ fisher_information(oed::OEDLayer{false, true, true}, x, ps, st::NamedTuple) = be (; sampling_indices, layer) = oed (; observation_grid) = st traj, st = oed(x, ps, st) - __fisher_information(oed, traj, ps, st), st + st.F_init + __fisher_information(oed, traj, ps, st), st end sensitivities(oed::OEDLayer, traj::Trajectory) = oed.observed.sensitivities(traj) From 7c5fddd3dd3628103f890b2f64b9c9d519a371ef Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Tue, 17 Mar 2026 16:49:02 +0100 Subject: [PATCH 10/73] pass also the state for updating fim --- lib/CorleoneOED/src/oed.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/CorleoneOED/src/oed.jl b/lib/CorleoneOED/src/oed.jl index ba791aad..03125c77 100644 --- a/lib/CorleoneOED/src/oed.jl +++ b/lib/CorleoneOED/src/oed.jl @@ -158,9 +158,8 @@ function get_idxs_fisher(oed::OEDLayer, symcache::SymbolicIndexingInterface.Symb end function update_fim(oed::OEDLayer{DISCRETE, SAMPLED, FIXED}, experiments, st::NamedTuple) where {DISCRETE, SAMPLED, FIXED} - _ps, _st = LuxCore.setup(Random.default_rng(), oed) FIM = sum(map(experiments) do experiment - fisher_information(oed, nothing, experiment, _st)[1] + fisher_information(oed, nothing, experiment.ps, experiment.st)[1] end) return merge(st, (; F_init = FIM)) From d0014571a2f8b682f51662d7a4a4665185e052be Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Wed, 18 Mar 2026 07:11:40 +0100 Subject: [PATCH 11/73] update multiexperiment fim --- lib/CorleoneOED/src/multiexperiments.jl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/CorleoneOED/src/multiexperiments.jl b/lib/CorleoneOED/src/multiexperiments.jl index 930fd68d..eee25840 100644 --- a/lib/CorleoneOED/src/multiexperiments.jl +++ b/lib/CorleoneOED/src/multiexperiments.jl @@ -141,6 +141,16 @@ Corleone.get_number_of_shooting_constraints(multi::MultiExperimentLayer{<:Any, < Corleone.get_number_of_shooting_constraints(multi::MultiExperimentLayer{<:Any, <:Any, true, <:MultipleShootingLayer}) = sum(map(Corleone.get_number_of_shooting_constraints, multi.layers)) Corleone.get_number_of_shooting_constraints(multi::MultiExperimentLayer{<:Any, <:Any, <:Any, <:SingleShootingLayer}) = 0 +function update_fim(oed::MultiExperimentLayer{DISCRETE, FIXED, SPLIT}, experiments, st::NamedTuple) where {DISCRETE, FIXED, SPLIT} + FIM = sum(map(experiments) do experiment + fisher_information(oed, nothing, experiment.ps, experiment.st)[1] + end) + + st1 = getproperty(st, Symbol("experiment_1")) + st1 = merge(st1, (; F_init = FIM)) + + return merge(st, (; experiment_1 = st1)) +end function get_sampling_sums(multi::MultiExperimentLayer{<:Any, <:Any, true}, x, ps, st::NamedTuple{fields}) where {fields} return reduce( From f09303b896bf546ee535d159c6f021d7e405235a Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Wed, 18 Mar 2026 08:25:24 +0100 Subject: [PATCH 12/73] fix size error --- lib/CorleoneOED/src/oed.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/CorleoneOED/src/oed.jl b/lib/CorleoneOED/src/oed.jl index 03125c77..7e18f12a 100644 --- a/lib/CorleoneOED/src/oed.jl +++ b/lib/CorleoneOED/src/oed.jl @@ -205,7 +205,11 @@ function LuxCore.initialstates(rng::Random.AbstractRNG, oed::Union{OEDLayer{true end : measurement_indices T = eltype(problem.u0) - F_init = zeros(T, size(oed.observed.fisher.getters)) + size_F = size(oed.observed.fisher.getters) + if length(size_F) > 2 + size_F = size_F[1:2] + end + F_init = zeros(T, size_F) return merge( st, (; From e1b02f56d969356eeff522770ffe4dff9606e8f4 Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Wed, 18 Mar 2026 10:16:56 +0100 Subject: [PATCH 13/73] formating and multiple shooting fix --- lib/CorleoneOED/src/multiexperiments.jl | 8 +++-- lib/CorleoneOED/src/oed.jl | 46 +++++++++++++++++-------- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/lib/CorleoneOED/src/multiexperiments.jl b/lib/CorleoneOED/src/multiexperiments.jl index eee25840..1a57138c 100644 --- a/lib/CorleoneOED/src/multiexperiments.jl +++ b/lib/CorleoneOED/src/multiexperiments.jl @@ -142,9 +142,11 @@ Corleone.get_number_of_shooting_constraints(multi::MultiExperimentLayer{<:Any, < Corleone.get_number_of_shooting_constraints(multi::MultiExperimentLayer{<:Any, <:Any, <:Any, <:SingleShootingLayer}) = 0 function update_fim(oed::MultiExperimentLayer{DISCRETE, FIXED, SPLIT}, experiments, st::NamedTuple) where {DISCRETE, FIXED, SPLIT} - FIM = sum(map(experiments) do experiment - fisher_information(oed, nothing, experiment.ps, experiment.st)[1] - end) + FIM = sum( + map(experiments) do experiment + fisher_information(oed, nothing, experiment.ps, experiment.st)[1] + end + ) st1 = getproperty(st, Symbol("experiment_1")) st1 = merge(st1, (; F_init = FIM)) diff --git a/lib/CorleoneOED/src/oed.jl b/lib/CorleoneOED/src/oed.jl index 7e18f12a..06f3b03b 100644 --- a/lib/CorleoneOED/src/oed.jl +++ b/lib/CorleoneOED/src/oed.jl @@ -144,11 +144,11 @@ end function get_idxs_fisher(oed::OEDLayer, symcache::SymbolicIndexingInterface.SymbolCache) ps, st = LuxCore.setup(Random.default_rng(), oed) fim = first(fisher_information(oed, nothing, ps, st)) - F_states = Symbol.(Symbolics.variables(:F, 1:size(fim,1), 1:size(fim, 2))[triu(ones(Bool, size(fim)))]) + F_states = Symbol.(Symbolics.variables(:F, 1:size(fim, 1), 1:size(fim, 2))[triu(ones(Bool, size(fim)))]) idxs = map(F_states) do F - for (k,v) in symcache.variables - if isequal(k,F) + for (k, v) in symcache.variables + if isequal(k, F) return v end end @@ -158,9 +158,11 @@ function get_idxs_fisher(oed::OEDLayer, symcache::SymbolicIndexingInterface.Symb end function update_fim(oed::OEDLayer{DISCRETE, SAMPLED, FIXED}, experiments, st::NamedTuple) where {DISCRETE, SAMPLED, FIXED} - FIM = sum(map(experiments) do experiment - fisher_information(oed, nothing, experiment.ps, experiment.st)[1] - end) + FIM = sum( + map(experiments) do experiment + fisher_information(oed, nothing, experiment.ps, experiment.st)[1] + end + ) return merge(st, (; F_init = FIM)) end @@ -259,20 +261,22 @@ function LuxCore.initialstates(rng::Random.AbstractRNG, oed::OEDLayer{true, true end end + sti1 = merge(sti.interval1, (; F_init = F_init)) + sti = merge(sti, (; interval_1 = sti1)) + merge( sti, (; observation_grid = WeightedObservation(weighting_grid), active_controls = measurement_indices, - F_init = F_init ) ) end end -get_problem(oed::OEDLayer{<:Any,<:Any,<:Any,<:SingleShootingLayer}) = oed.layer.problem -get_problem(oed::OEDLayer{<:Any,<:Any,<:Any,<:MultipleShootingLayer}) = oed.layer.layer.problem +get_problem(oed::OEDLayer{<:Any, <:Any, <:Any, <:SingleShootingLayer}) = oed.layer.problem +get_problem(oed::OEDLayer{<:Any, <:Any, <:Any, <:MultipleShootingLayer}) = oed.layer.layer.problem -function LuxCore.initialstates(rng::Random.AbstractRNG, oed::OEDLayer) +function LuxCore.initialstates(rng::Random.AbstractRNG, oed::OEDLayer{<:Any, <:Any, <:Any, <:SingleShootingLayer}) (; layer, sampling_indices) = oed st = LuxCore.initialstates(rng, layer) problem = get_problem(oed) @@ -282,6 +286,18 @@ function LuxCore.initialstates(rng::Random.AbstractRNG, oed::OEDLayer) return merge(st, (; F_init = F_init)) end +function LuxCore.initialstates(rng::Random.AbstractRNG, oed::OEDLayer{<:Any, <:Any, <:Any, <:MultipleShootingLayer}) + (; layer, sampling_indices) = oed + st = LuxCore.initialstates(rng, layer) + problem = get_problem(oed) + T = eltype(problem.u0) + F_init = zeros(T, size(oed.observed.fisher.getters)) + + st1 = st.interval_1 + st1 = merge(st1, (; F_init = F_init)) + return merge(st, (; interval_1 = st1)) +end + __fisher_information(oed::OEDLayer, traj::Trajectory) = oed.observed.fisher(traj) function __fisher_information(oed::OEDLayer{true, true, false, <:MultipleShootingLayer}, traj::Trajectory, ps, st::NamedTuple) @@ -345,13 +361,13 @@ fisher_information(oed::OEDLayer{true, true, false, <:MultipleShootingLayer}, x, traj, st = oed(x, ps, st) Gs = __fisher_information(oed, traj, ps, st) F = sum( - map(eachindex(Gs)) do i - psi, sti = getproperty(ps, Symbol("interval_$i")), getproperty(st, Symbol("interval_$i")) - sti.observation_grid(psi.controls, Gs[i]) + map(eachindex(Gs)) do i + psi, sti = getproperty(ps, Symbol("interval_$i")), getproperty(st, Symbol("interval_$i")) + sti.observation_grid(psi.controls, Gs[i]) end - ) + ) - return st.F_init + F, st + return st.interval_1.F_init + F, st end From 1e38bc082ff48c9aaa34867fc6f55251a4a3dd5d Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Wed, 18 Mar 2026 10:19:19 +0100 Subject: [PATCH 14/73] format --- lib/CorleoneOED/src/oed.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CorleoneOED/src/oed.jl b/lib/CorleoneOED/src/oed.jl index 06f3b03b..65e6947f 100644 --- a/lib/CorleoneOED/src/oed.jl +++ b/lib/CorleoneOED/src/oed.jl @@ -217,7 +217,7 @@ function LuxCore.initialstates(rng::Random.AbstractRNG, oed::Union{OEDLayer{true st, (; observation_grid = WeightedObservation(weighting_grid), active_controls = measurement_indices, - F_init = F_init + F_init = F_init, ) ) end From f26aa3bce9cd660df06f0c619bcb1a608ea0a640 Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Wed, 18 Mar 2026 12:02:33 +0100 Subject: [PATCH 15/73] more dispatching for single and multiple shooting --- lib/CorleoneOED/src/oed.jl | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/lib/CorleoneOED/src/oed.jl b/lib/CorleoneOED/src/oed.jl index 65e6947f..275b5b6a 100644 --- a/lib/CorleoneOED/src/oed.jl +++ b/lib/CorleoneOED/src/oed.jl @@ -157,16 +157,28 @@ function get_idxs_fisher(oed::OEDLayer, symcache::SymbolicIndexingInterface.Symb return idxs end -function update_fim(oed::OEDLayer{DISCRETE, SAMPLED, FIXED}, experiments, st::NamedTuple) where {DISCRETE, SAMPLED, FIXED} +function update_fim(oed::OEDLayer{<:Any, SAMPLED, FIXED, <:SingleShootingLayer}, experiments, st::NamedTuple) where {DISCRETE, SAMPLED, FIXED} FIM = sum( map(experiments) do experiment - fisher_information(oed, nothing, experiment.ps, experiment.st)[1] + fisher_information(oed, nothing, experiment.ps, experiment.st)[1] - experiments.st.F_init end ) return merge(st, (; F_init = FIM)) end +function update_fim(oed::OEDLayer{<:Any, SAMPLED, FIXED, <:MultipleShootingLayer}, experiments, st::NamedTuple) where {DISCRETE, SAMPLED, FIXED} + FIM = sum( + map(experiments) do experiment + fisher_information(oed, nothing, experiment.ps, experiment.st)[1] - experiments.st[1].F_init + end + ) + + st1 = merge(st[1], (; F_init = FIM)) + + return merge(st, (; interval_1 = st1)) +end + n_observed(layer::OEDLayer) = length(layer.sampling_indices) Corleone.get_number_of_shooting_constraints(oed::OEDLayer{<:Any, <:Any, <:Any, <:MultipleShootingLayer}) = Corleone.get_number_of_shooting_constraints(oed.layer) Corleone.get_number_of_shooting_constraints(oed::OEDLayer{<:Any, <:Any, <:Any, <:SingleShootingLayer}) = 0 @@ -261,7 +273,7 @@ function LuxCore.initialstates(rng::Random.AbstractRNG, oed::OEDLayer{true, true end end - sti1 = merge(sti.interval1, (; F_init = F_init)) + sti1 = merge(sti.interval_1, (; F_init = F_init)) sti = merge(sti, (; interval_1 = sti1)) merge( @@ -338,11 +350,16 @@ fisher_information(oed::OEDLayer, x, ps, st::NamedTuple) = begin end # Continuous ALWAYS last FIM -fisher_information(oed::OEDLayer{false}, x, ps, st::NamedTuple) = begin +fisher_information(oed::OEDLayer{false, <:Any, <:Any, <:SingleShootingLayer}, x, ps, st::NamedTuple) = begin traj, st = oed(x, ps, st) st.F_init + last(__fisher_information(oed, traj)), st end +fisher_information(oed::OEDLayer{false, <:Any, <:Any, <:MultipleShootingLayer}, x, ps, st::NamedTuple) = begin + traj, st = oed(x, ps, st) + st[1].F_init + last(__fisher_information(oed, traj)), st +end + # DISCRETE and SAMPLING -> weighted sum fisher_information(oed::OEDLayer{true, true, false, <:SingleShootingLayer}, x, ps, st::NamedTuple) = begin (; observation_grid) = st @@ -367,18 +384,25 @@ fisher_information(oed::OEDLayer{true, true, false, <:MultipleShootingLayer}, x, end ) - return st.interval_1.F_init + F, st + return st[1].F_init + F, st end # DISCRETE -> SUM -fisher_information(oed::OEDLayer{true, false}, x, ps, st::NamedTuple) = begin +fisher_information(oed::OEDLayer{true, false, <:Any, <:SingleShootingLayer}, x, ps, st::NamedTuple) = begin (; sampling_indices, layer) = oed (; observation_grid) = st traj, st = oed(x, ps, st) st.F_init + sum(__fisher_information(oed, traj)), st end +fisher_information(oed::OEDLayer{true, false, <:Any, <:MultipleShootingLayer}, x, ps, st::NamedTuple) = begin + (; sampling_indices, layer) = oed + (; observation_grid) = st + traj, st = oed(x, ps, st) + st[1].F_init + sum(__fisher_information(oed, traj)), st +end + # FIXED + CONTINUOUS fisher_information(oed::OEDLayer{false, true, true}, x, ps, st::NamedTuple) = begin (; sampling_indices, layer) = oed From ff9c635698ae25c2b3479ea7fc6de1409d1d506e Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Wed, 18 Mar 2026 16:00:37 +0100 Subject: [PATCH 16/73] fix tests --- lib/CorleoneOED/src/multiexperiments.jl | 31 +++++++---- lib/CorleoneOED/src/oed.jl | 70 +++++++++++-------------- 2 files changed, 53 insertions(+), 48 deletions(-) diff --git a/lib/CorleoneOED/src/multiexperiments.jl b/lib/CorleoneOED/src/multiexperiments.jl index 1a57138c..4a23605c 100644 --- a/lib/CorleoneOED/src/multiexperiments.jl +++ b/lib/CorleoneOED/src/multiexperiments.jl @@ -149,7 +149,16 @@ function update_fim(oed::MultiExperimentLayer{DISCRETE, FIXED, SPLIT}, experimen ) st1 = getproperty(st, Symbol("experiment_1")) - st1 = merge(st1, (; F_init = FIM)) + if SPLIT + if isa(oed.layers[1], MultipleShootingLayer) + int1 = merge(st1.interval_1, (; F_init = FIM)) + st1 = merge(st1, (; interval_1 = int1)) + end + else + if isa(oed.layers, MultipleShootingLayer) + st1 = merge(st1, (; F_init = FIM)) + end + end return merge(st, (; experiment_1 = st1)) end @@ -210,17 +219,20 @@ function __fisher_information(multi::MultiExperimentLayer{<:Any, true, true}, tr return F end -function fisher_information(multi::MultiExperimentLayer{<:Any, <:Any, false}, x, ps, st::NamedTuple{fields}) where {fields} - return sum( - map(fields) do field - fisher_information(multi.layers, x, getproperty(ps, field), getproperty(st, field))[1] +function fisher_information(multi::MultiExperimentLayer{<:Any, <:Any, false}, x, ps, st::NamedTuple{fields}; add_initial = true) where {fields} + F = sum( + map(fields) do field + fisher_information(multi.layers, x, getproperty(ps, field), getproperty(st, field), add_initial = false)[1] end - ), st + ) + F_init = isa(multi.layers.layer, MultipleShootingLayer) ? st[1][1].F_init : st[1].F_init + add_initial && return F + F_init, st + return F, st end -function fisher_information(multi::MultiExperimentLayer{<:Any, <:Any, true}, x, ps, st::NamedTuple{fields}) where {fields} +function fisher_information(multi::MultiExperimentLayer{<:Any, <:Any, true}, x, ps, st::NamedTuple{fields}; add_initial = true) where {fields} fim = map(enumerate(fields)) do (i, field) - fisher_information(multi.layers[i], x, getproperty(ps, field), getproperty(st, field))[1] + fisher_information(multi.layers[i], x, getproperty(ps, field), getproperty(st, field); add_initial = false)[1] end np = length(multi.params.all) F = zeros(eltype(fim[1]), (np, np)) @@ -228,7 +240,8 @@ function fisher_information(multi::MultiExperimentLayer{<:Any, <:Any, true}, x, idxs = [multi.params.permutation[j] for j in multi.params.original[i]] F[idxs, idxs] .+= fimi end - + F_init = isa(multi.layers[1].layer, MultipleShootingLayer) ? st[1][1].F_init : st[1].F_init + add_initial && return F + F_init, st return F, st end diff --git a/lib/CorleoneOED/src/oed.jl b/lib/CorleoneOED/src/oed.jl index 275b5b6a..c747ff89 100644 --- a/lib/CorleoneOED/src/oed.jl +++ b/lib/CorleoneOED/src/oed.jl @@ -141,22 +141,6 @@ function OEDLayer{DISCRETE}(layer::SingleShootingLayer, args...; measurements = return OEDLayer{DISCRETE, SAMPLED, FIXED, typeof(newlayer), typeof(observed)}(newlayer, observed, samplings) end -function get_idxs_fisher(oed::OEDLayer, symcache::SymbolicIndexingInterface.SymbolCache) - ps, st = LuxCore.setup(Random.default_rng(), oed) - fim = first(fisher_information(oed, nothing, ps, st)) - F_states = Symbol.(Symbolics.variables(:F, 1:size(fim, 1), 1:size(fim, 2))[triu(ones(Bool, size(fim)))]) - - idxs = map(F_states) do F - for (k, v) in symcache.variables - if isequal(k, F) - return v - end - end - end - - return idxs -end - function update_fim(oed::OEDLayer{<:Any, SAMPLED, FIXED, <:SingleShootingLayer}, experiments, st::NamedTuple) where {DISCRETE, SAMPLED, FIXED} FIM = sum( map(experiments) do experiment @@ -243,7 +227,7 @@ function LuxCore.initialstates(rng::Random.AbstractRNG, oed::OEDLayer{true, true T = eltype(problem.u0) F_init = zeros(T, size(oed.observed.fisher.getters)) - return map(st) do sti + st_new = map(st) do sti tspan = (first(first(sti.tspans)), last(last(sti.tspans))) overall_grid = vcat(reduce(vcat, grids), collect(tspan)) unique!(sort!(overall_grid)) @@ -273,9 +257,6 @@ function LuxCore.initialstates(rng::Random.AbstractRNG, oed::OEDLayer{true, true end end - sti1 = merge(sti.interval_1, (; F_init = F_init)) - sti = merge(sti, (; interval_1 = sti1)) - merge( sti, (; observation_grid = WeightedObservation(weighting_grid), @@ -283,6 +264,9 @@ function LuxCore.initialstates(rng::Random.AbstractRNG, oed::OEDLayer{true, true ) ) end + + st1 = merge(st_new[1], (; F_init = F_init)) + return merge(st_new, (; interval_1 = st1)) end get_problem(oed::OEDLayer{<:Any, <:Any, <:Any, <:SingleShootingLayer}) = oed.layer.problem @@ -344,37 +328,42 @@ function __fisher_information(oed::OEDLayer{true, true, true}, traj::Trajectory, return observation_grid(controls, Gs) end -fisher_information(oed::OEDLayer, x, ps, st::NamedTuple) = begin +fisher_information(oed::OEDLayer, x, ps, st::NamedTuple; add_initial = true) = begin traj, st = oed(x, ps, st) - st.F_init + sum(__fisher_information(oed, traj)), st + F = add_initial ? st.F_init + sum(__fisher_information(oed, traj)) : sum(__fisher_information(oed, traj)) + return F, st end # Continuous ALWAYS last FIM -fisher_information(oed::OEDLayer{false, <:Any, <:Any, <:SingleShootingLayer}, x, ps, st::NamedTuple) = begin +fisher_information(oed::OEDLayer{false, <:Any, <:Any, <:SingleShootingLayer}, x, ps, st::NamedTuple; add_initial = true) = begin traj, st = oed(x, ps, st) - st.F_init + last(__fisher_information(oed, traj)), st + F = add_initial ? st.F_init + last(__fisher_information(oed, traj)) : last(__fisher_information(oed, traj)) + return F, st end -fisher_information(oed::OEDLayer{false, <:Any, <:Any, <:MultipleShootingLayer}, x, ps, st::NamedTuple) = begin +fisher_information(oed::OEDLayer{false, <:Any, <:Any, <:MultipleShootingLayer}, x, ps, st::NamedTuple; add_initial = true) = begin traj, st = oed(x, ps, st) - st[1].F_init + last(__fisher_information(oed, traj)), st + F = add_initial ? st[1].F_init + last(__fisher_information(oed, traj)) : last(__fisher_information(oed, traj)) + return F, st end # DISCRETE and SAMPLING -> weighted sum -fisher_information(oed::OEDLayer{true, true, false, <:SingleShootingLayer}, x, ps, st::NamedTuple) = begin +fisher_information(oed::OEDLayer{true, true, false, <:SingleShootingLayer}, x, ps, st::NamedTuple; add_initial = true) = begin (; observation_grid) = st traj, st = oed(x, ps, st) Gs = __fisher_information(oed, traj) - st.F_init + observation_grid(ps.controls, Gs), st + F = add_initial ? st.F_init + observation_grid(ps.controls, Gs) : observation_grid(ps.controls, Gs) + return F, st end # FIXED DISCRETE and SAMPLING -> use helper function -fisher_information(oed::OEDLayer{true, true, true}, x, ps, st::NamedTuple) = begin +fisher_information(oed::OEDLayer{true, true, true}, x, ps, st::NamedTuple; add_initial = true) = begin traj, st = oed(x, ps, st) - st.F_init + __fisher_information(oed, traj, ps, st), st + F = add_initial ? st.F_init + __fisher_information(oed, traj, ps, st) : __fisher_information(oed, traj, ps, st) + return F, st end -fisher_information(oed::OEDLayer{true, true, false, <:MultipleShootingLayer}, x, ps, st::NamedTuple) = begin +fisher_information(oed::OEDLayer{true, true, false, <:MultipleShootingLayer}, x, ps, st::NamedTuple; add_initial = true) = begin traj, st = oed(x, ps, st) Gs = __fisher_information(oed, traj, ps, st) F = sum( @@ -384,31 +373,34 @@ fisher_information(oed::OEDLayer{true, true, false, <:MultipleShootingLayer}, x, end ) - return st[1].F_init + F, st - + add_initial && return st[1].F_init + F, st + return F, st end # DISCRETE -> SUM -fisher_information(oed::OEDLayer{true, false, <:Any, <:SingleShootingLayer}, x, ps, st::NamedTuple) = begin +fisher_information(oed::OEDLayer{true, false, <:Any, <:SingleShootingLayer}, x, ps, st::NamedTuple; add_initial = true) = begin (; sampling_indices, layer) = oed (; observation_grid) = st traj, st = oed(x, ps, st) - st.F_init + sum(__fisher_information(oed, traj)), st + F = add_initial ? st.F_init + sum(__fisher_information(oed, traj)) : sum(__fisher_information(oed, traj)) + return F, st end -fisher_information(oed::OEDLayer{true, false, <:Any, <:MultipleShootingLayer}, x, ps, st::NamedTuple) = begin +fisher_information(oed::OEDLayer{true, false, <:Any, <:MultipleShootingLayer}, x, ps, st::NamedTuple; add_initial = true) = begin (; sampling_indices, layer) = oed (; observation_grid) = st traj, st = oed(x, ps, st) - st[1].F_init + sum(__fisher_information(oed, traj)), st + F = add_initial ? st[1].F_init + sum(__fisher_information(oed, traj)) : sum(__fisher_information(oed, traj)) + return F, st end # FIXED + CONTINUOUS -fisher_information(oed::OEDLayer{false, true, true}, x, ps, st::NamedTuple) = begin +fisher_information(oed::OEDLayer{false, true, true}, x, ps, st::NamedTuple; add_initial = true) = begin (; sampling_indices, layer) = oed (; observation_grid) = st traj, st = oed(x, ps, st) - st.F_init + __fisher_information(oed, traj, ps, st), st + F = add_initial ? st.F_init + __fisher_information(oed, traj, ps, st) : __fisher_information(oed, traj, ps, st) + return F, st end sensitivities(oed::OEDLayer, traj::Trajectory) = oed.observed.sensitivities(traj) From 6ab2f89b25c39fab63cba9ecdac9ea91048d6c2f Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Thu, 19 Mar 2026 07:23:23 +0100 Subject: [PATCH 17/73] correct update for multiexperiment --- lib/CorleoneOED/src/multiexperiments.jl | 26 +++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/CorleoneOED/src/multiexperiments.jl b/lib/CorleoneOED/src/multiexperiments.jl index 4a23605c..f4a2695c 100644 --- a/lib/CorleoneOED/src/multiexperiments.jl +++ b/lib/CorleoneOED/src/multiexperiments.jl @@ -141,7 +141,7 @@ Corleone.get_number_of_shooting_constraints(multi::MultiExperimentLayer{<:Any, < Corleone.get_number_of_shooting_constraints(multi::MultiExperimentLayer{<:Any, <:Any, true, <:MultipleShootingLayer}) = sum(map(Corleone.get_number_of_shooting_constraints, multi.layers)) Corleone.get_number_of_shooting_constraints(multi::MultiExperimentLayer{<:Any, <:Any, <:Any, <:SingleShootingLayer}) = 0 -function update_fim(oed::MultiExperimentLayer{DISCRETE, FIXED, SPLIT}, experiments, st::NamedTuple) where {DISCRETE, FIXED, SPLIT} +function update_fim(oed::MultiExperimentLayer{DISCRETE, FIXED, <:Any, <:SingleShootingLayer}, experiments, st::NamedTuple) where {DISCRETE, FIXED, SPLIT} FIM = sum( map(experiments) do experiment fisher_information(oed, nothing, experiment.ps, experiment.st)[1] @@ -149,16 +149,22 @@ function update_fim(oed::MultiExperimentLayer{DISCRETE, FIXED, SPLIT}, experimen ) st1 = getproperty(st, Symbol("experiment_1")) - if SPLIT - if isa(oed.layers[1], MultipleShootingLayer) - int1 = merge(st1.interval_1, (; F_init = FIM)) - st1 = merge(st1, (; interval_1 = int1)) - end - else - if isa(oed.layers, MultipleShootingLayer) - st1 = merge(st1, (; F_init = FIM)) + st1 = merge(st1, (; F_init = FIM)) + + return merge(st, (; experiment_1 = st1)) +end + + +function update_fim(oed::MultiExperimentLayer{DISCRETE, FIXED, <:Any, <:MultipleShootingLayer}, experiments, st::NamedTuple) where {DISCRETE, FIXED, SPLIT} + FIM = sum( + map(experiments) do experiment + fisher_information(oed, nothing, experiment.ps, experiment.st)[1] end - end + ) + + st1 = getproperty(st, Symbol("experiment_1")) + int1 = merge(st1.interval_1, (; F_init = FIM)) + st1 = merge(st1, (; interval_1 = int1)) return merge(st, (; experiment_1 = st1)) end From 0ea15774e4aec4abca87770f23ccad111500fe52 Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Thu, 19 Mar 2026 10:17:22 +0100 Subject: [PATCH 18/73] update instead of replacing FIM --- lib/CorleoneOED/src/multiexperiments.jl | 4 ++-- lib/CorleoneOED/src/oed.jl | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/CorleoneOED/src/multiexperiments.jl b/lib/CorleoneOED/src/multiexperiments.jl index f4a2695c..d6552f8b 100644 --- a/lib/CorleoneOED/src/multiexperiments.jl +++ b/lib/CorleoneOED/src/multiexperiments.jl @@ -149,7 +149,7 @@ function update_fim(oed::MultiExperimentLayer{DISCRETE, FIXED, <:Any, <:SingleSh ) st1 = getproperty(st, Symbol("experiment_1")) - st1 = merge(st1, (; F_init = FIM)) + st1 = merge(st1, (; F_init = FIM + st[1].F_init)) return merge(st, (; experiment_1 = st1)) end @@ -163,7 +163,7 @@ function update_fim(oed::MultiExperimentLayer{DISCRETE, FIXED, <:Any, <:Multiple ) st1 = getproperty(st, Symbol("experiment_1")) - int1 = merge(st1.interval_1, (; F_init = FIM)) + int1 = merge(st1.interval_1, (; F_init = FIM + st[1][1].F_init)) st1 = merge(st1, (; interval_1 = int1)) return merge(st, (; experiment_1 = st1)) diff --git a/lib/CorleoneOED/src/oed.jl b/lib/CorleoneOED/src/oed.jl index c747ff89..a08ab049 100644 --- a/lib/CorleoneOED/src/oed.jl +++ b/lib/CorleoneOED/src/oed.jl @@ -144,21 +144,21 @@ end function update_fim(oed::OEDLayer{<:Any, SAMPLED, FIXED, <:SingleShootingLayer}, experiments, st::NamedTuple) where {DISCRETE, SAMPLED, FIXED} FIM = sum( map(experiments) do experiment - fisher_information(oed, nothing, experiment.ps, experiment.st)[1] - experiments.st.F_init + fisher_information(oed, nothing, experiment.ps, experiment.st)[1] end ) - return merge(st, (; F_init = FIM)) + return merge(st, (; F_init = FIM + st.F_init)) end function update_fim(oed::OEDLayer{<:Any, SAMPLED, FIXED, <:MultipleShootingLayer}, experiments, st::NamedTuple) where {DISCRETE, SAMPLED, FIXED} FIM = sum( map(experiments) do experiment - fisher_information(oed, nothing, experiment.ps, experiment.st)[1] - experiments.st[1].F_init + fisher_information(oed, nothing, experiment.ps, experiment.st)[1] end ) - st1 = merge(st[1], (; F_init = FIM)) + st1 = merge(st[1], (; F_init = FIM + st[1].F_init)) return merge(st, (; interval_1 = st1)) end From 8441996d35910b24abf54b2ec25243c807303a7e Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Thu, 19 Mar 2026 10:36:17 +0100 Subject: [PATCH 19/73] Document the update_fim in example --- .../examples/lotka_multiexperiments/main.jl | 23 +++++++++++++ lib/CorleoneOED/examples/lotka_oed/main.jl | 34 ++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/lib/CorleoneOED/examples/lotka_multiexperiments/main.jl b/lib/CorleoneOED/examples/lotka_multiexperiments/main.jl index ea9a47b2..e97b2717 100644 --- a/lib/CorleoneOED/examples/lotka_multiexperiments/main.jl +++ b/lib/CorleoneOED/examples/lotka_multiexperiments/main.jl @@ -117,6 +117,29 @@ optsol, _ = multi_exp(nothing, uopt + zero(ComponentArray(ps)), st) plot_experiments(optsol) +# ## Updating the FIM + +# As before in [the Lotka OED example](@ref lotka_oed), the initial FIM can be updated from +# the obtained solution. This way, the `MultiExperimentLayer` can be easily used in an iterative +# measurement campaign in which it is necessary to also consider previous experiments. + +previous_experiment = (ps=uopt.u + zero(ComponentArray(ps)), st=st) + +st_new = CorleoneOED.update_fim(multi_exp, [previous_experiment], st) +optprob = remake(optprob, p=st_new) + +uopt = solve( + optprob, Ipopt.Optimizer(), + tol = 1.0e-7, + hessian_approximation = "limited-memory", + max_iter = 300 +); + +# +optsol, _ = multi_exp(nothing, uopt + zero(ComponentArray(ps)), st_new) + +plot_experiments(optsol) + # ## Different parameters per experiment diff --git a/lib/CorleoneOED/examples/lotka_oed/main.jl b/lib/CorleoneOED/examples/lotka_oed/main.jl index a1121b08..b10566a4 100644 --- a/lib/CorleoneOED/examples/lotka_oed/main.jl +++ b/lib/CorleoneOED/examples/lotka_oed/main.jl @@ -156,7 +156,7 @@ fim optprob = OptimizationProblem(oed, DCriterion(); M = [4.0, 4.0]) uopt = solve( optprob, Ipopt.Optimizer(), - tol = 1.0e-6, + tol = 1.0e-8, hessian_approximation = "limited-memory", max_iter = 300 ); @@ -165,6 +165,38 @@ uopt = solve( optsol, _ = oed(nothing, uopt + zero(ComponentArray(ps)), st) plot_oed(optsol) +# ## Updating the FIM with previously run experiments + +# Assuming that OED is used iteratively in a measurement campaign during which parameter estimation +# and OED are alternatingly used, one is interested in updating the FIM in the current +# iteration of OED with the previously run experiments, but for the current parameter values. +# CorleoneOED provides this functionality via the state `st` of the `OEDLayer` in which +# the initial value of the Fisher information matrix is stored. Per default, the FIM is +# initialized with zeros. + +st.F_init + +# However, with the solution from above it can be updated easily + +previous_experiment = (ps=uopt.u + zero(ComponentArray(ps)), st=st) + +st_new = CorleoneOED.update_fim(oed, [previous_experiment], st_new) +st_new.F_init + +# Now, the optimization problem can be remaked and solved with the updated state. + +optprob = remake(optprob, p=st_new) +uopt = solve( + optprob, Ipopt.Optimizer(), + tol = 1.0e-8, + hessian_approximation = "limited-memory", + max_iter = 300 +); + +# However, in this example, the solution is the same, as also the parameter values are unchanged. +optsol, _ = oed(nothing, uopt + zero(ComponentArray(ps)), st_new) +plot_oed(optsol) + # ## Sampling-free OED From ecf3d19ceece21ad5fc735b8c995cde929cfc8f0 Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Thu, 19 Mar 2026 10:39:11 +0100 Subject: [PATCH 20/73] format --- lib/CorleoneOED/examples/lotka_multiexperiments/main.jl | 4 ++-- lib/CorleoneOED/examples/lotka_oed/main.jl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/CorleoneOED/examples/lotka_multiexperiments/main.jl b/lib/CorleoneOED/examples/lotka_multiexperiments/main.jl index e97b2717..560a332e 100644 --- a/lib/CorleoneOED/examples/lotka_multiexperiments/main.jl +++ b/lib/CorleoneOED/examples/lotka_multiexperiments/main.jl @@ -123,10 +123,10 @@ plot_experiments(optsol) # the obtained solution. This way, the `MultiExperimentLayer` can be easily used in an iterative # measurement campaign in which it is necessary to also consider previous experiments. -previous_experiment = (ps=uopt.u + zero(ComponentArray(ps)), st=st) +previous_experiment = (ps = uopt.u + zero(ComponentArray(ps)), st = st) st_new = CorleoneOED.update_fim(multi_exp, [previous_experiment], st) -optprob = remake(optprob, p=st_new) +optprob = remake(optprob, p = st_new) uopt = solve( optprob, Ipopt.Optimizer(), diff --git a/lib/CorleoneOED/examples/lotka_oed/main.jl b/lib/CorleoneOED/examples/lotka_oed/main.jl index b10566a4..78d2168a 100644 --- a/lib/CorleoneOED/examples/lotka_oed/main.jl +++ b/lib/CorleoneOED/examples/lotka_oed/main.jl @@ -178,14 +178,14 @@ st.F_init # However, with the solution from above it can be updated easily -previous_experiment = (ps=uopt.u + zero(ComponentArray(ps)), st=st) +previous_experiment = (ps = uopt.u + zero(ComponentArray(ps)), st = st) st_new = CorleoneOED.update_fim(oed, [previous_experiment], st_new) st_new.F_init # Now, the optimization problem can be remaked and solved with the updated state. -optprob = remake(optprob, p=st_new) +optprob = remake(optprob, p = st_new) uopt = solve( optprob, Ipopt.Optimizer(), tol = 1.0e-8, From 32d50b1c9c3a968925c965f73a95a8178899f32c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 06:11:31 +0000 Subject: [PATCH 21/73] Bump crate-ci/typos from 1.18.0 to 1.45.2 Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.18.0 to 1.45.2. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/v1.18.0...v1.45.2) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.45.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/SpellCheck.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml index 746b039a..d6965c15 100644 --- a/.github/workflows/SpellCheck.yml +++ b/.github/workflows/SpellCheck.yml @@ -10,4 +10,4 @@ jobs: - name: Checkout Actions Repository uses: actions/checkout@v6 - name: Check spelling - uses: crate-ci/typos@v1.18.0 \ No newline at end of file + uses: crate-ci/typos@v1.45.2 \ No newline at end of file From e4c1113cd13e873b8ce0e9226056784a4c371daf Mon Sep 17 00:00:00 2001 From: JuliusMartensen Date: Thu, 7 May 2026 13:46:21 +0200 Subject: [PATCH 22/73] Bump version from 0.0.2 to 0.0.3 --- lib/CorleoneOED/Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CorleoneOED/Project.toml b/lib/CorleoneOED/Project.toml index ed93fa2a..e7843da5 100644 --- a/lib/CorleoneOED/Project.toml +++ b/lib/CorleoneOED/Project.toml @@ -1,7 +1,7 @@ name = "CorleoneOED" uuid = "6590a4b1-d036-41fe-a5b4-03a980244101" authors = ["Christoph Plate", "Carl Julius Martensen", "Sebastian Sager"] -version = "0.0.2" +version = "0.0.3" [deps] Corleone = "2751e0db-33e9-4374-b36d-b7219e1e6b40" From 95bb52246f860b1478a6aae1e895455a41bf51ae Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Mon, 11 May 2026 11:28:58 +0200 Subject: [PATCH 23/73] feat: integer sampling in OED problems --- .../ext/CorleoneOEDOptimizationExtension.jl | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl b/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl index c045b95a..8bfa4258 100644 --- a/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl +++ b/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl @@ -301,13 +301,50 @@ function setup_constraints(layer::MultiExperimentLayer{<:Any, <:Any, <:Any, <:Mu return sampling_cons end +function get_integrality(layer::OEDLayer{<:Any, true, false, <:MultipleShootingLayer}, integer::Bool, ps::ComponentArray, st::NamedTuple{fields}) where {fields} + int_u0 = Bool.(ps * 0) + !integer && return int_u0 + + for field in fields + _ps = getproperty(int_u0, field) + _st = getproperty(st, field) + subsets = CorleoneOED.__get_subsets(_st.active_controls, layer.sampling_indices) + for subset in subsets + _ps.controls[subset] .= true + end + end + return int_u0 +end + +function get_integrality(layer::OEDLayer{<:Any, true, <:Any, <:SingleShootingLayer}, integer::Bool, ps::ComponentArray, st) + int_u0 = Bool.(ps * 0) + !integer && return int_u0 + + subsets = CorleoneOED.__get_subsets(st.active_controls, layer.sampling_indices) + for subset in subsets + int_u0.controls[subset] .= true + end + return int_u0 +end + +function get_integrality(layer::MultiExperimentLayer{<:Any, false, <:Any, <:SingleShootingLayer}, integer::Bool, ps::ComponentArray, st) + int_u0 = Bool.(ps * 0) + !integer && return int_u0 + return int_u0 +end +function get_integrality(layer::MultiExperimentLayer{<:Any, false, <:Any, <:MultipleShootingLayer}, integer::Bool, ps::ComponentArray, st) + int_u0 = Bool.(ps * 0) + !integer && return int_u0 + return int_u0 +end + function Optimization.OptimizationProblem( layer::Union{OEDLayer{<:Any, true, false}, MultiExperimentLayer{<:Any, false}}, crit::CorleoneOED.AbstractCriterion; AD::Optimization.ADTypes.AbstractADType = AutoForwardDiff(), u0::ComponentVector = ComponentArray(first(LuxCore.setup(Random.default_rng(), layer))), constraints::Union{Nothing, <:Dict{Any, <:NamedTuple{(:t, :bounds)}}, <:NamedTuple} = nothing, - variable_type::Type{T} = Float64, + variable_type::Type{T} = Float64, integer_weights = false, M = default_M(layer), kwargs... ) where {T} @@ -332,6 +369,8 @@ function Optimization.OptimizationProblem( @assert all(lb .<= u0 .<= ub) "The initial variables are not within the bounds. Please check the input!" + integer_sampling = get_integrality(layer, integer_weights, u0, st) + # Constraints cons = setup_constraints(layer, sol, constraints) lcons, ucons = extract_constraint_bounds(layer, constraints, M) @@ -342,7 +381,7 @@ function Optimization.OptimizationProblem( # Return the optimization problem return OptimizationProblem( opt_f, u0[:], st, lb = lb[:], ub = ub[:], - lcons = lcons, ucons = ucons, + lcons = lcons, ucons = ucons, int = integer_sampling[:] ) end @@ -352,7 +391,7 @@ function Optimization.OptimizationProblem( AD::Optimization.ADTypes.AbstractADType = AutoForwardDiff(), u0::ComponentVector = ComponentArray(first(LuxCore.setup(Random.default_rng(), layer))), constraints::Union{Nothing, <:Dict{Any, <:NamedTuple{(:t, :bounds)}}, <:NamedTuple} = nothing, - variable_type::Type{T} = Float64, + variable_type::Type{T} = Float64, integer_weights = false, M = default_M(layer), kwargs... ) where {T} @@ -387,6 +426,8 @@ function Optimization.OptimizationProblem( @assert all(lb .<= u0 .<= ub) "The initial variables are not within the bounds. Please check the input!" + integer_sampling = get_integrality(layer, integer_weights, u0, st) + # Constraints cons = setup_constraints(layer, sol, constraints) lcons, ucons = extract_constraint_bounds(layer, constraints, M) @@ -397,7 +438,7 @@ function Optimization.OptimizationProblem( # Return the optimization problem return OptimizationProblem( opt_f, u0[:], st, lb = lb[:], ub = ub[:], - lcons = lcons, ucons = ucons, + lcons = lcons, ucons = ucons, int = integer_sampling ) end From aac99c6574141ddc709332d9e808331d439b0f5a Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Mon, 11 May 2026 11:52:07 +0200 Subject: [PATCH 24/73] integer weights in multiexperiments --- .../ext/CorleoneOEDOptimizationExtension.jl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl b/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl index 8bfa4258..872ce847 100644 --- a/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl +++ b/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl @@ -327,14 +327,21 @@ function get_integrality(layer::OEDLayer{<:Any, true, <:Any, <:SingleShootingLay return int_u0 end -function get_integrality(layer::MultiExperimentLayer{<:Any, false, <:Any, <:SingleShootingLayer}, integer::Bool, ps::ComponentArray, st) +function get_integrality(layer::MultiExperimentLayer{<:Any, <:Any, false}, integer::Bool, ps::ComponentArray, st::NamedTuple{fields}) where {fields} int_u0 = Bool.(ps * 0) !integer && return int_u0 + for field in fields + int_u0[field] = get_integrality(layer.layers, integer, getproperty(int_u0, field), getproperty(st, field)) + end return int_u0 end -function get_integrality(layer::MultiExperimentLayer{<:Any, false, <:Any, <:MultipleShootingLayer}, integer::Bool, ps::ComponentArray, st) + +function get_integrality(layer::MultiExperimentLayer{<:Any, false, true}, integer::Bool, ps::ComponentArray, st) int_u0 = Bool.(ps * 0) !integer && return int_u0 + for (i,field) in enumerate(fields) + int_u0[field] = get_integrality(layer.layers[i], integer, getproperty(int_u0, field), getproperty(st, field)) + end return int_u0 end From 3760ca3c383f034242ee1ea43ea276a9294bb231 Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Mon, 11 May 2026 13:47:51 +0200 Subject: [PATCH 25/73] fix initalization of F_init in multiexperiments --- .../ext/CorleoneOEDOptimizationExtension.jl | 2 +- lib/CorleoneOED/src/multiexperiments.jl | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl b/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl index 872ce847..4dfcbcd3 100644 --- a/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl +++ b/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl @@ -336,7 +336,7 @@ function get_integrality(layer::MultiExperimentLayer{<:Any, <:Any, false}, integ return int_u0 end -function get_integrality(layer::MultiExperimentLayer{<:Any, false, true}, integer::Bool, ps::ComponentArray, st) +function get_integrality(layer::MultiExperimentLayer{<:Any, false, true}, integer::Bool, ps::ComponentArray, st::NamedTuple{fields}) where {fields} int_u0 = Bool.(ps * 0) !integer && return int_u0 for (i,field) in enumerate(fields) diff --git a/lib/CorleoneOED/src/multiexperiments.jl b/lib/CorleoneOED/src/multiexperiments.jl index d6552f8b..18be4558 100644 --- a/lib/CorleoneOED/src/multiexperiments.jl +++ b/lib/CorleoneOED/src/multiexperiments.jl @@ -108,7 +108,15 @@ function LuxCore.initialstates(rng::Random.AbstractRNG, multi::MultiExperimentLa LuxCore.initialstates(rng, multi.layers[i]) end ) - return NamedTuple{exp_names}(exp_ps) + np = length(multi.params.all) + + st1 = exp_ps[1] + F_init = zeros(eltype(exp_ps[1].F_init), np, np) + st1 = merge(st1, (; F_init = F_init, )) + + new_sts = (st1, exp_ps[2:end]...) + + return NamedTuple{exp_names}(new_sts) end function LuxCore.initialstates(rng::Random.AbstractRNG, multi::MultiExperimentLayer{<:Any, <:Any, false}) From ddbfd5c281940f847ad7fa7db7bb0caf8bb21522 Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Tue, 12 May 2026 08:08:07 +0200 Subject: [PATCH 26/73] fix initialization of multiexperiments with multiple shooting --- .../ext/CorleoneOEDOptimizationExtension.jl | 11 +-------- lib/CorleoneOED/src/multiexperiments.jl | 23 ++++++++++++++++++- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl b/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl index 4dfcbcd3..cd379f4f 100644 --- a/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl +++ b/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl @@ -327,16 +327,7 @@ function get_integrality(layer::OEDLayer{<:Any, true, <:Any, <:SingleShootingLay return int_u0 end -function get_integrality(layer::MultiExperimentLayer{<:Any, <:Any, false}, integer::Bool, ps::ComponentArray, st::NamedTuple{fields}) where {fields} - int_u0 = Bool.(ps * 0) - !integer && return int_u0 - for field in fields - int_u0[field] = get_integrality(layer.layers, integer, getproperty(int_u0, field), getproperty(st, field)) - end - return int_u0 -end - -function get_integrality(layer::MultiExperimentLayer{<:Any, false, true}, integer::Bool, ps::ComponentArray, st::NamedTuple{fields}) where {fields} +function get_integrality(layer::MultiExperimentLayer, integer::Bool, ps::ComponentArray, st::NamedTuple{fields}) where {fields} int_u0 = Bool.(ps * 0) !integer && return int_u0 for (i,field) in enumerate(fields) diff --git a/lib/CorleoneOED/src/multiexperiments.jl b/lib/CorleoneOED/src/multiexperiments.jl index 18be4558..cfde10fe 100644 --- a/lib/CorleoneOED/src/multiexperiments.jl +++ b/lib/CorleoneOED/src/multiexperiments.jl @@ -101,7 +101,7 @@ function LuxCore.initialparameters(rng::Random.AbstractRNG, multi::MultiExperime return NamedTuple{exp_names}(exp_ps) end -function LuxCore.initialstates(rng::Random.AbstractRNG, multi::MultiExperimentLayer{<:Any, <:Any, true}) +function LuxCore.initialstates(rng::Random.AbstractRNG, multi::MultiExperimentLayer{<:Any, <:Any, true, <:SingleShootingLayer}) exp_names = Tuple([Symbol("experiment_$i") for i in 1:multi.n_exp]) exp_ps = Tuple( map(1:multi.n_exp) do i @@ -119,6 +119,27 @@ function LuxCore.initialstates(rng::Random.AbstractRNG, multi::MultiExperimentLa return NamedTuple{exp_names}(new_sts) end + +function LuxCore.initialstates(rng::Random.AbstractRNG, multi::MultiExperimentLayer{<:Any, <:Any, true, <:MultipleShootingLayer}) + exp_names = Tuple([Symbol("experiment_$i") for i in 1:multi.n_exp]) + exp_ps = Tuple( + map(1:multi.n_exp) do i + LuxCore.initialstates(rng, multi.layers[i]) + end + ) + np = length(multi.params.all) + + exp1 = exp_ps[1] + int1 = exp1[1] + F_init = zeros(eltype(int1.F_init), np, np) + int1 = merge(int1, (; F_init = F_init)) + st1 = merge(exp1, (; interval_1 = int1 )) + + new_sts = (st1, exp_ps[2:end]...) + + return NamedTuple{exp_names}(new_sts) +end + function LuxCore.initialstates(rng::Random.AbstractRNG, multi::MultiExperimentLayer{<:Any, <:Any, false}) exp_names = Tuple([Symbol("experiment_$i") for i in 1:multi.n_exp]) exp_ps = Tuple([LuxCore.initialstates(rng, multi.layers) for _ in 1:multi.n_exp]) From f4371c1b5363ae3f34037bbc96ac2568113d0a5b Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Tue, 12 May 2026 08:13:11 +0200 Subject: [PATCH 27/73] formatting --- lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl | 2 +- lib/CorleoneOED/src/multiexperiments.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl b/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl index cd379f4f..209e35a6 100644 --- a/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl +++ b/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl @@ -330,7 +330,7 @@ end function get_integrality(layer::MultiExperimentLayer, integer::Bool, ps::ComponentArray, st::NamedTuple{fields}) where {fields} int_u0 = Bool.(ps * 0) !integer && return int_u0 - for (i,field) in enumerate(fields) + for (i, field) in enumerate(fields) int_u0[field] = get_integrality(layer.layers[i], integer, getproperty(int_u0, field), getproperty(st, field)) end return int_u0 diff --git a/lib/CorleoneOED/src/multiexperiments.jl b/lib/CorleoneOED/src/multiexperiments.jl index cfde10fe..05d4d870 100644 --- a/lib/CorleoneOED/src/multiexperiments.jl +++ b/lib/CorleoneOED/src/multiexperiments.jl @@ -112,7 +112,7 @@ function LuxCore.initialstates(rng::Random.AbstractRNG, multi::MultiExperimentLa st1 = exp_ps[1] F_init = zeros(eltype(exp_ps[1].F_init), np, np) - st1 = merge(st1, (; F_init = F_init, )) + st1 = merge(st1, (; F_init = F_init)) new_sts = (st1, exp_ps[2:end]...) @@ -133,7 +133,7 @@ function LuxCore.initialstates(rng::Random.AbstractRNG, multi::MultiExperimentLa int1 = exp1[1] F_init = zeros(eltype(int1.F_init), np, np) int1 = merge(int1, (; F_init = F_init)) - st1 = merge(exp1, (; interval_1 = int1 )) + st1 = merge(exp1, (; interval_1 = int1)) new_sts = (st1, exp_ps[2:end]...) From 3820bd0f57095bcf8b1f08c080d72c6d137030a2 Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Tue, 12 May 2026 10:32:04 +0200 Subject: [PATCH 28/73] fix doc error --- lib/CorleoneOED/examples/lotka_oed/main.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CorleoneOED/examples/lotka_oed/main.jl b/lib/CorleoneOED/examples/lotka_oed/main.jl index 78d2168a..d4a4a4e1 100644 --- a/lib/CorleoneOED/examples/lotka_oed/main.jl +++ b/lib/CorleoneOED/examples/lotka_oed/main.jl @@ -180,7 +180,7 @@ st.F_init previous_experiment = (ps = uopt.u + zero(ComponentArray(ps)), st = st) -st_new = CorleoneOED.update_fim(oed, [previous_experiment], st_new) +st_new = CorleoneOED.update_fim(oed, [previous_experiment], st) st_new.F_init # Now, the optimization problem can be remaked and solved with the updated state. From d5e1a9c7a4f68efa90e172bdeec5a4e0dc202156 Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Tue, 12 May 2026 11:38:10 +0200 Subject: [PATCH 29/73] dispatch in setup of multiexperiments, tests work now --- .../ext/CorleoneOEDOptimizationExtension.jl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl b/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl index 209e35a6..7aed860a 100644 --- a/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl +++ b/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl @@ -327,7 +327,7 @@ function get_integrality(layer::OEDLayer{<:Any, true, <:Any, <:SingleShootingLay return int_u0 end -function get_integrality(layer::MultiExperimentLayer, integer::Bool, ps::ComponentArray, st::NamedTuple{fields}) where {fields} +function get_integrality(layer::MultiExperimentLayer{<:Any, <:Any, true}, integer::Bool, ps::ComponentArray, st::NamedTuple{fields}) where {fields} int_u0 = Bool.(ps * 0) !integer && return int_u0 for (i, field) in enumerate(fields) @@ -336,6 +336,16 @@ function get_integrality(layer::MultiExperimentLayer, integer::Bool, ps::Compone return int_u0 end +function get_integrality(layer::MultiExperimentLayer{<:Any, <:Any, false}, integer::Bool, ps::ComponentArray, st::NamedTuple{fields}) where {fields} + int_u0 = Bool.(ps * 0) + !integer && return int_u0 + for field in fields + int_u0[field] = get_integrality(layer.layers, integer, getproperty(int_u0, field), getproperty(st, field)) + end + return int_u0 +end + + function Optimization.OptimizationProblem( layer::Union{OEDLayer{<:Any, true, false}, MultiExperimentLayer{<:Any, false}}, crit::CorleoneOED.AbstractCriterion; From 20f9a77aae5dcfdcd9082a730e635a372c23c02c Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Tue, 12 May 2026 14:47:37 +0200 Subject: [PATCH 30/73] testing correct integer setup --- .../ext/CorleoneOEDOptimizationExtension.jl | 12 +++++- lib/CorleoneOED/test/lotka_oed.jl | 40 ++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl b/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl index 7aed860a..2f54dae2 100644 --- a/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl +++ b/lib/CorleoneOED/ext/CorleoneOEDOptimizationExtension.jl @@ -316,6 +316,16 @@ function get_integrality(layer::OEDLayer{<:Any, true, false, <:MultipleShootingL return int_u0 end +function get_integrality(layer::OEDLayer{false, true, <:Any, <:SingleShootingLayer}, integer::Bool, ps::ComponentArray, st) + int_u0 = Bool.(ps * 0) + !integer && return int_u0 + subsets = CorleoneOED.__get_subsets(st.index_grid, layer.sampling_indices) + for subset in subsets + int_u0.controls[unique(subset)] .= true + end + return int_u0 +end + function get_integrality(layer::OEDLayer{<:Any, true, <:Any, <:SingleShootingLayer}, integer::Bool, ps::ComponentArray, st) int_u0 = Bool.(ps * 0) !integer && return int_u0 @@ -394,7 +404,7 @@ function Optimization.OptimizationProblem( end function Optimization.OptimizationProblem( - layer::Union{OEDLayer{<:Any, true, true}, MultiExperimentLayer{<:Any, true}}, + layer::Union{OEDLayer{<:Any, true, true, <:SingleShootingLayer}, MultiExperimentLayer{<:Any, true}}, crit::CorleoneOED.AbstractCriterion; AD::Optimization.ADTypes.AbstractADType = AutoForwardDiff(), u0::ComponentVector = ComponentArray(first(LuxCore.setup(Random.default_rng(), layer))), diff --git a/lib/CorleoneOED/test/lotka_oed.jl b/lib/CorleoneOED/test/lotka_oed.jl index 47ff923c..1a208d9a 100644 --- a/lib/CorleoneOED/test/lotka_oed.jl +++ b/lib/CorleoneOED/test/lotka_oed.jl @@ -63,10 +63,48 @@ res = zeros(3) @test [12.0, 12.0, 0.0] == res @test CorleoneOED.get_sampling_sums(oed, nothing, ps, st) == res[1:2] @test_nowarn @inferred CorleoneOED.get_sampling_sums(oed, nothing, ps, st) +shooting_points = [0.0, 3.0, 6.0, 9.0] + +@testset "Integer measurements" begin + for discrete in [true, false] + for integer_sampling in [true, false] + for fixed in [true, false] + for shooting in [true, false] + + shooting && fixed && continue + + oed = shooting ? OEDLayer{discrete}( + prob, Tsit5(), shooting_points, + params = [2, 3], + controls = fixed ? [] : (1 => control, ), + measurements = [ + ControlParameter(collect(tgrid1), controls = ones(length(tgrid1)), bounds = (0.0, 1.0)), + ControlParameter(collect(tgrid2), controls = ones(length(tgrid2)), bounds = (0.0, 1.0)), + ], + observed = (u, p, t) -> u[1:2], + ) : OEDLayer{discrete}( + prob, Tsit5(), + params = [2, 3], + controls = fixed ? [] : (1 => control, ), + measurements = [ + ControlParameter(collect(tgrid1), controls = ones(length(tgrid1)), bounds = (0.0, 1.0)), + ControlParameter(collect(tgrid2), controls = ones(length(tgrid2)), bounds = (0.0, 1.0)), + ], + observed = (u, p, t) -> u[1:2], + ) + + optprob = OptimizationProblem(oed, ACriterion(), M = [4.0, 4.0], integer_weights = integer_sampling) + + @test sum(optprob.int) == (integer_sampling ? length(tgrid1) + length(tgrid2) : 0) + end + end + end + end +end + @testset "Single Experiments" begin - shooting_points = [0.0, 3.0, 6.0, 9.0] multi_layer = MultipleShootingLayer(prob, Tsit5(), shooting_points..., controls = (1 => control,), bounds_p = ([1.0, 1.0], [1.0, 1.0])) for _layer in [layer, multi_layer] From 08d9ee782e1bf143efa1eeeba16182ed97e4d0c3 Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Tue, 12 May 2026 14:56:48 +0200 Subject: [PATCH 31/73] bump version, document integer sampling --- docs/Project.toml | 1 + lib/CorleoneOED/Project.toml | 2 +- .../examples/compartmental_oed/main.jl | 29 +++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/docs/Project.toml b/docs/Project.toml index 0ee74323..c58e4e46 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -9,6 +9,7 @@ DocumenterInterLinks = "d12716ef-a0f6-4df4-a9f1-a5a34e75c656" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +Juniper = "2ddba703-00a4-53a7-87a5-e8b9971dde84" Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" LuxCore = "bb33d45b-7691-41d6-9220-0943567d0623" ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" diff --git a/lib/CorleoneOED/Project.toml b/lib/CorleoneOED/Project.toml index e7843da5..2ca89bdd 100644 --- a/lib/CorleoneOED/Project.toml +++ b/lib/CorleoneOED/Project.toml @@ -1,7 +1,7 @@ name = "CorleoneOED" uuid = "6590a4b1-d036-41fe-a5b4-03a980244101" authors = ["Christoph Plate", "Carl Julius Martensen", "Sebastian Sager"] -version = "0.0.3" +version = "0.0.4" [deps] Corleone = "2751e0db-33e9-4374-b36d-b7219e1e6b40" diff --git a/lib/CorleoneOED/examples/compartmental_oed/main.jl b/lib/CorleoneOED/examples/compartmental_oed/main.jl index 4cb459d4..51e8719f 100644 --- a/lib/CorleoneOED/examples/compartmental_oed/main.jl +++ b/lib/CorleoneOED/examples/compartmental_oed/main.jl @@ -112,3 +112,32 @@ optu = uopt.u + zero(ComponentArray(ps)) sol, _ = oed(nothing, optu, st) plot_oed(sol, optu.controls) + + +# ## Integer sampling decisions + +# Even though optimal solutions to OED problems tend to have integer solutions if the +# discretization grid is chosen adequately, sometimes it might be necessary to enforce +# integrality of the sampling decisions and include it in the problem formulation. This is +# also possible in Corleone. This is possible by passing `integer_weights = true` to the +# OptimizationProblem constructor. + +optprob = OptimizationProblem(oed, DCriterion(); M = [6.0], integer_weights = true) + +# This problem must then be solved by a suitable MINLP solver. In this case, we use Juniper. + +using Juniper + +opt = OptimizationMOI.MOI.OptimizerWithAttributes(Juniper.Optimizer, + "nl_solver" => OptimizationMOI.MOI.OptimizerWithAttributes(Ipopt.Optimizer, + "print_level" => 0, +)) + +sol = solve(optprob, opt) + +# As the relaxed solution was already integer, the same solution is obtained via Juniper. + +optu = uopt.u + zero(ComponentArray(ps)) +sol, _ = oed(nothing, optu, st) + +plot_oed(sol, optu.controls) From d3c7cc31906515a851acba715492233aecb965d2 Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Tue, 12 May 2026 15:02:52 +0200 Subject: [PATCH 32/73] formatting --- lib/CorleoneOED/examples/compartmental_oed/main.jl | 9 ++++++--- lib/CorleoneOED/test/lotka_oed.jl | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/CorleoneOED/examples/compartmental_oed/main.jl b/lib/CorleoneOED/examples/compartmental_oed/main.jl index 51e8719f..f2d7255f 100644 --- a/lib/CorleoneOED/examples/compartmental_oed/main.jl +++ b/lib/CorleoneOED/examples/compartmental_oed/main.jl @@ -128,10 +128,13 @@ optprob = OptimizationProblem(oed, DCriterion(); M = [6.0], integer_weights = tr using Juniper -opt = OptimizationMOI.MOI.OptimizerWithAttributes(Juniper.Optimizer, - "nl_solver" => OptimizationMOI.MOI.OptimizerWithAttributes(Ipopt.Optimizer, +opt = OptimizationMOI.MOI.OptimizerWithAttributes( + Juniper.Optimizer, + "nl_solver" => OptimizationMOI.MOI.OptimizerWithAttributes( + Ipopt.Optimizer, "print_level" => 0, -)) + ) +) sol = solve(optprob, opt) diff --git a/lib/CorleoneOED/test/lotka_oed.jl b/lib/CorleoneOED/test/lotka_oed.jl index 1a208d9a..fb128753 100644 --- a/lib/CorleoneOED/test/lotka_oed.jl +++ b/lib/CorleoneOED/test/lotka_oed.jl @@ -76,7 +76,7 @@ shooting_points = [0.0, 3.0, 6.0, 9.0] oed = shooting ? OEDLayer{discrete}( prob, Tsit5(), shooting_points, params = [2, 3], - controls = fixed ? [] : (1 => control, ), + controls = fixed ? [] : (1 => control,), measurements = [ ControlParameter(collect(tgrid1), controls = ones(length(tgrid1)), bounds = (0.0, 1.0)), ControlParameter(collect(tgrid2), controls = ones(length(tgrid2)), bounds = (0.0, 1.0)), @@ -85,7 +85,7 @@ shooting_points = [0.0, 3.0, 6.0, 9.0] ) : OEDLayer{discrete}( prob, Tsit5(), params = [2, 3], - controls = fixed ? [] : (1 => control, ), + controls = fixed ? [] : (1 => control,), measurements = [ ControlParameter(collect(tgrid1), controls = ones(length(tgrid1)), bounds = (0.0, 1.0)), ControlParameter(collect(tgrid2), controls = ones(length(tgrid2)), bounds = (0.0, 1.0)), From 032c69a6269a61df77ee5a9301d2b9fc0ae6c3ab Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Mon, 18 May 2026 09:54:18 +0200 Subject: [PATCH 33/73] Increase size threshold for documentation generation --- docs/make.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index c1e7c766..a5a79897 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -22,7 +22,7 @@ makedocs( format = Documenter.HTML( assets = ["assets/favicon.ico"], canonical = "https://docs.sciml.ai/Corleone/stable/", - size_threshold = 1_000_000, # bytes + size_threshold = 1_500_000, # bytes ), #DocumenterVitepress.MarkdownVitepress( # repo = "github.com/SciML/Corleone.jl", From 93bdaf4573a5fc16c76765a5ec547fbedcff5113 Mon Sep 17 00:00:00 2001 From: Robert Lampel Date: Thu, 4 Jun 2026 10:12:19 +0200 Subject: [PATCH 34/73] Adjusted bounds and fixed minor errors --- lib/OptimalControlBenchmarks/src/problems/quadrotor.jl | 1 + lib/OptimalControlBenchmarks/src/problems/rao_mease.jl | 2 +- lib/OptimalControlBenchmarks/src/problems/three_tank.jl | 8 ++++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl b/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl index 8b62ba59..e7b22e46 100644 --- a/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl +++ b/lib/OptimalControlBenchmarks/src/problems/quadrotor.jl @@ -44,6 +44,7 @@ function quadrotor(grids) grid_cons_x₃..., ] + t_f = last(tspan) costs = [ 5 * (x₁(t_f) - 6)^2 + 5 * (x₃(t_f) - 1)^2 + 5 * (sin(x₅(t_f) * 0.5))^2 + Symbolics.Integral(t in (0.0, last(tspan)))( 5 * u(t)^2 diff --git a/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl b/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl index 46d3d46a..d3c52fcd 100644 --- a/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl +++ b/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl @@ -2,7 +2,7 @@ function rao_mease(grids) num_states = 2 num_controls = 1 - tspan = (0.0, 10) + tspan = (0.0, 10.) scaled_grids = scale_grids!(tspan, grids) diff --git a/lib/OptimalControlBenchmarks/src/problems/three_tank.jl b/lib/OptimalControlBenchmarks/src/problems/three_tank.jl index 7527f0aa..74cefa53 100644 --- a/lib/OptimalControlBenchmarks/src/problems/three_tank.jl +++ b/lib/OptimalControlBenchmarks/src/problems/three_tank.jl @@ -35,8 +35,16 @@ function three_tank(grids) grid_cons_w = [w₁(tᵢ) + w₂(tᵢ) + w₃(tᵢ) ~ 1.0 for tᵢ in constraint_grid] + # ensure that the state variables remain positive + grid_cons_x₁ = [x₁(tᵢ) ≳ 0. for tᵢ in constraint_grid] + grid_cons_x₂ = [x₂(tᵢ) ≳ 0. for tᵢ in constraint_grid] + grid_cons_x₃ = [x₃(tᵢ) ≳ 0. for tᵢ in constraint_grid] + cons = [ grid_cons_w..., + grid_cons_x₁..., + grid_cons_x₂..., + grid_cons_x₃..., ] costs = [ From 87de01f97caf5b6432d66ea7d76888c3169079f3 Mon Sep 17 00:00:00 2001 From: Robert Lampel Date: Thu, 4 Jun 2026 10:32:32 +0200 Subject: [PATCH 35/73] formatting Co-Authored-By: Claude Sonnet 4.6 --- lib/OptimalControlBenchmarks/src/problems/rao_mease.jl | 4 ++-- lib/OptimalControlBenchmarks/src/problems/three_tank.jl | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl b/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl index d3c52fcd..8641030a 100644 --- a/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl +++ b/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl @@ -2,8 +2,8 @@ function rao_mease(grids) num_states = 2 num_controls = 1 - tspan = (0.0, 10.) - + tspan = (0.0, 10.0) + scaled_grids = scale_grids!(tspan, grids) @variables begin diff --git a/lib/OptimalControlBenchmarks/src/problems/three_tank.jl b/lib/OptimalControlBenchmarks/src/problems/three_tank.jl index 74cefa53..7fb8bfa9 100644 --- a/lib/OptimalControlBenchmarks/src/problems/three_tank.jl +++ b/lib/OptimalControlBenchmarks/src/problems/three_tank.jl @@ -36,9 +36,9 @@ function three_tank(grids) grid_cons_w = [w₁(tᵢ) + w₂(tᵢ) + w₃(tᵢ) ~ 1.0 for tᵢ in constraint_grid] # ensure that the state variables remain positive - grid_cons_x₁ = [x₁(tᵢ) ≳ 0. for tᵢ in constraint_grid] - grid_cons_x₂ = [x₂(tᵢ) ≳ 0. for tᵢ in constraint_grid] - grid_cons_x₃ = [x₃(tᵢ) ≳ 0. for tᵢ in constraint_grid] + grid_cons_x₁ = [x₁(tᵢ) ≳ 0.0 for tᵢ in constraint_grid] + grid_cons_x₂ = [x₂(tᵢ) ≳ 0.0 for tᵢ in constraint_grid] + grid_cons_x₃ = [x₃(tᵢ) ≳ 0.0 for tᵢ in constraint_grid] cons = [ grid_cons_w..., From 3ab616dd1e36ae472ecb06d7f57ca4a95ba908ad Mon Sep 17 00:00:00 2001 From: Robert Lampel Date: Thu, 4 Jun 2026 10:40:04 +0200 Subject: [PATCH 36/73] formatting Co-Authored-By: Claude Sonnet 4.6 --- lib/OptimalControlBenchmarks/src/problems/rao_mease.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl b/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl index 8641030a..3837f3fa 100644 --- a/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl +++ b/lib/OptimalControlBenchmarks/src/problems/rao_mease.jl @@ -3,7 +3,7 @@ function rao_mease(grids) num_states = 2 num_controls = 1 tspan = (0.0, 10.0) - + scaled_grids = scale_grids!(tspan, grids) @variables begin From 3f9d5db1216f5256f3225e1f297549e5e9d81425 Mon Sep 17 00:00:00 2001 From: Robert Lampel Date: Thu, 4 Jun 2026 11:26:36 +0200 Subject: [PATCH 37/73] formatting Co-Authored-By: Claude Sonnet 4.6 --- lib/CorleoneOED/test/lotka_oed.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/CorleoneOED/test/lotka_oed.jl b/lib/CorleoneOED/test/lotka_oed.jl index fb128753..0065bab0 100644 --- a/lib/CorleoneOED/test/lotka_oed.jl +++ b/lib/CorleoneOED/test/lotka_oed.jl @@ -103,7 +103,6 @@ shooting_points = [0.0, 3.0, 6.0, 9.0] end - @testset "Single Experiments" begin multi_layer = MultipleShootingLayer(prob, Tsit5(), shooting_points..., controls = (1 => control,), bounds_p = ([1.0, 1.0], [1.0, 1.0])) From e4b8bcb34a8ae2d3d4a674b3f52cd1c49a2b58c8 Mon Sep 17 00:00:00 2001 From: Robert Lampel Date: Thu, 4 Jun 2026 11:32:40 +0200 Subject: [PATCH 38/73] formatting Co-Authored-By: Claude Sonnet 4.6 --- ext/MTKExtension/optimal_control.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/MTKExtension/optimal_control.jl b/ext/MTKExtension/optimal_control.jl index ba14d176..649a4abe 100644 --- a/ext/MTKExtension/optimal_control.jl +++ b/ext/MTKExtension/optimal_control.jl @@ -111,7 +111,7 @@ function Corleone.CorleoneDynamicOptProblem( # We currently assume a single cost costbody = SymbolicUtils.Code.toexpr(substitute(only(newcosts), vars_subs)) - costfn = :(($(args_...),) -> $costbody) + costfn = :(($(args_...)) -> $costbody) costfun = @RuntimeGeneratedFunction( costfn From 7b6a6661b8d45927aad97ebd58a4a38c3701ef5f Mon Sep 17 00:00:00 2001 From: Robert Lampel Date: Thu, 4 Jun 2026 11:55:27 +0200 Subject: [PATCH 39/73] Use Expr to build costfn lambda, avoiding trailing comma Co-Authored-By: Claude Sonnet 4.6 --- ext/MTKExtension/optimal_control.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/MTKExtension/optimal_control.jl b/ext/MTKExtension/optimal_control.jl index 649a4abe..fe16cf6d 100644 --- a/ext/MTKExtension/optimal_control.jl +++ b/ext/MTKExtension/optimal_control.jl @@ -111,7 +111,7 @@ function Corleone.CorleoneDynamicOptProblem( # We currently assume a single cost costbody = SymbolicUtils.Code.toexpr(substitute(only(newcosts), vars_subs)) - costfn = :(($(args_...)) -> $costbody) + costfn = Expr(:->, Expr(:tuple, args_...), costbody) costfun = @RuntimeGeneratedFunction( costfn From 7bc5d59ad9586bd71b190d13295f23aaf03befb5 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Thu, 4 Jun 2026 08:07:08 -0400 Subject: [PATCH 40/73] Re-enable downgrade CI with genuine test-at-floor (allow_reresolve: false) --- .github/workflows/Downgrade.yml | 1 - Project.toml | 14 +++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/Downgrade.yml b/.github/workflows/Downgrade.yml index b4ef4779..df6e588e 100644 --- a/.github/workflows/Downgrade.yml +++ b/.github/workflows/Downgrade.yml @@ -12,7 +12,6 @@ on: - 'docs/**' jobs: test: - if: false # Disabled: waiting on dependency updates. See #586 runs-on: ubuntu-latest strategy: matrix: diff --git a/Project.toml b/Project.toml index 69b10da8..99bc30cb 100644 --- a/Project.toml +++ b/Project.toml @@ -31,10 +31,10 @@ CorleoneModelingToolkitExtension = "ModelingToolkit" [compat] Aqua = "0.8" -ChainRulesCore = "1" +ChainRulesCore = "1.25.1" ComponentArrays = "0.15" Distributed = "1" -DocStringExtensions = "0.9" +DocStringExtensions = "0.9.3" ForwardDiff = "1.2" Functors = "0.5" Ipopt = "1.10" @@ -46,16 +46,16 @@ ModelingToolkit = "11" OhMyThreads = "0.8" Optimization = "5" OptimizationMOI = "1" -OrdinaryDiffEqBDF = "1" +OrdinaryDiffEqBDF = "1.14.0" OrdinaryDiffEqTsit5 = "1" Random = "1" -RecursiveArrayTools = "3.30" -Reexport = "1.2" +RecursiveArrayTools = "3.37.0" +Reexport = "1.2.2" SafeTestsets = "0.1" -SciMLBase = "2.110" +SciMLBase = "2.141.0" SciMLSensitivity = "7.87" SciMLStructures = "1.7" -SymbolicIndexingInterface = "0.3" +SymbolicIndexingInterface = "0.3.43" Test = "1" julia = "1.10" From 9f8d93630710362a2a54b79d4db4e9ea83a86016 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Thu, 4 Jun 2026 10:53:22 -0400 Subject: [PATCH 41/73] ci: add centralized sublibrary downgrade CI Add a DowngradeSublibraries.yml workflow that calls the reusable SciML/.github sublibrary-downgrade.yml@v1 workflow to run downgrade tests for each lib/* package. Auto-discovers all lib/* packages with a Project.toml and skips stdlibs plus the in-repo Corleone core package. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/DowngradeSublibraries.yml | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/DowngradeSublibraries.yml diff --git a/.github/workflows/DowngradeSublibraries.yml b/.github/workflows/DowngradeSublibraries.yml new file mode 100644 index 00000000..552169de --- /dev/null +++ b/.github/workflows/DowngradeSublibraries.yml @@ -0,0 +1,23 @@ +name: Downgrade Sublibraries +on: + pull_request: + branches: + - main + paths-ignore: + - 'docs/**' + push: + branches: + - main + paths-ignore: + - 'docs/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} +jobs: + downgrade-sublibraries: + uses: "SciML/.github/.github/workflows/sublibrary-downgrade.yml@v1" + secrets: "inherit" + with: + julia-version: "lts" + skip: "Pkg,TOML,Statistics,LinearAlgebra,SparseArrays,InteractiveUtils,Random,Test,Corleone" + allow-reresolve: true From 84e4b97528781c99f543c6b3db9a20b3357c1b23 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Thu, 4 Jun 2026 11:14:35 -0400 Subject: [PATCH 42/73] ci: route CI through SciML/.github reusable workflows (@v1) Convert bespoke GitHub Actions workflows into thin callers of the reusable workflows in SciML/.github, preserving the repo's existing config (Julia version matrix, downgrade settings, coverage dirs, triggers, default branch). - CI.yml: main-package tests via tests.yml@v1 (versions 1.11, lts; coverage dirs src,lib/CorleoneOED/src) - SublibraryCI.yml (new): consolidate sublibrary testing via sublibrary-tests.yml@v1 (auto-discovers lib/*) - Downgrade.yml: downgrade.yml@v1 (julia 1.10, skip Pkg,TOML, allow-reresolve false) - FormatCheck.yml: runic.yml@v1 - SpellCheck.yml: spellcheck.yml@v1 - Documentation.yml already reusable: left untouched - TagBot.yml, CompatHelper.yml: left local (non-CI automation) Co-Authored-By: Chris Rackauckas --- .github/workflows/CI.yml | 69 +++++------------------------- .github/workflows/Downgrade.yml | 24 +++-------- .github/workflows/FormatCheck.yml | 11 ++--- .github/workflows/SpellCheck.yml | 12 ++---- .github/workflows/SublibraryCI.yml | 15 +++++++ 5 files changed, 38 insertions(+), 93 deletions(-) create mode 100644 .github/workflows/SublibraryCI.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index cae2d87d..ff706a4d 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -10,69 +10,20 @@ on: - main paths-ignore: - 'docs/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref_name != github.event.repository.default_branch || github.ref != 'refs/tags/v*' }} jobs: - test: - runs-on: ubuntu-latest - continue-on-error: ${{ matrix.group == 'Downstream' }} + tests: + name: "Tests" strategy: fail-fast: false matrix: - group: - - Corleone - - CorleoneOED version: - '1.11' - 'lts' - steps: - - uses: actions/checkout@v6 - - uses: julia-actions/setup-julia@v2 - with: - version: ${{ matrix.version }} - - uses: actions/cache@v5 - env: - cache-name: cache-artifacts - with: - path: ~/.julia/artifacts - key: ${{ runner.os }}-test-${{ matrix.version }}-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }} - restore-keys: | - ${{ runner.os }}-test-${{ matrix.version }}-${{ env.cache-name }}- - ${{ runner.os }}-test-${{ matrix.version }}- - ${{ runner.os }}- - - uses: julia-actions/julia-buildpkg@v1 - - name: ${{ matrix.group }} - env: - GROUP: ${{ matrix.group }} - shell: julia --color=yes --check-bounds=yes --depwarn=yes {0} - run: | - using Pkg - const GROUP = get(ENV, "GROUP", "Core") - - function dev_subpkg(subpkgs::Vector{String}) - specs = [PackageSpec(path = "lib/$subpkg") for subpkg in subpkgs] - Pkg.develop(specs) - end - - if GROUP == "Corleone" - Pkg.activate(".") - else - subpkg_path = "lib/${{ matrix.group }}" - Pkg.activate(subpkg_path) - end - - if VERSION < v"1.11" - @info "Preparing env" - if GROUP == "Corleone" - @info "Testing Corleone" - elseif GROUP == "CorleoneOED" - dev_subpkg([".."]) - end - end - - @info "Starting tests" - Pkg.test() - - uses: julia-actions/julia-processcoverage@v1 - with: - directories: src,lib/CorleoneOED/src - - uses: codecov/codecov-action@v5 - with: - files: lcov.info \ No newline at end of file + uses: "SciML/.github/.github/workflows/tests.yml@v1" + with: + julia-version: "${{ matrix.version }}" + coverage-directories: "src,lib/CorleoneOED/src" + secrets: "inherit" diff --git a/.github/workflows/Downgrade.yml b/.github/workflows/Downgrade.yml index df6e588e..677ec28b 100644 --- a/.github/workflows/Downgrade.yml +++ b/.github/workflows/Downgrade.yml @@ -12,20 +12,10 @@ on: - 'docs/**' jobs: test: - runs-on: ubuntu-latest - strategy: - matrix: - downgrade_mode: ['alldeps'] - julia-version: ['1.10'] - steps: - - uses: actions/checkout@v6 - - uses: julia-actions/setup-julia@v2 - with: - version: ${{ matrix.julia-version }} - - uses: julia-actions/julia-downgrade-compat@v2 - with: - skip: Pkg,TOML - - uses: julia-actions/julia-buildpkg@v1 - - uses: julia-actions/julia-runtest@v1 - with: - ALLOW_RERESOLVE: false \ No newline at end of file + name: "Downgrade" + uses: "SciML/.github/.github/workflows/downgrade.yml@v1" + with: + julia-version: "1.10" + skip: "Pkg,TOML" + allow-reresolve: false + secrets: "inherit" diff --git a/.github/workflows/FormatCheck.yml b/.github/workflows/FormatCheck.yml index 6bc43919..7ac23235 100644 --- a/.github/workflows/FormatCheck.yml +++ b/.github/workflows/FormatCheck.yml @@ -1,5 +1,4 @@ name: format-check - on: push: branches: @@ -8,12 +7,8 @@ on: - 'release-' tags: '*' pull_request: - jobs: runic: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: fredrikekre/runic-action@v1 - with: - version: '1' \ No newline at end of file + name: "Runic" + uses: "SciML/.github/.github/workflows/runic.yml@v1" + secrets: "inherit" diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml index d6965c15..16d404b7 100644 --- a/.github/workflows/SpellCheck.yml +++ b/.github/workflows/SpellCheck.yml @@ -1,13 +1,7 @@ name: Spell Check - on: [pull_request] - jobs: typos-check: - name: Spell Check with Typos - runs-on: ubuntu-latest - steps: - - name: Checkout Actions Repository - uses: actions/checkout@v6 - - name: Check spelling - uses: crate-ci/typos@v1.45.2 \ No newline at end of file + name: "Spell Check with Typos" + uses: "SciML/.github/.github/workflows/spellcheck.yml@v1" + secrets: "inherit" diff --git a/.github/workflows/SublibraryCI.yml b/.github/workflows/SublibraryCI.yml new file mode 100644 index 00000000..e0b925b2 --- /dev/null +++ b/.github/workflows/SublibraryCI.yml @@ -0,0 +1,15 @@ +name: Sublibrary CI +on: + pull_request: + branches: + - main + push: + branches: + - main +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} +jobs: + sublibrary-ci: + uses: "SciML/.github/.github/workflows/sublibrary-tests.yml@v1" + secrets: "inherit" From 48b4dbd73fae1abb3664102c1e879075ae67824b Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Thu, 4 Jun 2026 12:01:58 -0400 Subject: [PATCH 43/73] ci: add sublibrary test_groups.toml so centralized SublibraryCI runs all groups Keep the centralized SciML sublibrary-tests.yml@v1 form (SublibraryCI.yml is untouched) and make every sublibrary group/version actually run. The reusable workflow runs the ROOT test suite with GROUP set to a sublibrary's CI group ("" for the Core group, "_" for a named group), but the root test/runtests.jl had no GROUP dispatch, so sublibrary legs silently ran the main-package suite instead of the sublibrary tests. Fix it by: - Adding a GROUP dispatch to test/runtests.jl that, when GROUP names a lib/, activates that sublibrary's test env, develops its local [sources] path deps on Julia < 1.11, and runs Pkg.test() forwarding GROUP. GROUP="All"/"Corleone" keep running the main Corleone suite. - Adding lib/CorleoneOED/test/test_groups.toml ([Core] versions ["1.11","lts"]) and lib/OptimalControlBenchmarks/test/test_groups.toml (same), encoding the groups/versions the old bespoke CI ran. - Adding the GROUP-parsing shim to each sublibrary runtests.jl so GROUP="" maps to Core and "_" maps to , driving the existing dispatch; "All" still works for local runs. Verified by running SciML/.github compute_affected_sublibraries.jl: a change to lib/CorleoneOED/src emits CorleoneOED on 1.11 and lts; lib/OptimalControlBenchmarks emits OptimalControlBenchmarks on 1.11 and lts. The root dispatch + shim then runs the full CorleoneOED suite (1D, Lotka, Lotka SVD) under GROUP=CorleoneOED. Co-Authored-By: Chris Rackauckas --- lib/CorleoneOED/test/runtests.jl | 25 ++++-- lib/CorleoneOED/test/test_groups.toml | 2 + lib/OptimalControlBenchmarks/test/runtests.jl | 11 ++- .../test/test_groups.toml | 2 + test/runtests.jl | 81 +++++++++++++++---- 5 files changed, 95 insertions(+), 26 deletions(-) create mode 100644 lib/CorleoneOED/test/test_groups.toml create mode 100644 lib/OptimalControlBenchmarks/test/test_groups.toml diff --git a/lib/CorleoneOED/test/runtests.jl b/lib/CorleoneOED/test/runtests.jl index 0095a017..226f2941 100644 --- a/lib/CorleoneOED/test/runtests.jl +++ b/lib/CorleoneOED/test/runtests.jl @@ -1,12 +1,21 @@ using CorleoneOED using SafeTestsets -@safetestset "1D Example" begin - include("1d_oed.jl") -end -@safetestset "Lotka Volterra" begin - include("lotka_oed.jl") -end -@safetestset "Lotka Volterra SVD" begin - include("lotka_oed_svd.jl") +# Centralized sublibrary CI emits GROUP as the bare package name (-> "Core") or +# "_" (-> ""). Map it to the bare section names this file +# dispatches on. GROUP="All" keeps local `Pkg.test()` runs running everything. +const _G = get(ENV, "GROUP", "All") +const _SUB = "CorleoneOED" +const GROUP = _G == _SUB ? "Core" : (startswith(_G, _SUB * "_") ? _G[(length(_SUB) + 2):end] : _G) + +if GROUP == "All" || GROUP == "Core" + @safetestset "1D Example" begin + include("1d_oed.jl") + end + @safetestset "Lotka Volterra" begin + include("lotka_oed.jl") + end + @safetestset "Lotka Volterra SVD" begin + include("lotka_oed_svd.jl") + end end diff --git a/lib/CorleoneOED/test/test_groups.toml b/lib/CorleoneOED/test/test_groups.toml new file mode 100644 index 00000000..10837a88 --- /dev/null +++ b/lib/CorleoneOED/test/test_groups.toml @@ -0,0 +1,2 @@ +[Core] +versions = ["1.11", "lts"] diff --git a/lib/OptimalControlBenchmarks/test/runtests.jl b/lib/OptimalControlBenchmarks/test/runtests.jl index d3ee58d7..75929598 100644 --- a/lib/OptimalControlBenchmarks/test/runtests.jl +++ b/lib/OptimalControlBenchmarks/test/runtests.jl @@ -1,4 +1,13 @@ using OptimalControlBenchmarks using Test -@test 1 == 1 +# Centralized sublibrary CI emits GROUP as the bare package name (-> "Core") or +# "_" (-> ""). Map it to the bare section names this file +# dispatches on. GROUP="All" keeps local `Pkg.test()` runs running everything. +const _G = get(ENV, "GROUP", "All") +const _SUB = "OptimalControlBenchmarks" +const GROUP = _G == _SUB ? "Core" : (startswith(_G, _SUB * "_") ? _G[(length(_SUB) + 2):end] : _G) + +if GROUP == "All" || GROUP == "Core" + @test 1 == 1 +end diff --git a/lib/OptimalControlBenchmarks/test/test_groups.toml b/lib/OptimalControlBenchmarks/test/test_groups.toml new file mode 100644 index 00000000..10837a88 --- /dev/null +++ b/lib/OptimalControlBenchmarks/test/test_groups.toml @@ -0,0 +1,2 @@ +[Core] +versions = ["1.11", "lts"] diff --git a/test/runtests.jl b/test/runtests.jl index d4c99249..4869fd9a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,28 +1,75 @@ +using Pkg using Test -using JET using SafeTestsets -@testset "Corleone.jl" begin - @safetestset "Code quality (Aqua.jl)" begin - using Aqua - using Corleone - Aqua.test_all(Corleone) +const GROUP = get(ENV, "GROUP", "All") + +# Centralized sublibrary CI (SciML/.github sublibrary-tests.yml@v1) runs this +# root test suite with GROUP set to a sublibrary's CI group: the bare sublibrary +# name selects that sublibrary's "Core" group, and "_" selects a +# named group. Detect those here, activate the sublibrary's own test environment, +# and hand off to its runtests.jl (which parses GROUP itself). GROUP="All" (the +# local default) and GROUP="Corleone" run the main-package suite below. +const LIB_DIR = joinpath(@__DIR__, "..", "lib") + +function _detect_sublibrary(group, lib_dir) + isdir(joinpath(lib_dir, group)) && return group + for i in length(group):-1:1 + if group[i] == '_' && isdir(joinpath(lib_dir, group[1:(i - 1)])) + return group[1:(i - 1)] + end end - @safetestset "Local controls" begin - include("local_controls.jl") + return nothing +end + +const _SUBLIB = _detect_sublibrary(GROUP, LIB_DIR) + +if _SUBLIB !== nothing + sublib_path = joinpath(LIB_DIR, _SUBLIB) + Pkg.activate(sublib_path) + # On Julia < 1.11 the [sources] table in Project.toml is ignored, so develop + # the local path dependencies (e.g. Corleone) to test the PR branch code. + if VERSION < v"1.11.0-DEV.0" + toml = Pkg.TOML.parsefile(joinpath(sublib_path, "Project.toml")) + if haskey(toml, "sources") + specs = Pkg.PackageSpec[] + for (dep, spec) in toml["sources"] + if spec isa Dict && haskey(spec, "path") + dep_path = normpath(joinpath(sublib_path, spec["path"])) + isdir(dep_path) && push!(specs, Pkg.PackageSpec(path = dep_path)) + end + end + isempty(specs) || Pkg.develop(specs) + end end - @safetestset "Multiple shooting" begin - include("multiple_shooting.jl") + # Forward GROUP unchanged; the sublibrary runtests.jl parses it. + withenv("GROUP" => GROUP) do + Pkg.test(_SUBLIB; allow_reresolve = true) end - @testset "Examples" begin - @safetestset "Lotka" begin - include("examples/lotka_oc.jl") +else + using JET + @testset "Corleone.jl" begin + @safetestset "Code quality (Aqua.jl)" begin + using Aqua + using Corleone + Aqua.test_all(Corleone) + end + @safetestset "Local controls" begin + include("local_controls.jl") end - @safetestset "Lotka MS" begin - include("examples/lotka_ms.jl") + @safetestset "Multiple shooting" begin + include("multiple_shooting.jl") end - @safetestset "Lotka MTK" begin - include("examples/mtk.jl") + @testset "Examples" begin + @safetestset "Lotka" begin + include("examples/lotka_oc.jl") + end + @safetestset "Lotka MS" begin + include("examples/lotka_ms.jl") + end + @safetestset "Lotka MTK" begin + include("examples/mtk.jl") + end end end end From d05f63a2084244c1325d83668af8f81940075a5d Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Thu, 4 Jun 2026 12:38:11 -0400 Subject: [PATCH 44/73] ci: standardize sublibrary test groups to Core/QA Replace the bespoke per-sublibrary test_groups.toml (which only declared a narrowed [Core] versions = ["1.11", "lts"] matrix and suppressed QA entirely) with the SciML standard Core/QA groups. Both CorleoneOED and OptimalControlBenchmarks are small suites with no GPU tests and no isolated-env/timeout need, so deleting test_groups.toml lets the reusable workflow's defaults apply: Core on [lts, 1.11, 1, pre] and QA on [1]. The sublibrary runtests.jl now dispatch on the parsed standard group: - Core (+All): functional/correctness tests (folds the old Core). - QA (+All): Aqua.test_all (Aqua was already a declared but unused test dep). The GROUP prefix-strip shim is kept so the centralized sublibrary-tests.yml@v1 GROUP="" -> Core and GROUP="_QA" -> QA mapping drives dispatch, while GROUP="All" (local Pkg.test default) still runs everything. No GPU tests exist, so no [GPU] test_groups.toml is added. These sublibraries never had macOS CI legs, so none were dropped. Co-Authored-By: Chris Rackauckas --- lib/CorleoneOED/test/runtests.jl | 12 ++++++++++-- lib/CorleoneOED/test/test_groups.toml | 2 -- lib/OptimalControlBenchmarks/test/runtests.jl | 13 +++++++++++-- lib/OptimalControlBenchmarks/test/test_groups.toml | 2 -- 4 files changed, 21 insertions(+), 8 deletions(-) delete mode 100644 lib/CorleoneOED/test/test_groups.toml delete mode 100644 lib/OptimalControlBenchmarks/test/test_groups.toml diff --git a/lib/CorleoneOED/test/runtests.jl b/lib/CorleoneOED/test/runtests.jl index 226f2941..4056d223 100644 --- a/lib/CorleoneOED/test/runtests.jl +++ b/lib/CorleoneOED/test/runtests.jl @@ -2,8 +2,8 @@ using CorleoneOED using SafeTestsets # Centralized sublibrary CI emits GROUP as the bare package name (-> "Core") or -# "_" (-> ""). Map it to the bare section names this file -# dispatches on. GROUP="All" keeps local `Pkg.test()` runs running everything. +# "_" (-> ""). Map it to the standard Core/QA section names this +# file dispatches on. GROUP="All" keeps local `Pkg.test()` runs running everything. const _G = get(ENV, "GROUP", "All") const _SUB = "CorleoneOED" const GROUP = _G == _SUB ? "Core" : (startswith(_G, _SUB * "_") ? _G[(length(_SUB) + 2):end] : _G) @@ -19,3 +19,11 @@ if GROUP == "All" || GROUP == "Core" include("lotka_oed_svd.jl") end end + +if GROUP == "All" || GROUP == "QA" + @safetestset "Code quality (Aqua.jl)" begin + using Aqua + using CorleoneOED + Aqua.test_all(CorleoneOED) + end +end diff --git a/lib/CorleoneOED/test/test_groups.toml b/lib/CorleoneOED/test/test_groups.toml deleted file mode 100644 index 10837a88..00000000 --- a/lib/CorleoneOED/test/test_groups.toml +++ /dev/null @@ -1,2 +0,0 @@ -[Core] -versions = ["1.11", "lts"] diff --git a/lib/OptimalControlBenchmarks/test/runtests.jl b/lib/OptimalControlBenchmarks/test/runtests.jl index 75929598..4b730a0b 100644 --- a/lib/OptimalControlBenchmarks/test/runtests.jl +++ b/lib/OptimalControlBenchmarks/test/runtests.jl @@ -1,9 +1,10 @@ using OptimalControlBenchmarks using Test +using SafeTestsets # Centralized sublibrary CI emits GROUP as the bare package name (-> "Core") or -# "_" (-> ""). Map it to the bare section names this file -# dispatches on. GROUP="All" keeps local `Pkg.test()` runs running everything. +# "_" (-> ""). Map it to the standard Core/QA section names this +# file dispatches on. GROUP="All" keeps local `Pkg.test()` runs running everything. const _G = get(ENV, "GROUP", "All") const _SUB = "OptimalControlBenchmarks" const GROUP = _G == _SUB ? "Core" : (startswith(_G, _SUB * "_") ? _G[(length(_SUB) + 2):end] : _G) @@ -11,3 +12,11 @@ const GROUP = _G == _SUB ? "Core" : (startswith(_G, _SUB * "_") ? _G[(length(_SU if GROUP == "All" || GROUP == "Core" @test 1 == 1 end + +if GROUP == "All" || GROUP == "QA" + @safetestset "Code quality (Aqua.jl)" begin + using Aqua + using OptimalControlBenchmarks + Aqua.test_all(OptimalControlBenchmarks) + end +end diff --git a/lib/OptimalControlBenchmarks/test/test_groups.toml b/lib/OptimalControlBenchmarks/test/test_groups.toml deleted file mode 100644 index 10837a88..00000000 --- a/lib/OptimalControlBenchmarks/test/test_groups.toml +++ /dev/null @@ -1,2 +0,0 @@ -[Core] -versions = ["1.11", "lts"] From 541299dee5d9f490d7deb20335e0636e7b396fcb Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Thu, 4 Jun 2026 21:10:12 -0400 Subject: [PATCH 45/73] ci(SublibraryCI): test each lib/* via project-model tests.yml@v1 caller Switch SublibraryCI from the GROUP-dispatch sublibrary-tests.yml@v1 (which needs a root runtests.jl sublib dispatcher) to the project model: list lib/* and run each as a tests.yml@v1 caller with project: lib/X. The reusable workflow transitively git-develops the sublib's in-repo [sources] deps, so no per-repo dispatcher is needed. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/SublibraryCI.yml | 34 ++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/.github/workflows/SublibraryCI.yml b/.github/workflows/SublibraryCI.yml index e0b925b2..96f51d72 100644 --- a/.github/workflows/SublibraryCI.yml +++ b/.github/workflows/SublibraryCI.yml @@ -1,15 +1,45 @@ name: Sublibrary CI + on: pull_request: branches: - main + paths-ignore: + - 'docs/**' push: branches: - main + paths-ignore: + - 'docs/**' + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} + jobs: - sublibrary-ci: - uses: "SciML/.github/.github/workflows/sublibrary-tests.yml@v1" + list-sublibraries: + runs-on: ubuntu-latest + outputs: + projects: ${{ steps.list.outputs.projects }} + steps: + - uses: actions/checkout@v6 + - name: "List lib/* sublibraries" + id: list + run: | + projects=$(for d in lib/*/; do d="${d%/}"; [ -f "$d/Project.toml" ] && echo "$d"; done \ + | python3 -c 'import sys,json; print(json.dumps([l.strip() for l in sys.stdin if l.strip()]))') + echo "Sublibraries: $projects" + echo "projects=$projects" >> "$GITHUB_OUTPUT" + + test: + needs: list-sublibraries + strategy: + fail-fast: false + matrix: + project: ${{ fromJson(needs.list-sublibraries.outputs.projects) }} + name: "Sublibrary - ${{ matrix.project }}" + uses: "SciML/.github/.github/workflows/tests.yml@v1" + with: + project: "${{ matrix.project }}" + coverage: false secrets: "inherit" From 0bd40f7456cda4a67fb496d8372994cbd4e0f6a9 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Thu, 4 Jun 2026 22:01:27 -0400 Subject: [PATCH 46/73] ci(SublibraryCI): collect sublibrary coverage (point dirs at lib/X) Drop coverage: false (cargo-culted from OrdinaryDiffEq, which disables it only to avoid Codecov flooding at 56-sublibrary scale). For this repo the sublib tests are the only thing covering the sublib's src, so collect it -- with coverage-directories pointed at the sublibrary (lib/X/src,ext) rather than the repo-root default (src,ext). Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/SublibraryCI.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/SublibraryCI.yml b/.github/workflows/SublibraryCI.yml index 96f51d72..e978424d 100644 --- a/.github/workflows/SublibraryCI.yml +++ b/.github/workflows/SublibraryCI.yml @@ -41,5 +41,7 @@ jobs: uses: "SciML/.github/.github/workflows/tests.yml@v1" with: project: "${{ matrix.project }}" - coverage: false + # collect the sublibrary's own source coverage (its tests are the only thing + # that exercises it); point processcoverage at the sublibrary, not repo root. + coverage-directories: "${{ matrix.project }}/src,${{ matrix.project }}/ext" secrets: "inherit" From a195e74614de11612e9c46c24bd469de2d13f5d3 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Thu, 4 Jun 2026 22:06:17 -0400 Subject: [PATCH 47/73] ci(SublibraryCI): build the matrix JSON with jq, not python3 Don't assume python3 is on the runner. jq is preinstalled on GitHub-hosted ubuntu runners (where the list-sublibraries job runs) and builds the JSON array directly. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/SublibraryCI.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/SublibraryCI.yml b/.github/workflows/SublibraryCI.yml index e978424d..d96e4e67 100644 --- a/.github/workflows/SublibraryCI.yml +++ b/.github/workflows/SublibraryCI.yml @@ -26,8 +26,10 @@ jobs: - name: "List lib/* sublibraries" id: list run: | + # jq is preinstalled on GitHub-hosted runners; -R reads each path as a + # JSON string, -s slurps them into a compact array (-> [] when empty). projects=$(for d in lib/*/; do d="${d%/}"; [ -f "$d/Project.toml" ] && echo "$d"; done \ - | python3 -c 'import sys,json; print(json.dumps([l.strip() for l in sys.stdin if l.strip()]))') + | jq -R . | jq -sc .) echo "Sublibraries: $projects" echo "projects=$projects" >> "$GITHUB_OUTPUT" From 33f4598fc1b5e228d49157a207cd4c1930f476e0 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Thu, 4 Jun 2026 22:11:55 -0400 Subject: [PATCH 48/73] ci(SublibraryCI): build matrix JSON in pure shell (no jq/python3) jq isn't guaranteed on self-hosted runners any more than python3 is. lib/* directory names have no JSON-special characters, so build the array with plain shell quoting and depend on neither. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/SublibraryCI.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/SublibraryCI.yml b/.github/workflows/SublibraryCI.yml index d96e4e67..7a36f948 100644 --- a/.github/workflows/SublibraryCI.yml +++ b/.github/workflows/SublibraryCI.yml @@ -26,10 +26,16 @@ jobs: - name: "List lib/* sublibraries" id: list run: | - # jq is preinstalled on GitHub-hosted runners; -R reads each path as a - # JSON string, -s slurps them into a compact array (-> [] when empty). - projects=$(for d in lib/*/; do d="${d%/}"; [ -f "$d/Project.toml" ] && echo "$d"; done \ - | jq -R . | jq -sc .) + # Build the JSON array in pure shell -- no jq/python3, since neither is + # guaranteed on a self-hosted runner. lib/* names contain no JSON-special + # characters, so plain quoting is safe. + projects="" + for d in lib/*/; do + d="${d%/}" + [ -f "$d/Project.toml" ] || continue + if [ -z "$projects" ]; then projects="\"$d\""; else projects="$projects,\"$d\""; fi + done + projects="[$projects]" echo "Sublibraries: $projects" echo "projects=$projects" >> "$GITHUB_OUTPUT" From 37eebf89286609431376756b728134f432542526 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Thu, 4 Jun 2026 22:21:02 -0400 Subject: [PATCH 49/73] ci(SublibraryCI): call centralized sublibrary-project-tests.yml@v1 The list-lib/*-and-matrix logic moved into the reusable sublibrary-project-tests.yml in SciML/.github, so this caller collapses to just the branch triggers, concurrency, and a single `uses:`. No duplicated detection logic per repo. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/SublibraryCI.yml | 37 ++---------------------------- 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/.github/workflows/SublibraryCI.yml b/.github/workflows/SublibraryCI.yml index 7a36f948..a44aae5b 100644 --- a/.github/workflows/SublibraryCI.yml +++ b/.github/workflows/SublibraryCI.yml @@ -17,39 +17,6 @@ concurrency: cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} jobs: - list-sublibraries: - runs-on: ubuntu-latest - outputs: - projects: ${{ steps.list.outputs.projects }} - steps: - - uses: actions/checkout@v6 - - name: "List lib/* sublibraries" - id: list - run: | - # Build the JSON array in pure shell -- no jq/python3, since neither is - # guaranteed on a self-hosted runner. lib/* names contain no JSON-special - # characters, so plain quoting is safe. - projects="" - for d in lib/*/; do - d="${d%/}" - [ -f "$d/Project.toml" ] || continue - if [ -z "$projects" ]; then projects="\"$d\""; else projects="$projects,\"$d\""; fi - done - projects="[$projects]" - echo "Sublibraries: $projects" - echo "projects=$projects" >> "$GITHUB_OUTPUT" - - test: - needs: list-sublibraries - strategy: - fail-fast: false - matrix: - project: ${{ fromJson(needs.list-sublibraries.outputs.projects) }} - name: "Sublibrary - ${{ matrix.project }}" - uses: "SciML/.github/.github/workflows/tests.yml@v1" - with: - project: "${{ matrix.project }}" - # collect the sublibrary's own source coverage (its tests are the only thing - # that exercises it); point processcoverage at the sublibrary, not repo root. - coverage-directories: "${{ matrix.project }}/src,${{ matrix.project }}/ext" + sublibraries: + uses: "SciML/.github/.github/workflows/sublibrary-project-tests.yml@v1" secrets: "inherit" From b91e0012b0c4acdcdb62de93e1bf409e25e4f08a Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Fri, 5 Jun 2026 08:28:45 -0400 Subject: [PATCH 50/73] =?UTF-8?q?ci:=20uniformize=20=E2=80=94=20rm-CompatH?= =?UTF-8?q?elper=20add-DocPreviewCleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Part of bringing this repo onto the full centralized CI setup (SciML/.github @v1): remove retired CompatHelper (Dependabot drives [compat]) and/or add the centralized Documenter preview cleanup. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/CompatHelper.yml | 16 ---------------- .github/workflows/DocPreviewCleanup.yml | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 16 deletions(-) delete mode 100644 .github/workflows/CompatHelper.yml create mode 100644 .github/workflows/DocPreviewCleanup.yml diff --git a/.github/workflows/CompatHelper.yml b/.github/workflows/CompatHelper.yml deleted file mode 100644 index cba9134c..00000000 --- a/.github/workflows/CompatHelper.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: CompatHelper -on: - schedule: - - cron: 0 0 * * * - workflow_dispatch: -jobs: - CompatHelper: - runs-on: ubuntu-latest - steps: - - name: Pkg.add("CompatHelper") - run: julia -e 'using Pkg; Pkg.add("CompatHelper")' - - name: CompatHelper.main() - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }} - run: julia -e 'using CompatHelper; CompatHelper.main()' diff --git a/.github/workflows/DocPreviewCleanup.yml b/.github/workflows/DocPreviewCleanup.yml new file mode 100644 index 00000000..7b694802 --- /dev/null +++ b/.github/workflows/DocPreviewCleanup.yml @@ -0,0 +1,17 @@ +name: Doc Preview Cleanup + +on: + pull_request: + types: [closed] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: write + +jobs: + doc-preview-cleanup: + uses: "SciML/.github/.github/workflows/docs-preview-cleanup.yml@v1" + secrets: "inherit" From 9115f67480061469bc5a8b6247b33281d0208f57 Mon Sep 17 00:00:00 2001 From: Chris Rackauckas - Beep Boop Edition Date: Sun, 7 Jun 2026 10:29:44 +0000 Subject: [PATCH 51/73] ci: add standard centralized workflows (#78) Adds the missing SciML standard centralized caller workflows. Co-authored-by: ChrisRackauckas-Claude Co-authored-by: Claude Opus 4.8 (1M context) --- .github/workflows/DependabotAutoMerge.yml | 9 +++++++++ .github/workflows/RunicSuggestions.yml | 9 +++++++++ 2 files changed, 18 insertions(+) create mode 100644 .github/workflows/DependabotAutoMerge.yml create mode 100644 .github/workflows/RunicSuggestions.yml diff --git a/.github/workflows/DependabotAutoMerge.yml b/.github/workflows/DependabotAutoMerge.yml new file mode 100644 index 00000000..44b814c7 --- /dev/null +++ b/.github/workflows/DependabotAutoMerge.yml @@ -0,0 +1,9 @@ +name: "Dependabot Auto-merge" +on: pull_request +permissions: + contents: write + pull-requests: write +jobs: + automerge: + uses: "SciML/.github/.github/workflows/dependabot-automerge.yml@v1" + secrets: "inherit" diff --git a/.github/workflows/RunicSuggestions.yml b/.github/workflows/RunicSuggestions.yml new file mode 100644 index 00000000..43c07684 --- /dev/null +++ b/.github/workflows/RunicSuggestions.yml @@ -0,0 +1,9 @@ +name: "Runic Suggestions" +on: pull_request +permissions: + contents: read + pull-requests: write +jobs: + runic-suggestions: + uses: "SciML/.github/.github/workflows/runic-suggestions-on-pr.yml@v1" + secrets: "inherit" From 88fa249f03f7c705ebe4e276ae9160968f93b05e Mon Sep 17 00:00:00 2001 From: Chris Rackauckas - Beep Boop Edition Date: Sun, 7 Jun 2026 12:11:27 +0000 Subject: [PATCH 52/73] Drop allow-reresolve input from downgrade workflow(s) (#79) The reusable SciML/.github downgrade workflows now always use allow_reresolve: false and no longer accept an allow-reresolve input (see SciML/.github#71). Remove the now-invalid input so this caller keeps working once @v1 is retagged. Co-authored-by: Chris Rackauckas Co-authored-by: Claude Opus 4.8 (1M context) --- .github/workflows/Downgrade.yml | 1 - .github/workflows/DowngradeSublibraries.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/Downgrade.yml b/.github/workflows/Downgrade.yml index 677ec28b..4d36aeb9 100644 --- a/.github/workflows/Downgrade.yml +++ b/.github/workflows/Downgrade.yml @@ -17,5 +17,4 @@ jobs: with: julia-version: "1.10" skip: "Pkg,TOML" - allow-reresolve: false secrets: "inherit" diff --git a/.github/workflows/DowngradeSublibraries.yml b/.github/workflows/DowngradeSublibraries.yml index 552169de..9669e756 100644 --- a/.github/workflows/DowngradeSublibraries.yml +++ b/.github/workflows/DowngradeSublibraries.yml @@ -20,4 +20,3 @@ jobs: with: julia-version: "lts" skip: "Pkg,TOML,Statistics,LinearAlgebra,SparseArrays,InteractiveUtils,Random,Test,Corleone" - allow-reresolve: true From fa6a2b9a2a54dbc00c0e76327191bb829cd53cf1 Mon Sep 17 00:00:00 2001 From: Chris Rackauckas - Beep Boop Edition Date: Sun, 7 Jun 2026 19:34:21 +0000 Subject: [PATCH 53/73] Bump compat for OrdinaryDiffEq v7 / SciMLBase v3 ecosystem (#55) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OrdinaryDiffEqBDF, OrdinaryDiffEqTsit5: "1" → "1, 2" - SciMLBase: "2" → "2, 3" across all sub-packages - Migrate DEProblem/DEAlgorithm → AbstractDEProblem/AbstractDEAlgorithm - Migrate SciMLBase.DEAlgorithm → SciMLBase.AbstractDEAlgorithm Co-authored-by: ChrisRackauckas-Claude --- Project.toml | 6 +++--- lib/CorleoneOED/Project.toml | 4 ++-- lib/CorleoneOED/src/multiexperiments.jl | 8 ++++---- lib/CorleoneOED/src/oed.jl | 4 ++-- lib/OptimalControlBenchmarks/Project.toml | 5 +++-- src/multiple_shooting.jl | 2 +- src/single_shooting.jl | 2 +- 7 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Project.toml b/Project.toml index 99bc30cb..1f815323 100644 --- a/Project.toml +++ b/Project.toml @@ -46,13 +46,13 @@ ModelingToolkit = "11" OhMyThreads = "0.8" Optimization = "5" OptimizationMOI = "1" -OrdinaryDiffEqBDF = "1.14.0" -OrdinaryDiffEqTsit5 = "1" +OrdinaryDiffEqBDF = "1.14.0, 2" +OrdinaryDiffEqTsit5 = "1, 2" Random = "1" RecursiveArrayTools = "3.37.0" Reexport = "1.2.2" SafeTestsets = "0.1" -SciMLBase = "2.141.0" +SciMLBase = "2.141.0, 3" SciMLSensitivity = "7.87" SciMLStructures = "1.7" SymbolicIndexingInterface = "0.3.43" diff --git a/lib/CorleoneOED/Project.toml b/lib/CorleoneOED/Project.toml index 2ca89bdd..fe0a56f3 100644 --- a/lib/CorleoneOED/Project.toml +++ b/lib/CorleoneOED/Project.toml @@ -37,11 +37,11 @@ LinearAlgebra = "1" LuxCore = "1.4" Optimization = "5" OptimizationMOI = "1" -OrdinaryDiffEqTsit5 = "1.5" +OrdinaryDiffEqTsit5 = "1.5, 2" Random = "1" Reexport = "1.2" SafeTestsets = "0.1" -SciMLBase = "2" +SciMLBase = "2, 3" SciMLStructures = "1.7" StableRNGs = "1" SymbolicIndexingInterface = "0.3" diff --git a/lib/CorleoneOED/src/multiexperiments.jl b/lib/CorleoneOED/src/multiexperiments.jl index 05d4d870..a5eb614d 100644 --- a/lib/CorleoneOED/src/multiexperiments.jl +++ b/lib/CorleoneOED/src/multiexperiments.jl @@ -42,7 +42,7 @@ function Base.show(io::IO, oed::MultiExperimentLayer{DISCRETE, FIXED, SPLIT}) wh end function MultiExperimentLayer{DISCRETE}( - prob::DEProblem, alg::DEAlgorithm, nexp::Int; + prob::SciMLBase.AbstractDEProblem, alg::SciMLBase.AbstractDEAlgorithm, nexp::Int; params = eachindex(prob.p), measurements = [], observed = default_observed, kwargs... ) where {DISCRETE} layer = OEDLayer{DISCRETE}(prob, alg; params = params, measurements = measurements, observed = observed, kwargs...) @@ -50,7 +50,7 @@ function MultiExperimentLayer{DISCRETE}( return MultiExperimentLayer{DISCRETE, fixed, false, SingleShootingLayer, typeof(layer), typeof(params)}(layer, nexp, params) end -function MultiExperimentLayer{DISCRETE}(prob::DEProblem, alg::DEAlgorithm, params::Vector{Vector{Int64}}; measurements = [], observed = default_observed, kwargs...) where {DISCRETE} +function MultiExperimentLayer{DISCRETE}(prob::SciMLBase.AbstractDEProblem, alg::SciMLBase.AbstractDEAlgorithm, params::Vector{Vector{Int64}}; measurements = [], observed = default_observed, kwargs...) where {DISCRETE} nexp = length(params) layers = map(params) do param OEDLayer{DISCRETE}(prob, alg; params = param, measurements = measurements, observed = observed, kwargs...) @@ -66,12 +66,12 @@ function MultiExperimentLayer{DISCRETE}(prob::DEProblem, alg::DEAlgorithm, param return MultiExperimentLayer{DISCRETE, fixed, true, SingleShootingLayer, typeof(layers), typeof(new_params)}(layers, nexp, new_params) end -function MultiExperimentLayer{DISCRETE}(prob::DEProblem, alg::DEAlgorithm, shooting_points::AbstractVector{<:Real}, nexp::Int; params = eachindex(prob.p), measurements = [], observed = default_observed, kwargs...) where {DISCRETE} +function MultiExperimentLayer{DISCRETE}(prob::SciMLBase.AbstractDEProblem, alg::SciMLBase.AbstractDEAlgorithm, shooting_points::AbstractVector{<:Real}, nexp::Int; params = eachindex(prob.p), measurements = [], observed = default_observed, kwargs...) where {DISCRETE} layers = OEDLayer{DISCRETE}(prob, alg, shooting_points...; params = params, measurements = measurements, observed = observed, kwargs...) return MultiExperimentLayer{DISCRETE, false, false, MultipleShootingLayer, typeof(layers), typeof(params)}(layers, nexp, params) end -function MultiExperimentLayer{DISCRETE}(prob::DEProblem, alg::DEAlgorithm, shooting_points::AbstractVector{<:Real}, params::Vector{Vector{Int64}} = [eachindex(prob.p) for _ in 1:nexp]; measurements = [], observed = default_observed, kwargs...) where {DISCRETE} +function MultiExperimentLayer{DISCRETE}(prob::SciMLBase.AbstractDEProblem, alg::SciMLBase.AbstractDEAlgorithm, shooting_points::AbstractVector{<:Real}, params::Vector{Vector{Int64}} = [eachindex(prob.p) for _ in 1:nexp]; measurements = [], observed = default_observed, kwargs...) where {DISCRETE} nexp = length(params) layers = map(params) do param OEDLayer{DISCRETE}(prob, alg, shooting_points...; params = param, measurements = measurements, observed = observed, kwargs...) diff --git a/lib/CorleoneOED/src/oed.jl b/lib/CorleoneOED/src/oed.jl index a08ab049..7e0d6fc1 100644 --- a/lib/CorleoneOED/src/oed.jl +++ b/lib/CorleoneOED/src/oed.jl @@ -54,12 +54,12 @@ function Base.show(io::IO, oed::OEDLayer{DISCRETE, SAMPLED, FIXED}) where {DISCR return Base.show(io, "text/plain", isa(layer, SingleShootingLayer) ? layer.problem : layer.layer.problem) end -function OEDLayer{DISCRETE}(prob::DEProblem, alg::DEAlgorithm; params = eachindex(prob.p), measurements = [], observed = default_observed, kwargs...) where {DISCRETE} +function OEDLayer{DISCRETE}(prob::SciMLBase.AbstractDEProblem, alg::SciMLBase.AbstractDEAlgorithm; params = eachindex(prob.p), measurements = [], observed = default_observed, kwargs...) where {DISCRETE} layer = SingleShootingLayer(prob, alg; kwargs...) return OEDLayer{DISCRETE}(layer; params = params, measurements = measurements, observed = observed, kwargs...) end -function OEDLayer{DISCRETE}(prob::DEProblem, alg::DEAlgorithm, shooting_points...; params = eachindex(prob.p), measurements = [], observed = default_observed, kwargs...) where {DISCRETE} +function OEDLayer{DISCRETE}(prob::SciMLBase.AbstractDEProblem, alg::SciMLBase.AbstractDEAlgorithm, shooting_points...; params = eachindex(prob.p), measurements = [], observed = default_observed, kwargs...) where {DISCRETE} layer = MultipleShootingLayer(prob, alg, shooting_points...; kwargs...) return OEDLayer{DISCRETE}(layer; params = params, measurements = measurements, observed = observed, kwargs...) end diff --git a/lib/OptimalControlBenchmarks/Project.toml b/lib/OptimalControlBenchmarks/Project.toml index 7ecd7ce5..02cc7170 100644 --- a/lib/OptimalControlBenchmarks/Project.toml +++ b/lib/OptimalControlBenchmarks/Project.toml @@ -42,11 +42,12 @@ LinearAlgebra = "1" LuxCore = "1.4" Optimization = "5" OptimizationMOI = "1" -OrdinaryDiffEqTsit5 = "1.5" +OrdinaryDiffEq = "6, 7" +OrdinaryDiffEqTsit5 = "1.5, 2" Random = "1" Reexport = "1.2" SafeTestsets = "0.1" -SciMLBase = "2" +SciMLBase = "2, 3" SciMLStructures = "1.7" StableRNGs = "1" SymbolicIndexingInterface = "0.3" diff --git a/src/multiple_shooting.jl b/src/multiple_shooting.jl index b3d9e323..309dd94e 100644 --- a/src/multiple_shooting.jl +++ b/src/multiple_shooting.jl @@ -66,7 +66,7 @@ end function MultipleShootingLayer( prob::SciMLBase.AbstractDEProblem, - alg::SciMLBase.DEAlgorithm, + alg::SciMLBase.AbstractDEAlgorithm, tpoints::Real...; ensemble_alg = EnsembleSerial(), initialization = default_initialization, diff --git a/src/single_shooting.jl b/src/single_shooting.jl index 5a3c91d1..db140b22 100644 --- a/src/single_shooting.jl +++ b/src/single_shooting.jl @@ -214,7 +214,7 @@ function __parameterlength( return N end -function retrieve_symbol_cache(problem::SciMLBase.DEProblem, control_indices; control_names = [Symbol(:u, _subscript(u_id)) for u_id in 1:length(control_indices)]) +function retrieve_symbol_cache(problem::SciMLBase.AbstractDEProblem, control_indices; control_names = [Symbol(:u, _subscript(u_id)) for u_id in 1:length(control_indices)]) return retrieve_symbol_cache(problem.f.sys, problem.u0, problem.p, control_indices; control_names = control_names) end From 5612d1ce696dabc1b6f3b2ea68f6492365b83a49 Mon Sep 17 00:00:00 2001 From: Chris Rackauckas - Beep Boop Edition Date: Mon, 8 Jun 2026 09:30:31 +0000 Subject: [PATCH 54/73] Uniformize monorepo structure to OrdinaryDiffEq canonical style (#80) * ci: uniformize monorepo structure to OrdinaryDiffEq canonical style Thread a repo-specific group-env-name (CORLEONE_TEST_GROUP) consistently through the sublibrary CI callers and runtests, GROUP-dispatch the root CI, and fix the root test harness so Pkg.test() on the root works. - SublibraryCI.yml: pass group-env-name CORLEONE_TEST_GROUP + check-bounds auto. - DowngradeSublibraries.yml: add group-env-name CORLEONE_TEST_GROUP / value Core. - Sublib runtests (CorleoneOED, OptimalControlBenchmarks): read CORLEONE_TEST_GROUP (fall back to GROUP, then All). - CI.yml: GROUP-dispatched matrix over the root's own Core/QA groups (no hardcoded sublibrary rows; sublibs covered by the project model). - test/runtests.jl: _detect_sublibrary_group dispatcher sets CORLEONE_TEST_GROUP on sublib hand-off, transitive [sources] develop on Julia <1.11, Core/QA root groups; drop dead `using JET`. - Project.toml: add Pkg to [extras]/[compat]/[targets].test (root runtests uses Pkg; matches the canonical root and keeps Aqua deps_compat green). The leaf->root [sources] graph (sublibs depend on Corleone) is preserved; no root->leaf edges are added since the root does not re-export the sublibs. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) * fix: remove self-import of OptimalControlBenchmarks in its own module The module's source file did `using OptimalControlBenchmarks` inside the `module OptimalControlBenchmarks ... end` block, importing itself. Remove it. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) * chore: canonicalize LICENSE files to LICENSE.md Rename root LICENSE -> LICENSE.md and lib/CorleoneOED/LICENSE -> LICENSE.md (content preserved via git mv), and add lib/OptimalControlBenchmarks/LICENSE.md so every component carries an MIT LICENSE.md, matching the OrdinaryDiffEq.jl monorepo layout. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) * docs: canonical component READMEs for sublibraries Rewrite lib/CorleoneOED/README.md (was pointing at the stale external AlCap23/CorleoneOED.jl CI badge) and add lib/OptimalControlBenchmarks/README.md, both using the canonical Corleone.jl monorepo component README template (Zulip + Global Docs + ColPrac + SciML Code Style badges and the 'component of the Corleone.jl monorepo' wording), matching OrdinaryDiffEq.jl. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) * ci: add .typos.toml/.codecov.yml and canonical TagBot with subpackage matrix - Add .typos.toml with [default.extend-words] (matches SpellCheck/typos workflow) - Add .codecov.yml with comment: false - Convert TagBot.yml to the canonical OrdinaryDiffEq.jl form: top-level permissions block, workflow_dispatch.inputs.lookback default '3', the root TagBot-Corleone job, and a TagBot-Subpackages matrix (fail-fast: false) that tags the registered sublibrary CorleoneOED via subdir lib/CorleoneOED. OptimalControlBenchmarks is not registered in General, so it is excluded. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) * Remove self-import of OptimalControlBenchmarks in run_benchmarks.jl run_benchmarks.jl is include()d into module OptimalControlBenchmarks, so the line `using OptimalControlBenchmarks` executed inside the module's own namespace and hard-failed to load (ArgumentError: Package OptimalControlBenchmarks not found). The symbols it would import are already in scope inside the module, so the line is simply removed. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) * test: isolate root + CorleoneOED QA into canonical test/qa sub-env Move the Aqua code-quality checks for the root Corleone package and the CorleoneOED sublibrary out of the main test target into dedicated test/qa Pkg sub-environments, matching the canonical OrdinaryDiffEq structure (lib//test/qa). Each package now has: - test/qa/Project.toml: Aqua + the package (+ in-repo Corleone sibling) + Test, with relative [sources] and [compat] for every dep plus julia = "1.10". - test/qa/qa.jl: the Aqua.test_all testset. - runtests.jl: an activate_qa_env() helper that activates test/qa, develops the package (and sibling) by path so the PR branch code is tested on Julia < 1.11 where [sources] is ignored, then instantiates. The QA branch calls it and then @safetestset includes qa/qa.jl. Aqua (and the vestigial, unused JET entry in the root) are removed from each main Project.toml's [compat]/[extras]/[targets].test so the QA tooling deps no longer participate in the main test resolve. No QA coverage changed: each package still runs exactly Aqua, with identical pass/fail behavior (verified the CorleoneOED qa env resolves and Aqua.test_all produces the same 6-pass/5-fail result as the pristine base, confirming the pre-existing Aqua failures are unchanged by this restructure). Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) * test: isolate OptimalControlBenchmarks QA into canonical test/qa sub-env Move the Aqua code-quality check for the OptimalControlBenchmarks sublibrary out of the main test target into a dedicated test/qa Pkg sub-environment, matching the canonical OrdinaryDiffEq structure (lib//test/qa) and the root + CorleoneOED migration in the preceding commit. - test/qa/Project.toml: Aqua + OptimalControlBenchmarks + in-repo Corleone sibling + Test, with relative [sources] and [compat] for every dep plus julia = "1.10". - test/qa/qa.jl: the Aqua.test_all testset. - runtests.jl: activate_qa_env() activates test/qa, develops the package and the Corleone sibling by path (for Julia < 1.11 where [sources] is ignored), then instantiates; the QA branch calls it and @safetestset includes qa/qa.jl. Aqua is removed from the main Project.toml's [compat]/[extras]/[targets].test so the QA tooling deps no longer participate in the main test resolve. QA coverage is unchanged: the package still runs exactly Aqua. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) * test(CorleoneOED): add test_groups.toml pinning Core/QA version matrix Make the centralized SublibraryCI (sublibrary-project-tests.yml@v1) discover this sublibrary's groups explicitly instead of falling back to the default shape. Core runs on [lts, 1, pre]; QA (Aqua) runs on [lts, 1]. Matches the canonical OrdinaryDiffEq.jl per-sublibrary test/test_groups.toml convention. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) * test(OptimalControlBenchmarks): add test_groups.toml pinning Core/QA matrix Make the centralized SublibraryCI (sublibrary-project-tests.yml@v1) discover this sublibrary's groups explicitly instead of the default shape. Core runs on [lts, 1, pre]; QA (Aqua) runs on [lts, 1]. Matches the canonical OrdinaryDiffEq.jl per-sublibrary test/test_groups.toml convention. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) * ci: align root CI version matrix with canonical group matrix Core runs on [lts, 1, pre]; QA (Aqua) runs on [lts, 1] (drops the redundant 1.11 row and the pre/1.11/lts QA excludes, adding lts to QA). Mirrors the test_groups.toml matrix used by the sublibraries. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) * test: canonical group-folder layout for the root package Make the root test suite conform to the OrdinaryDiffEq canonical group-folder model: every test lives in exactly one test// folder; no-extra-dep groups run in the light main test env as part of GROUP=All, while dep-adding groups carry their own isolated test//Project.toml that runtests.jl activates and that the CI matrix runs explicitly. - Move the loose root tests into test/core/ (Core group, light env). - Promote the example scripts (test/examples/) into a dep-adding "Examples" group with test/examples/Project.toml carrying the heavy optimal-control stack (ModelingToolkit, Ipopt/OptimizationMOI, SciMLSensitivity, ForwardDiff, OrdinaryDiffEqBDF) + Corleone via [sources]. - runtests.jl: GROUP=All/Core run the light Core group; Examples and QA each activate their own test/ env. - Make the main [extras]/[targets].test LIGHT: drop the heavy test-only deps (now provided by the Examples group env) and their stale [compat] entries. - test/test_groups.toml pins the canonical version matrix: Core/Examples on [lts,1,pre], QA on [lts,1]; CI.yml matrix kept consistent (adds Examples). Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) * Declare Pkg in test deps and group CorleoneOED core tests CorleoneOED and OptimalControlBenchmarks test/runtests.jl both `using Pkg` to activate the isolated QA env, but Pkg was not declared in [extras] or [targets].test. Under Pkg.test the test environment doesn't get Pkg by default, so `using Pkg` failed at load time and blocked the entire sublib test suites before any test ran. Add Pkg to both [extras] and [targets].test. Also move CorleoneOED's Core test files (1d_oed.jl, lotka_oed.jl, lotka_oed_svd.jl) into test/core/ and update the runtests.jl includes, matching the uniform monorepo test layout. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) * ci: canonical TagBot thin caller + downgrade skip/version cleanup - TagBot.yml: replace verbose JuliaRegistries/TagBot steps with the centralized SciML/.github tagbot.yml@v1 thin caller, plus a tagbot-subpackages matrix over registered lib sublibraries (only CorleoneOED; OptimalControlBenchmarks is not yet registered). - Downgrade.yml: drop hand-listed stdlib skip (Pkg,TOML) now that the central workflow auto-populates skip; bump julia-version 1.10 -> lts. - DowngradeSublibraries.yml: drop hand-listed skip (stdlibs + Corleone caller pkg) now auto-populated by the central workflow. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --------- Co-authored-by: ChrisRackauckas-Claude Co-authored-by: Claude Opus 4.8 (1M context) --- .codecov.yml | 1 + .github/workflows/CI.yml | 18 ++- .github/workflows/Downgrade.yml | 3 +- .github/workflows/DowngradeSublibraries.yml | 4 +- .github/workflows/SublibraryCI.yml | 3 + .github/workflows/TagBot.yml | 26 ++-- .typos.toml | 61 ++++++++ LICENSE => LICENSE.md | 0 Project.toml | 20 +-- lib/CorleoneOED/{LICENSE => LICENSE.md} | 0 lib/CorleoneOED/Project.toml | 5 +- lib/CorleoneOED/README.md | 11 +- lib/CorleoneOED/test/{ => core}/1d_oed.jl | 0 lib/CorleoneOED/test/{ => core}/lotka_oed.jl | 0 .../test/{ => core}/lotka_oed_svd.jl | 0 lib/CorleoneOED/test/qa/Project.toml | 16 +++ lib/CorleoneOED/test/qa/qa.jl | 8 ++ lib/CorleoneOED/test/runtests.jl | 35 +++-- lib/CorleoneOED/test/test_groups.toml | 5 + lib/OptimalControlBenchmarks/LICENSE.md | 21 +++ lib/OptimalControlBenchmarks/Project.toml | 5 +- lib/OptimalControlBenchmarks/README.md | 10 ++ .../src/OptimalControlBenchmarks.jl | 1 - .../src/run_benchmarks.jl | 1 - .../test/qa/Project.toml | 16 +++ lib/OptimalControlBenchmarks/test/qa/qa.jl | 8 ++ lib/OptimalControlBenchmarks/test/runtests.jl | 32 +++-- .../test/test_groups.toml | 5 + test/{ => core}/local_controls.jl | 0 test/{ => core}/multiple_shooting.jl | 0 test/examples/.PLACEHOLDER | 0 test/examples/Project.toml | 39 +++++ test/qa/Project.toml | 13 ++ test/qa/qa.jl | 8 ++ test/runtests.jl | 133 ++++++++++++------ test/test_groups.toml | 8 ++ 36 files changed, 410 insertions(+), 106 deletions(-) create mode 100644 .codecov.yml create mode 100644 .typos.toml rename LICENSE => LICENSE.md (100%) rename lib/CorleoneOED/{LICENSE => LICENSE.md} (100%) rename lib/CorleoneOED/test/{ => core}/1d_oed.jl (100%) rename lib/CorleoneOED/test/{ => core}/lotka_oed.jl (100%) rename lib/CorleoneOED/test/{ => core}/lotka_oed_svd.jl (100%) create mode 100644 lib/CorleoneOED/test/qa/Project.toml create mode 100644 lib/CorleoneOED/test/qa/qa.jl create mode 100644 lib/CorleoneOED/test/test_groups.toml create mode 100644 lib/OptimalControlBenchmarks/LICENSE.md create mode 100644 lib/OptimalControlBenchmarks/README.md create mode 100644 lib/OptimalControlBenchmarks/test/qa/Project.toml create mode 100644 lib/OptimalControlBenchmarks/test/qa/qa.jl create mode 100644 lib/OptimalControlBenchmarks/test/test_groups.toml rename test/{ => core}/local_controls.jl (100%) rename test/{ => core}/multiple_shooting.jl (100%) delete mode 100644 test/examples/.PLACEHOLDER create mode 100644 test/examples/Project.toml create mode 100644 test/qa/Project.toml create mode 100644 test/qa/qa.jl create mode 100644 test/test_groups.toml diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 00000000..69cb7601 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1 @@ +comment: false diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ff706a4d..3ba5feca 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -15,15 +15,27 @@ concurrency: cancel-in-progress: ${{ github.ref_name != github.event.repository.default_branch || github.ref != 'refs/tags/v*' }} jobs: tests: - name: "Tests" + name: "${{ matrix.group }}" strategy: fail-fast: false matrix: + group: + - Core + - Examples + - QA version: - - '1.11' - 'lts' + - '1' + - 'pre' + exclude: + # QA (Aqua) runs on lts + current release only, not pre. + - group: QA + version: 'pre' uses: "SciML/.github/.github/workflows/tests.yml@v1" with: julia-version: "${{ matrix.version }}" - coverage-directories: "src,lib/CorleoneOED/src" + group: "${{ matrix.group }}" + group-env-name: "GROUP" + check-bounds: "auto" + coverage-directories: "src" secrets: "inherit" diff --git a/.github/workflows/Downgrade.yml b/.github/workflows/Downgrade.yml index 4d36aeb9..b49343d8 100644 --- a/.github/workflows/Downgrade.yml +++ b/.github/workflows/Downgrade.yml @@ -15,6 +15,5 @@ jobs: name: "Downgrade" uses: "SciML/.github/.github/workflows/downgrade.yml@v1" with: - julia-version: "1.10" - skip: "Pkg,TOML" + julia-version: "lts" secrets: "inherit" diff --git a/.github/workflows/DowngradeSublibraries.yml b/.github/workflows/DowngradeSublibraries.yml index 9669e756..8e8c4122 100644 --- a/.github/workflows/DowngradeSublibraries.yml +++ b/.github/workflows/DowngradeSublibraries.yml @@ -19,4 +19,6 @@ jobs: secrets: "inherit" with: julia-version: "lts" - skip: "Pkg,TOML,Statistics,LinearAlgebra,SparseArrays,InteractiveUtils,Random,Test,Corleone" + group-env-name: "CORLEONE_TEST_GROUP" + group-env-value: "Core" + # Every lib/* sublibrary is downgrade-tested (projects auto-discovered). diff --git a/.github/workflows/SublibraryCI.yml b/.github/workflows/SublibraryCI.yml index a44aae5b..32de868f 100644 --- a/.github/workflows/SublibraryCI.yml +++ b/.github/workflows/SublibraryCI.yml @@ -19,4 +19,7 @@ concurrency: jobs: sublibraries: uses: "SciML/.github/.github/workflows/sublibrary-project-tests.yml@v1" + with: + group-env-name: CORLEONE_TEST_GROUP + check-bounds: auto secrets: "inherit" diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml index 623860f7..ba02ddae 100644 --- a/.github/workflows/TagBot.yml +++ b/.github/workflows/TagBot.yml @@ -1,15 +1,19 @@ -name: TagBot +name: "TagBot" on: issue_comment: - types: - - created + types: [created] workflow_dispatch: jobs: - TagBot: - if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' - runs-on: ubuntu-latest - steps: - - uses: JuliaRegistries/TagBot@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - ssh: ${{ secrets.DOCUMENTER_KEY }} \ No newline at end of file + tagbot: + uses: "SciML/.github/.github/workflows/tagbot.yml@v1" + secrets: "inherit" + tagbot-subpackages: + strategy: + fail-fast: false + matrix: + subdir: + - "lib/CorleoneOED" + uses: "SciML/.github/.github/workflows/tagbot.yml@v1" + with: + subdir: "${{ matrix.subdir }}" + secrets: "inherit" diff --git a/.typos.toml b/.typos.toml new file mode 100644 index 00000000..18f11359 --- /dev/null +++ b/.typos.toml @@ -0,0 +1,61 @@ +[default.extend-words] +# Julia-specific functions +indexin = "indexin" +findfirst = "findfirst" +findlast = "findlast" +eachindex = "eachindex" +setp = "setp" +getp = "getp" +setu = "setu" +getu = "getu" + +# Common variable patterns in Julia/SciML +osys = "osys" +rsys = "rsys" +usys = "usys" +eqs = "eqs" +rhs = "rhs" +lhs = "lhs" +ode = "ode" +oed = "oed" +ivp = "ivp" + +# Common abbreviations +tol = "tol" +rtol = "rtol" +atol = "atol" +idx = "idx" +prev = "prev" +curr = "curr" +init = "init" +tmp = "tmp" +vec = "vec" +arr = "arr" +dt = "dt" +du = "du" +dx = "dx" +dy = "dy" + +# Algorithm/type suffixes +alg = "alg" +prob = "prob" +sol = "sol" +cb = "cb" +opts = "opts" +args = "args" +kwargs = "kwargs" + +# Scientific abbreviations +ND = "ND" +nd = "nd" +MTK = "MTK" +ODE = "ODE" +OED = "OED" + +# Domain terms +jacobian = "jacobian" +hessian = "hessian" +discretization = "discretization" +parameterized = "parameterized" +discretized = "discretized" +vectorized = "vectorized" diff --git a/LICENSE b/LICENSE.md similarity index 100% rename from LICENSE rename to LICENSE.md diff --git a/Project.toml b/Project.toml index 1f815323..1ffa89bc 100644 --- a/Project.toml +++ b/Project.toml @@ -30,50 +30,36 @@ CorleoneComponentArraysExtension = ["ComponentArrays"] CorleoneModelingToolkitExtension = "ModelingToolkit" [compat] -Aqua = "0.8" ChainRulesCore = "1.25.1" ComponentArrays = "0.15" Distributed = "1" DocStringExtensions = "0.9.3" -ForwardDiff = "1.2" Functors = "0.5" -Ipopt = "1.10" -JET = "0.9" LinearAlgebra = "1" LuxCore = "1.4" Makie = "0.24" ModelingToolkit = "11" OhMyThreads = "0.8" Optimization = "5" -OptimizationMOI = "1" -OrdinaryDiffEqBDF = "1.14.0, 2" OrdinaryDiffEqTsit5 = "1, 2" +Pkg = "1" Random = "1" RecursiveArrayTools = "3.37.0" Reexport = "1.2.2" SafeTestsets = "0.1" SciMLBase = "2.141.0, 3" -SciMLSensitivity = "7.87" SciMLStructures = "1.7" SymbolicIndexingInterface = "0.3.43" Test = "1" julia = "1.10" [extras] -Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" -ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" -Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" -JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" -Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba" -OptimizationMOI = "fd9f6733-72f4-499f-8506-86b2bdd0dea1" -OrdinaryDiffEqBDF = "6ad6398a-0878-4a85-9266-38940aa047c8" OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" -SciMLSensitivity = "1ed8b502-d754-442c-8d5d-10ac956f44a1" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Aqua", "ComponentArrays", "Test", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqBDF", "LinearAlgebra", "JET", "SciMLSensitivity", "Optimization", "OptimizationMOI", "Ipopt", "SafeTestsets", "ForwardDiff", "ModelingToolkit"] +test = ["ComponentArrays", "Test", "OrdinaryDiffEqTsit5", "LinearAlgebra", "SafeTestsets", "Pkg"] diff --git a/lib/CorleoneOED/LICENSE b/lib/CorleoneOED/LICENSE.md similarity index 100% rename from lib/CorleoneOED/LICENSE rename to lib/CorleoneOED/LICENSE.md diff --git a/lib/CorleoneOED/Project.toml b/lib/CorleoneOED/Project.toml index fe0a56f3..9d50eb7f 100644 --- a/lib/CorleoneOED/Project.toml +++ b/lib/CorleoneOED/Project.toml @@ -27,7 +27,6 @@ CorleoneOEDOptimizationExtension = ["Optimization", "ComponentArrays"] CorleoneOEDZygoteExtension = "Zygote" [compat] -Aqua = "0.8" Corleone = "0.0.3" ComponentArrays = "0.15" DocStringExtensions = "0.9" @@ -51,12 +50,12 @@ Zygote = "0.7" julia = "1.10" [extras] -Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba" OptimizationMOI = "fd9f6733-72f4-499f-8506-86b2bdd0dea1" OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" @@ -64,4 +63,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" path = "../.." [targets] -test = ["Aqua", "ComponentArrays", "Ipopt", "Optimization", "OptimizationMOI", "OrdinaryDiffEqTsit5", "SafeTestsets", "Test"] +test = ["ComponentArrays", "Ipopt", "Optimization", "OptimizationMOI", "OrdinaryDiffEqTsit5", "Pkg", "SafeTestsets", "Test"] diff --git a/lib/CorleoneOED/README.md b/lib/CorleoneOED/README.md index 57549462..c70777d5 100644 --- a/lib/CorleoneOED/README.md +++ b/lib/CorleoneOED/README.md @@ -1,3 +1,10 @@ -# CorleoneOED +# CorleoneOED.jl -[![Build Status](https://github.com/AlCap23/CorleoneOED.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/AlCap23/CorleoneOED.jl/actions/workflows/CI.yml?query=branch%3Amain) +[![Join the chat at https://julialang.zulipchat.com #sciml-bridged](https://img.shields.io/static/v1?label=Zulip&message=chat&color=9558b2&labelColor=389826)](https://julialang.zulipchat.com/#narrow/stream/279055-sciml-bridged) +[![Global Docs](https://img.shields.io/badge/docs-SciML-blue.svg)](https://docs.sciml.ai/Corleone/stable/) + +[![ColPrac: Contributor's Guide on Collaborative Practices for Community Packages](https://img.shields.io/badge/ColPrac-Contributor%27s%20Guide-blueviolet)](https://github.com/SciML/ColPrac) +[![SciML Code Style](https://img.shields.io/static/v1?label=code%20style&message=SciML&color=9558b2&labelColor=389826)](https://github.com/SciML/SciMLStyle) + +CorleoneOED.jl is a component of the [Corleone.jl](https://github.com/SciML/Corleone.jl) monorepo. It provides optimal experimental design tooling, including Fisher information computation, sensitivity-based OED layers, and A/D/E optimality criteria. +While completely independent and usable on its own, users wanting the full optimal control and experimental design suite should use [Corleone.jl](https://github.com/SciML/Corleone.jl). diff --git a/lib/CorleoneOED/test/1d_oed.jl b/lib/CorleoneOED/test/core/1d_oed.jl similarity index 100% rename from lib/CorleoneOED/test/1d_oed.jl rename to lib/CorleoneOED/test/core/1d_oed.jl diff --git a/lib/CorleoneOED/test/lotka_oed.jl b/lib/CorleoneOED/test/core/lotka_oed.jl similarity index 100% rename from lib/CorleoneOED/test/lotka_oed.jl rename to lib/CorleoneOED/test/core/lotka_oed.jl diff --git a/lib/CorleoneOED/test/lotka_oed_svd.jl b/lib/CorleoneOED/test/core/lotka_oed_svd.jl similarity index 100% rename from lib/CorleoneOED/test/lotka_oed_svd.jl rename to lib/CorleoneOED/test/core/lotka_oed_svd.jl diff --git a/lib/CorleoneOED/test/qa/Project.toml b/lib/CorleoneOED/test/qa/Project.toml new file mode 100644 index 00000000..aef4671d --- /dev/null +++ b/lib/CorleoneOED/test/qa/Project.toml @@ -0,0 +1,16 @@ +[deps] +Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +Corleone = "2751e0db-33e9-4374-b36d-b7219e1e6b40" +CorleoneOED = "6590a4b1-d036-41fe-a5b4-03a980244101" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[sources] +Corleone = {path = "../../../.."} +CorleoneOED = {path = "../.."} + +[compat] +Aqua = "0.8" +Corleone = "0.0.3" +CorleoneOED = "0.0.4" +Test = "1" +julia = "1.10" diff --git a/lib/CorleoneOED/test/qa/qa.jl b/lib/CorleoneOED/test/qa/qa.jl new file mode 100644 index 00000000..c9f196fb --- /dev/null +++ b/lib/CorleoneOED/test/qa/qa.jl @@ -0,0 +1,8 @@ +using CorleoneOED +using Aqua + +@testset "Aqua" begin + Aqua.test_all( + CorleoneOED + ) +end diff --git a/lib/CorleoneOED/test/runtests.jl b/lib/CorleoneOED/test/runtests.jl index 4056d223..aeedd399 100644 --- a/lib/CorleoneOED/test/runtests.jl +++ b/lib/CorleoneOED/test/runtests.jl @@ -1,29 +1,40 @@ using CorleoneOED using SafeTestsets +using Pkg -# Centralized sublibrary CI emits GROUP as the bare package name (-> "Core") or -# "_" (-> ""). Map it to the standard Core/QA section names this -# file dispatches on. GROUP="All" keeps local `Pkg.test()` runs running everything. -const _G = get(ENV, "GROUP", "All") +# QA (Aqua) runs in an isolated environment (test/qa) so its tooling deps never +# enter the main test target's resolve. On Julia < 1.11 the [sources] table is +# ignored, so develop the package and its in-repo sibling by path. +function activate_qa_env() + Pkg.activate(joinpath(@__DIR__, "qa")) + Pkg.develop([ + Pkg.PackageSpec(path = joinpath(@__DIR__, "..")), + Pkg.PackageSpec(path = joinpath(@__DIR__, "..", "..", "..")) + ]) + return Pkg.instantiate() +end + +# Centralized sublibrary CI sets CORLEONE_TEST_GROUP to the bare package name +# (-> "Core") or "_" (-> ""). Fall back to GROUP, then "All", so +# local `Pkg.test()` runs (which set neither) run everything. Map the value to +# the standard Core/QA section names this file dispatches on. +const _G = get(ENV, "CORLEONE_TEST_GROUP", get(ENV, "GROUP", "All")) const _SUB = "CorleoneOED" const GROUP = _G == _SUB ? "Core" : (startswith(_G, _SUB * "_") ? _G[(length(_SUB) + 2):end] : _G) if GROUP == "All" || GROUP == "Core" @safetestset "1D Example" begin - include("1d_oed.jl") + include("core/1d_oed.jl") end @safetestset "Lotka Volterra" begin - include("lotka_oed.jl") + include("core/lotka_oed.jl") end @safetestset "Lotka Volterra SVD" begin - include("lotka_oed_svd.jl") + include("core/lotka_oed_svd.jl") end end if GROUP == "All" || GROUP == "QA" - @safetestset "Code quality (Aqua.jl)" begin - using Aqua - using CorleoneOED - Aqua.test_all(CorleoneOED) - end + activate_qa_env() + @safetestset "Code quality (Aqua.jl)" include("qa/qa.jl") end diff --git a/lib/CorleoneOED/test/test_groups.toml b/lib/CorleoneOED/test/test_groups.toml new file mode 100644 index 00000000..1fe84cd4 --- /dev/null +++ b/lib/CorleoneOED/test/test_groups.toml @@ -0,0 +1,5 @@ +[Core] +versions = ["lts", "1", "pre"] + +[QA] +versions = ["lts", "1"] diff --git a/lib/OptimalControlBenchmarks/LICENSE.md b/lib/OptimalControlBenchmarks/LICENSE.md new file mode 100644 index 00000000..d21503c7 --- /dev/null +++ b/lib/OptimalControlBenchmarks/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Robert Lampel, Julius Martensen, Christoph Plate, Reinhold Wittmann and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/OptimalControlBenchmarks/Project.toml b/lib/OptimalControlBenchmarks/Project.toml index 02cc7170..5c16ce3f 100644 --- a/lib/OptimalControlBenchmarks/Project.toml +++ b/lib/OptimalControlBenchmarks/Project.toml @@ -32,7 +32,6 @@ Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" UnoSolver = "1baa60ac-02f7-4b39-a7a8-2f4f58486b05" [compat] -Aqua = "0.8" ComponentArrays = "0.15" Corleone = "0.0.3" DocStringExtensions = "0.9" @@ -56,12 +55,12 @@ Test = "1" julia = "1.10" [extras] -Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba" OptimizationMOI = "fd9f6733-72f4-499f-8506-86b2bdd0dea1" OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" @@ -69,4 +68,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" path = "../.." [targets] -test = ["Aqua", "SafeTestsets", "Test"] +test = ["Pkg", "SafeTestsets", "Test"] diff --git a/lib/OptimalControlBenchmarks/README.md b/lib/OptimalControlBenchmarks/README.md new file mode 100644 index 00000000..be8785c7 --- /dev/null +++ b/lib/OptimalControlBenchmarks/README.md @@ -0,0 +1,10 @@ +# OptimalControlBenchmarks.jl + +[![Join the chat at https://julialang.zulipchat.com #sciml-bridged](https://img.shields.io/static/v1?label=Zulip&message=chat&color=9558b2&labelColor=389826)](https://julialang.zulipchat.com/#narrow/stream/279055-sciml-bridged) +[![Global Docs](https://img.shields.io/badge/docs-SciML-blue.svg)](https://docs.sciml.ai/Corleone/stable/) + +[![ColPrac: Contributor's Guide on Collaborative Practices for Community Packages](https://img.shields.io/badge/ColPrac-Contributor%27s%20Guide-blueviolet)](https://github.com/SciML/ColPrac) +[![SciML Code Style](https://img.shields.io/static/v1?label=code%20style&message=SciML&color=9558b2&labelColor=389826)](https://github.com/SciML/SciMLStyle) + +OptimalControlBenchmarks.jl is a component of the [Corleone.jl](https://github.com/SciML/Corleone.jl) monorepo. It collects optimal control benchmark problems and the tooling to run, scale, and plot them against Corleone.jl solvers. +While completely independent and usable on its own, users wanting the full optimal control suite should use [Corleone.jl](https://github.com/SciML/Corleone.jl). diff --git a/lib/OptimalControlBenchmarks/src/OptimalControlBenchmarks.jl b/lib/OptimalControlBenchmarks/src/OptimalControlBenchmarks.jl index cd8c7a4d..06f9401a 100644 --- a/lib/OptimalControlBenchmarks/src/OptimalControlBenchmarks.jl +++ b/lib/OptimalControlBenchmarks/src/OptimalControlBenchmarks.jl @@ -3,7 +3,6 @@ module OptimalControlBenchmarks using ModelingToolkit using ModelingToolkit: t_nounits as t, D_nounits as D using ModelingToolkit: inputs -using OptimalControlBenchmarks using CairoMakie using Corleone using OrdinaryDiffEqTsit5 diff --git a/lib/OptimalControlBenchmarks/src/run_benchmarks.jl b/lib/OptimalControlBenchmarks/src/run_benchmarks.jl index d6dc1ce1..87dbbbe1 100644 --- a/lib/OptimalControlBenchmarks/src/run_benchmarks.jl +++ b/lib/OptimalControlBenchmarks/src/run_benchmarks.jl @@ -1,5 +1,4 @@ using BenchmarkTools -using OptimalControlBenchmarks function run_all(benchmarks, optimizer, grids) diff --git a/lib/OptimalControlBenchmarks/test/qa/Project.toml b/lib/OptimalControlBenchmarks/test/qa/Project.toml new file mode 100644 index 00000000..f821e30a --- /dev/null +++ b/lib/OptimalControlBenchmarks/test/qa/Project.toml @@ -0,0 +1,16 @@ +[deps] +Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +Corleone = "2751e0db-33e9-4374-b36d-b7219e1e6b40" +OptimalControlBenchmarks = "d147904a-1eb9-11f1-01e7-bdf88e0f9445" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[sources] +Corleone = {path = "../../../.."} +OptimalControlBenchmarks = {path = "../.."} + +[compat] +Aqua = "0.8" +Corleone = "0.0.3" +OptimalControlBenchmarks = "0.0.3" +Test = "1" +julia = "1.10" diff --git a/lib/OptimalControlBenchmarks/test/qa/qa.jl b/lib/OptimalControlBenchmarks/test/qa/qa.jl new file mode 100644 index 00000000..d09e9dbe --- /dev/null +++ b/lib/OptimalControlBenchmarks/test/qa/qa.jl @@ -0,0 +1,8 @@ +using OptimalControlBenchmarks +using Aqua + +@testset "Aqua" begin + Aqua.test_all( + OptimalControlBenchmarks + ) +end diff --git a/lib/OptimalControlBenchmarks/test/runtests.jl b/lib/OptimalControlBenchmarks/test/runtests.jl index 4b730a0b..76c18329 100644 --- a/lib/OptimalControlBenchmarks/test/runtests.jl +++ b/lib/OptimalControlBenchmarks/test/runtests.jl @@ -1,22 +1,36 @@ using OptimalControlBenchmarks using Test using SafeTestsets +using Pkg -# Centralized sublibrary CI emits GROUP as the bare package name (-> "Core") or -# "_" (-> ""). Map it to the standard Core/QA section names this -# file dispatches on. GROUP="All" keeps local `Pkg.test()` runs running everything. -const _G = get(ENV, "GROUP", "All") +# QA (Aqua) runs in an isolated environment (test/qa) so its tooling deps never +# enter the main test target's resolve. On Julia < 1.11 the [sources] table is +# ignored, so develop the package and its in-repo sibling by path. +function activate_qa_env() + Pkg.activate(joinpath(@__DIR__, "qa")) + Pkg.develop([ + Pkg.PackageSpec(path = joinpath(@__DIR__, "..")), + Pkg.PackageSpec(path = joinpath(@__DIR__, "..", "..", "..")) + ]) + return Pkg.instantiate() +end + +# Centralized sublibrary CI sets CORLEONE_TEST_GROUP to the bare package name +# (-> "Core") or "_" (-> ""). Fall back to GROUP, then "All", so +# local `Pkg.test()` runs (which set neither) run everything. Map the value to +# the standard Core/QA section names this file dispatches on. +const _G = get(ENV, "CORLEONE_TEST_GROUP", get(ENV, "GROUP", "All")) const _SUB = "OptimalControlBenchmarks" const GROUP = _G == _SUB ? "Core" : (startswith(_G, _SUB * "_") ? _G[(length(_SUB) + 2):end] : _G) if GROUP == "All" || GROUP == "Core" + # No Core unit tests yet; running the benchmarks requires the full MTK + + # Ipopt/MOI optimal-control stack and is too expensive for CI. Keep a + # trivial assertion so the Core group still emits a Test Summary. @test 1 == 1 end if GROUP == "All" || GROUP == "QA" - @safetestset "Code quality (Aqua.jl)" begin - using Aqua - using OptimalControlBenchmarks - Aqua.test_all(OptimalControlBenchmarks) - end + activate_qa_env() + @safetestset "Code quality (Aqua.jl)" include("qa/qa.jl") end diff --git a/lib/OptimalControlBenchmarks/test/test_groups.toml b/lib/OptimalControlBenchmarks/test/test_groups.toml new file mode 100644 index 00000000..1fe84cd4 --- /dev/null +++ b/lib/OptimalControlBenchmarks/test/test_groups.toml @@ -0,0 +1,5 @@ +[Core] +versions = ["lts", "1", "pre"] + +[QA] +versions = ["lts", "1"] diff --git a/test/local_controls.jl b/test/core/local_controls.jl similarity index 100% rename from test/local_controls.jl rename to test/core/local_controls.jl diff --git a/test/multiple_shooting.jl b/test/core/multiple_shooting.jl similarity index 100% rename from test/multiple_shooting.jl rename to test/core/multiple_shooting.jl diff --git a/test/examples/.PLACEHOLDER b/test/examples/.PLACEHOLDER deleted file mode 100644 index e69de29b..00000000 diff --git a/test/examples/Project.toml b/test/examples/Project.toml new file mode 100644 index 00000000..ffc7850e --- /dev/null +++ b/test/examples/Project.toml @@ -0,0 +1,39 @@ +[deps] +ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" +Corleone = "2751e0db-33e9-4374-b36d-b7219e1e6b40" +ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" +Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" +LuxCore = "bb33d45b-7691-41d6-9220-0943567d0623" +ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" +Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba" +OptimizationMOI = "fd9f6733-72f4-499f-8506-86b2bdd0dea1" +OrdinaryDiffEqBDF = "6ad6398a-0878-4a85-9266-38940aa047c8" +OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" +SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" +SciMLSensitivity = "1ed8b502-d754-442c-8d5d-10ac956f44a1" +SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[compat] +ComponentArrays = "0.15" +Corleone = "0.0.3" +ForwardDiff = "1.2" +Ipopt = "1.10" +LuxCore = "1.4" +ModelingToolkit = "11" +Optimization = "5" +OptimizationMOI = "1" +OrdinaryDiffEqBDF = "1.14.0, 2" +OrdinaryDiffEqTsit5 = "1, 2" +Random = "1" +SafeTestsets = "0.1" +SciMLBase = "2.141.0, 3" +SciMLSensitivity = "7.87" +SymbolicIndexingInterface = "0.3.43" +Test = "1" +julia = "1.10" + +[sources] +Corleone = {path = "../.."} diff --git a/test/qa/Project.toml b/test/qa/Project.toml new file mode 100644 index 00000000..a1d89045 --- /dev/null +++ b/test/qa/Project.toml @@ -0,0 +1,13 @@ +[deps] +Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +Corleone = "2751e0db-33e9-4374-b36d-b7219e1e6b40" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[sources] +Corleone = {path = "../.."} + +[compat] +Aqua = "0.8" +Corleone = "0.0.3" +Test = "1" +julia = "1.10" diff --git a/test/qa/qa.jl b/test/qa/qa.jl new file mode 100644 index 00000000..5e14cab7 --- /dev/null +++ b/test/qa/qa.jl @@ -0,0 +1,8 @@ +using Corleone +using Aqua + +@testset "Aqua" begin + Aqua.test_all( + Corleone + ) +end diff --git a/test/runtests.jl b/test/runtests.jl index 4869fd9a..601ae0b0 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,73 +4,124 @@ using SafeTestsets const GROUP = get(ENV, "GROUP", "All") -# Centralized sublibrary CI (SciML/.github sublibrary-tests.yml@v1) runs this -# root test suite with GROUP set to a sublibrary's CI group: the bare sublibrary -# name selects that sublibrary's "Core" group, and "_" selects a -# named group. Detect those here, activate the sublibrary's own test environment, -# and hand off to its runtests.jl (which parses GROUP itself). GROUP="All" (the -# local default) and GROUP="Corleone" run the main-package suite below. +# Centralized sublibrary CI (SciML/.github sublibrary-project-tests.yml@v1) tests +# each lib/ via the project model and never routes through this file. This +# dispatcher only matters when the root suite is invoked with a GROUP that names +# a sublibrary (e.g. local `GROUP=CorleoneOED julia test/runtests.jl`): the bare +# sublibrary name selects that sublibrary's "Core" group and "_" +# selects a named group. We then activate the sublibrary's own test environment +# and hand off to its runtests.jl via CORLEONE_TEST_GROUP. Otherwise the +# main-package suite below runs: GROUP="All" (the local default) and "Core" run +# the light Core group; "Examples" and "QA" each activate their own dep-adding +# test/ environment. const LIB_DIR = joinpath(@__DIR__, "..", "lib") -function _detect_sublibrary(group, lib_dir) - isdir(joinpath(lib_dir, group)) && return group +# QA (Aqua) runs in an isolated environment (test/qa) so its tooling deps never +# enter the main test target's resolve. On Julia < 1.11 the [sources] table is +# ignored, so develop the package by path to test the PR branch code. +function activate_qa_env() + Pkg.activate(joinpath(@__DIR__, "qa")) + Pkg.develop(Pkg.PackageSpec(path = joinpath(@__DIR__, ".."))) + return Pkg.instantiate() +end + +# Examples (dep-adding group): the example scripts need the heavy optimal-control +# stack (ModelingToolkit, Ipopt/OptimizationMOI, SciMLSensitivity), isolated in +# test/examples so they never enter the main test target's resolve. On Julia +# < 1.11 the [sources] table is ignored, so develop Corleone by path. +function activate_examples_env() + Pkg.activate(joinpath(@__DIR__, "examples")) + Pkg.develop(Pkg.PackageSpec(path = joinpath(@__DIR__, ".."))) + return Pkg.instantiate() +end + +# Scan underscores right-to-left to find the longest matching sublibrary prefix, +# returning (sublibrary, test_group) where test_group defaults to "Core". +function _detect_sublibrary_group(group, lib_dir) + isdir(joinpath(lib_dir, group)) && return (group, "Core") for i in length(group):-1:1 if group[i] == '_' && isdir(joinpath(lib_dir, group[1:(i - 1)])) - return group[1:(i - 1)] + return (group[1:(i - 1)], group[(i + 1):end]) end end - return nothing + return (group, "Core") end -const _SUBLIB = _detect_sublibrary(GROUP, LIB_DIR) +const _SUBLIB, _SUB_GROUP = _detect_sublibrary_group(GROUP, LIB_DIR) -if _SUBLIB !== nothing +if isdir(joinpath(LIB_DIR, _SUBLIB)) sublib_path = joinpath(LIB_DIR, _SUBLIB) Pkg.activate(sublib_path) # On Julia < 1.11 the [sources] table in Project.toml is ignored, so develop # the local path dependencies (e.g. Corleone) to test the PR branch code. + # Walk [sources] transitively in case a developed dependency carries its own. if VERSION < v"1.11.0-DEV.0" - toml = Pkg.TOML.parsefile(joinpath(sublib_path, "Project.toml")) - if haskey(toml, "sources") - specs = Pkg.PackageSpec[] - for (dep, spec) in toml["sources"] - if spec isa Dict && haskey(spec, "path") - dep_path = normpath(joinpath(sublib_path, spec["path"])) - isdir(dep_path) && push!(specs, Pkg.PackageSpec(path = dep_path)) + developed = Set{String}() + push!(developed, normpath(sublib_path)) + specs = Pkg.PackageSpec[] + queue = [sublib_path] + while !isempty(queue) + pkg_dir = popfirst!(queue) + toml_path = joinpath(pkg_dir, "Project.toml") + isfile(toml_path) || continue + toml = Pkg.TOML.parsefile(toml_path) + if haskey(toml, "sources") + for (dep_name, source_spec) in toml["sources"] + if source_spec isa Dict && haskey(source_spec, "path") + dep_path = normpath(joinpath(pkg_dir, source_spec["path"])) + if isdir(dep_path) && !(dep_path in developed) + push!(developed, dep_path) + push!(specs, Pkg.PackageSpec(path = dep_path)) + push!(queue, dep_path) + end + end end end - isempty(specs) || Pkg.develop(specs) end + isempty(specs) || Pkg.develop(specs) end - # Forward GROUP unchanged; the sublibrary runtests.jl parses it. - withenv("GROUP" => GROUP) do + # Hand the resolved test group to the sublibrary runtests.jl, which reads + # CORLEONE_TEST_GROUP (matching the SublibraryCI group-env-name). + withenv("CORLEONE_TEST_GROUP" => _SUB_GROUP) do Pkg.test(_SUBLIB; allow_reresolve = true) end else - using JET @testset "Corleone.jl" begin - @safetestset "Code quality (Aqua.jl)" begin - using Aqua - using Corleone - Aqua.test_all(Corleone) - end - @safetestset "Local controls" begin - include("local_controls.jl") - end - @safetestset "Multiple shooting" begin - include("multiple_shooting.jl") - end - @testset "Examples" begin - @safetestset "Lotka" begin - include("examples/lotka_oc.jl") + # Core: light tests that resolve in the main test target's environment. + # The default GROUP="All" runs only these no-extra-dep groups; the + # dep-adding groups below (Examples, QA) each activate their own + # isolated test/ environment and run only when selected. + if GROUP == "All" || GROUP == "Core" + @safetestset "Local controls" begin + include("core/local_controls.jl") end - @safetestset "Lotka MS" begin - include("examples/lotka_ms.jl") + @safetestset "Multiple shooting" begin + include("core/multiple_shooting.jl") end - @safetestset "Lotka MTK" begin - include("examples/mtk.jl") + end + + # Examples: the optimal-control example scripts pull in the heavy + # MTK + Ipopt/MOI + SciMLSensitivity stack, isolated in test/examples + # so those deps never enter the light main test target's resolve. + if GROUP == "Examples" + activate_examples_env() + @testset "Examples" begin + @safetestset "Lotka" begin + include("examples/lotka_oc.jl") + end + @safetestset "Lotka MS" begin + include("examples/lotka_ms.jl") + end + @safetestset "Lotka MTK" begin + include("examples/mtk.jl") + end end end + + if GROUP == "QA" + activate_qa_env() + @safetestset "Code quality (Aqua.jl)" include("qa/qa.jl") + end end end diff --git a/test/test_groups.toml b/test/test_groups.toml new file mode 100644 index 00000000..d2dfda16 --- /dev/null +++ b/test/test_groups.toml @@ -0,0 +1,8 @@ +[Core] +versions = ["lts", "1", "pre"] + +[Examples] +versions = ["lts", "1", "pre"] + +[QA] +versions = ["lts", "1"] From 03a33e9f5e5742cee3152acf9a3392ddf8192b28 Mon Sep 17 00:00:00 2001 From: Chris Rackauckas - Beep Boop Edition Date: Mon, 8 Jun 2026 11:48:50 +0000 Subject: [PATCH 55/73] Simplify root CI to grouped-tests.yml (matrix from test/test_groups.toml) (#81) Replace the hand-maintained group x version matrix in the root CI test job with the thin grouped-tests.yml@v1 caller, which computes the matrix from test/test_groups.toml. The non-default check-bounds: auto and coverage-directories: src inputs are preserved; group-env-name stays the default GROUP (which root test/runtests.jl reads). Co-authored-by: ChrisRackauckas-Claude Co-authored-by: Claude Opus 4.8 (1M context) --- .github/workflows/CI.yml | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 3ba5feca..6151a370 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -15,27 +15,8 @@ concurrency: cancel-in-progress: ${{ github.ref_name != github.event.repository.default_branch || github.ref != 'refs/tags/v*' }} jobs: tests: - name: "${{ matrix.group }}" - strategy: - fail-fast: false - matrix: - group: - - Core - - Examples - - QA - version: - - 'lts' - - '1' - - 'pre' - exclude: - # QA (Aqua) runs on lts + current release only, not pre. - - group: QA - version: 'pre' - uses: "SciML/.github/.github/workflows/tests.yml@v1" + uses: "SciML/.github/.github/workflows/grouped-tests.yml@v1" with: - julia-version: "${{ matrix.version }}" - group: "${{ matrix.group }}" - group-env-name: "GROUP" check-bounds: "auto" coverage-directories: "src" secrets: "inherit" From 928ef31193b01e147addffe4c006e02ed5901f56 Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Wed, 10 Jun 2026 15:03:17 +0200 Subject: [PATCH 56/73] fix dimension error of F_init in edge case --- lib/CorleoneOED/src/oed.jl | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/CorleoneOED/src/oed.jl b/lib/CorleoneOED/src/oed.jl index 7e0d6fc1..a5411245 100644 --- a/lib/CorleoneOED/src/oed.jl +++ b/lib/CorleoneOED/src/oed.jl @@ -168,6 +168,21 @@ Corleone.get_number_of_shooting_constraints(oed::OEDLayer{<:Any, <:Any, <:Any, < Corleone.get_number_of_shooting_constraints(oed::OEDLayer{<:Any, <:Any, <:Any, <:SingleShootingLayer}) = 0 Corleone.get_bounds(oed::OEDLayer; kwargs...) = Corleone.get_bounds(oed.layer; kwargs...) +get_size_F(oed::OEDLayer{true, true, <:Any}) = begin + size_hxG = size(oed.observed.fisher.getters) + if length(size_hxG) > 2 + size_hxG = size_hxG[1:2] + end + return (size_hxG[2],size_hxG[2]) +end +get_size_F(oed::OEDLayer{false, true, true}) = begin + sizeF = size(oed.observed.fisher.getters) + if length(sizeF) > 2 + sizeF = sizeF[1:2] + end + return sizeF +end + # This is the only case where we need to sample the trajectory function LuxCore.initialstates(rng::Random.AbstractRNG, oed::Union{OEDLayer{true, true, <:Any, <:SingleShootingLayer}, OEDLayer{false, true, true}}) (; layer, sampling_indices) = oed @@ -203,11 +218,7 @@ function LuxCore.initialstates(rng::Random.AbstractRNG, oed::Union{OEDLayer{true end : measurement_indices T = eltype(problem.u0) - size_F = size(oed.observed.fisher.getters) - if length(size_F) > 2 - size_F = size_F[1:2] - end - F_init = zeros(T, size_F) + F_init = zeros(T, get_size_F(oed)) return merge( st, (; @@ -225,7 +236,7 @@ function LuxCore.initialstates(rng::Random.AbstractRNG, oed::OEDLayer{true, true # Our goal is to build a weigthing matrix similar to the indexgrid grids = Corleone.get_timegrid.(controls) T = eltype(problem.u0) - F_init = zeros(T, size(oed.observed.fisher.getters)) + F_init = zeros(T, get_size_F(oed)) st_new = map(st) do sti tspan = (first(first(sti.tspans)), last(last(sti.tspans))) From c71dbc54551cdc6f24ae8c8bc53852265b63a46d Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Thu, 11 Jun 2026 09:01:03 +0200 Subject: [PATCH 57/73] Update lib/CorleoneOED/src/oed.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- lib/CorleoneOED/src/oed.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CorleoneOED/src/oed.jl b/lib/CorleoneOED/src/oed.jl index a5411245..6f8a69ae 100644 --- a/lib/CorleoneOED/src/oed.jl +++ b/lib/CorleoneOED/src/oed.jl @@ -173,7 +173,7 @@ get_size_F(oed::OEDLayer{true, true, <:Any}) = begin if length(size_hxG) > 2 size_hxG = size_hxG[1:2] end - return (size_hxG[2],size_hxG[2]) + return (size_hxG[2], size_hxG[2]) end get_size_F(oed::OEDLayer{false, true, true}) = begin sizeF = size(oed.observed.fisher.getters) From ac92d9fbf54b444a9606ec78e20d76228105eaa9 Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Thu, 11 Jun 2026 09:01:12 +0200 Subject: [PATCH 58/73] Update lib/CorleoneOED/src/oed.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- lib/CorleoneOED/src/oed.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CorleoneOED/src/oed.jl b/lib/CorleoneOED/src/oed.jl index 6f8a69ae..f08e9a7b 100644 --- a/lib/CorleoneOED/src/oed.jl +++ b/lib/CorleoneOED/src/oed.jl @@ -177,7 +177,7 @@ get_size_F(oed::OEDLayer{true, true, <:Any}) = begin end get_size_F(oed::OEDLayer{false, true, true}) = begin sizeF = size(oed.observed.fisher.getters) - if length(sizeF) > 2 + if length(sizeF) > 2 sizeF = sizeF[1:2] end return sizeF From 7b5367c2c377a8139e2eea66e8d3431b45cffc73 Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Thu, 11 Jun 2026 09:06:01 +0200 Subject: [PATCH 59/73] fix formatting --- lib/CorleoneOED/test/runtests.jl | 6 ++++-- lib/OptimalControlBenchmarks/test/runtests.jl | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/CorleoneOED/test/runtests.jl b/lib/CorleoneOED/test/runtests.jl index aeedd399..86ac23f6 100644 --- a/lib/CorleoneOED/test/runtests.jl +++ b/lib/CorleoneOED/test/runtests.jl @@ -7,10 +7,12 @@ using Pkg # ignored, so develop the package and its in-repo sibling by path. function activate_qa_env() Pkg.activate(joinpath(@__DIR__, "qa")) - Pkg.develop([ + Pkg.develop( + [ Pkg.PackageSpec(path = joinpath(@__DIR__, "..")), Pkg.PackageSpec(path = joinpath(@__DIR__, "..", "..", "..")) - ]) + ] + ) return Pkg.instantiate() end diff --git a/lib/OptimalControlBenchmarks/test/runtests.jl b/lib/OptimalControlBenchmarks/test/runtests.jl index 76c18329..95ef6bcd 100644 --- a/lib/OptimalControlBenchmarks/test/runtests.jl +++ b/lib/OptimalControlBenchmarks/test/runtests.jl @@ -8,10 +8,12 @@ using Pkg # ignored, so develop the package and its in-repo sibling by path. function activate_qa_env() Pkg.activate(joinpath(@__DIR__, "qa")) - Pkg.develop([ + Pkg.develop( + [ Pkg.PackageSpec(path = joinpath(@__DIR__, "..")), Pkg.PackageSpec(path = joinpath(@__DIR__, "..", "..", "..")) - ]) + ] + ) return Pkg.instantiate() end From 43d27851261dc54d31e7548cde691fcf68e981da Mon Sep 17 00:00:00 2001 From: Christoph Plate Date: Thu, 11 Jun 2026 09:06:35 +0200 Subject: [PATCH 60/73] fix formatting --- lib/CorleoneOED/test/runtests.jl | 4 ++-- lib/OptimalControlBenchmarks/test/runtests.jl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/CorleoneOED/test/runtests.jl b/lib/CorleoneOED/test/runtests.jl index 86ac23f6..45f8f18d 100644 --- a/lib/CorleoneOED/test/runtests.jl +++ b/lib/CorleoneOED/test/runtests.jl @@ -9,8 +9,8 @@ function activate_qa_env() Pkg.activate(joinpath(@__DIR__, "qa")) Pkg.develop( [ - Pkg.PackageSpec(path = joinpath(@__DIR__, "..")), - Pkg.PackageSpec(path = joinpath(@__DIR__, "..", "..", "..")) + Pkg.PackageSpec(path = joinpath(@__DIR__, "..")), + Pkg.PackageSpec(path = joinpath(@__DIR__, "..", "..", "..")) ] ) return Pkg.instantiate() diff --git a/lib/OptimalControlBenchmarks/test/runtests.jl b/lib/OptimalControlBenchmarks/test/runtests.jl index 95ef6bcd..8e7becb4 100644 --- a/lib/OptimalControlBenchmarks/test/runtests.jl +++ b/lib/OptimalControlBenchmarks/test/runtests.jl @@ -10,8 +10,8 @@ function activate_qa_env() Pkg.activate(joinpath(@__DIR__, "qa")) Pkg.develop( [ - Pkg.PackageSpec(path = joinpath(@__DIR__, "..")), - Pkg.PackageSpec(path = joinpath(@__DIR__, "..", "..", "..")) + Pkg.PackageSpec(path = joinpath(@__DIR__, "..")), + Pkg.PackageSpec(path = joinpath(@__DIR__, "..", "..", "..")) ] ) return Pkg.instantiate() From ee1f1f22570cc0957b1d37d1f69047c9bf43f7f5 Mon Sep 17 00:00:00 2001 From: Carl Julius Martensen Date: Thu, 11 Jun 2026 15:25:09 +0200 Subject: [PATCH 61/73] Update tests - Replace RNG with StableRNG - Replace equality check with isapprox --- Project.toml | 3 ++- test/core/local_controls.jl | 6 +++--- test/core/multiple_shooting.jl | 19 +++++++++---------- test/examples/lotka_ms.jl | 4 ++-- test/examples/lotka_oc.jl | 4 ++-- test/examples/mtk.jl | 8 +++++--- 6 files changed, 23 insertions(+), 21 deletions(-) diff --git a/Project.toml b/Project.toml index 1ffa89bc..83d0f031 100644 --- a/Project.toml +++ b/Project.toml @@ -59,7 +59,8 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" +StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["ComponentArrays", "Test", "OrdinaryDiffEqTsit5", "LinearAlgebra", "SafeTestsets", "Pkg"] +test = ["ComponentArrays", "Test", "OrdinaryDiffEqTsit5", "LinearAlgebra", "SafeTestsets", "Pkg", "StableRNGs"] diff --git a/test/core/local_controls.jl b/test/core/local_controls.jl index bbb27bcf..764be3d4 100644 --- a/test/core/local_controls.jl +++ b/test/core/local_controls.jl @@ -1,10 +1,10 @@ using Corleone using OrdinaryDiffEqTsit5 using Test -using Random +using StableRNGs using LuxCore -rng = Random.default_rng() +rng = StableRNG(42) c = ControlParameter(0:0.01:1.0) lb, ub = Corleone.get_bounds(c) @@ -70,7 +70,7 @@ c5 = ControlParameter(1.0:10.0, bounds = (ones(10), -ones(10)), controls = colle ) layer = Corleone.SingleShootingLayer(prob, Tsit5(), controls = ([2, 3, 1] .=> [c2, c3, c1])) - ps, st = LuxCore.setup(Random.default_rng(), layer) + ps, st = LuxCore.setup(rng, layer) sol, _ = layer(nothing, ps, st) @test all(0.6 .<= sol[:con3] .<= 0.8) diff --git a/test/core/multiple_shooting.jl b/test/core/multiple_shooting.jl index 78173870..a3bf968c 100644 --- a/test/core/multiple_shooting.jl +++ b/test/core/multiple_shooting.jl @@ -1,12 +1,12 @@ using Corleone using OrdinaryDiffEqTsit5 using Test -using Random +using StableRNGs using LuxCore using ComponentArrays using LinearAlgebra -rng = Random.default_rng() +rng = StableRNG(42) function lotka_dynamics(u, p, t) return [ @@ -51,7 +51,7 @@ lb, ub = Corleone.get_bounds(layer) @test Corleone.get_block_structure(layer) == vcat(0, blocks) traj, st2 = layer(nothing, ps, st) @test Corleone.is_shooting_solution(traj) - @test Corleone.shooting_constraints(traj) == [ + @test isapprox(Corleone.shooting_constraints(traj), [ 1.375754960969482, 0.22357357513551113, 1.2417260108009396, @@ -67,12 +67,12 @@ lb, ub = Corleone.get_bounds(layer) 0.0, 0.0, 0.0, - ] + ], atol=1e-10) @test Corleone.get_number_of_shooting_constraints(layer) == length(Corleone.shooting_constraints(traj)) == 15 res = zeros(30) - @test Corleone.shooting_constraints!(res[10:24], traj) == [ + @test isapprox(Corleone.shooting_constraints!(res[10:24], traj), [ 1.375754960969482, 0.22357357513551113, 1.2417260108009396, @@ -88,9 +88,9 @@ lb, ub = Corleone.get_bounds(layer) 0.0, 0.0, 0.0, - ] + ], atol=1e-10) @views Corleone.shooting_constraints!(res[10:24], traj) - @test res[10:24] == [ + @test isapprox(res[10:24], [ 1.375754960969482, 0.22357357513551113, 1.2417260108009396, @@ -106,7 +106,7 @@ lb, ub = Corleone.get_bounds(layer) 0.0, 0.0, 0.0, - ] + ], atol=1e-10) end @testset "Parallel" begin @@ -211,8 +211,7 @@ end traj, _ = layer(nothing, ps, st) @test isempty(ps.interval_1.u0) @test ps.interval_2.u0 == [0.5, 3.0, -5.0] - @test ps.interval_3.u0 == - [0.2875960819428889, 0.27699894224421623, -1.088467272254529] + @test isapprox(ps.interval_3.u0, [0.2875960819428889, 0.27699894224421623, -1.088467272254529], atol=1e-10) @test ps.interval_4.u0 == u0 end end diff --git a/test/examples/lotka_ms.jl b/test/examples/lotka_ms.jl index 35709c0d..27401153 100644 --- a/test/examples/lotka_ms.jl +++ b/test/examples/lotka_ms.jl @@ -1,12 +1,12 @@ using Corleone using OrdinaryDiffEqTsit5 using Test -using Random +using StableRNGs using LuxCore using ComponentArrays using Optimization, OptimizationMOI, Ipopt -rng = Random.default_rng() +rng = StableRNG(42) function lotka_dynamics!(du, u, p, t) du[1] = u[1] - p[2] * prod(u[1:2]) - 0.4 * p[1] * u[1] diff --git a/test/examples/lotka_oc.jl b/test/examples/lotka_oc.jl index ff8246a1..77093886 100644 --- a/test/examples/lotka_oc.jl +++ b/test/examples/lotka_oc.jl @@ -1,7 +1,7 @@ using Corleone using OrdinaryDiffEqTsit5 using Test -using Random +using StableRNGs using LuxCore using ComponentArrays using Optimization, OptimizationMOI, Ipopt @@ -11,7 +11,7 @@ using SciMLSensitivity.ReverseDiff using SciMLSensitivity.Zygote using SymbolicIndexingInterface -rng = Random.default_rng() +rng = StableRNG(42) function lotka_dynamics!(du, u, p, t) du[1] = u[1] - p[2] * prod(u[1:2]) - 0.4 * p[1] * u[1] diff --git a/test/examples/mtk.jl b/test/examples/mtk.jl index f17f0c9f..8922b906 100644 --- a/test/examples/mtk.jl +++ b/test/examples/mtk.jl @@ -6,7 +6,9 @@ using OrdinaryDiffEqTsit5 using ComponentArrays, ForwardDiff using Optimization using OptimizationMOI, Ipopt -using LuxCore, Random +using LuxCore, StableRNGs + +rng = StableRNG(42) @variables x(..) = 0.5 [tunable = false] y(..) = 0.7 [tunable = false] @@ -49,7 +51,7 @@ cons = [ @test size(optprob.lcons, 1) == size(optprob.ucons, 1) == length(cons) - ps, st = LuxCore.setup(Random.default_rng(), dynopt.layer) + ps, st = LuxCore.setup(rng, dynopt.layer) traj, _ = dynopt.layer(nothing, ps, st) @@ -82,7 +84,7 @@ end @test size(optprob.lcons, 1) == size(optprob.ucons, 1) == length(cons) + Corleone.get_number_of_shooting_constraints(dynopt.layer) - ps, st = LuxCore.setup(Random.default_rng(), dynopt.layer) + ps, st = LuxCore.setup(rng, dynopt.layer) traj, _ = dynopt.layer(nothing, ps, st) From 9b0c1af1308ad6b6c86ab9ec5e1dfc0268ca8f63 Mon Sep 17 00:00:00 2001 From: Carl Julius Martensen Date: Fri, 12 Jun 2026 06:29:23 +0200 Subject: [PATCH 62/73] Format --- test/core/multiple_shooting.jl | 110 +++++++++++++++++---------------- 1 file changed, 58 insertions(+), 52 deletions(-) diff --git a/test/core/multiple_shooting.jl b/test/core/multiple_shooting.jl index a3bf968c..9d1c990a 100644 --- a/test/core/multiple_shooting.jl +++ b/test/core/multiple_shooting.jl @@ -51,62 +51,68 @@ lb, ub = Corleone.get_bounds(layer) @test Corleone.get_block_structure(layer) == vcat(0, blocks) traj, st2 = layer(nothing, ps, st) @test Corleone.is_shooting_solution(traj) - @test isapprox(Corleone.shooting_constraints(traj), [ - 1.375754960969482, - 0.22357357513551113, - 1.2417260108009396, - 1.3757549609694817, - 0.22357357513551046, - 1.2417260108009387, - 1.3757549609694821, - 0.2235735751355138, - 1.2417260108009425, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - ], atol=1e-10) + @test isapprox( + Corleone.shooting_constraints(traj), [ + 1.375754960969482, + 0.22357357513551113, + 1.2417260108009396, + 1.3757549609694817, + 0.22357357513551046, + 1.2417260108009387, + 1.3757549609694821, + 0.2235735751355138, + 1.2417260108009425, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + ], atol = 1.0e-10 + ) @test Corleone.get_number_of_shooting_constraints(layer) == length(Corleone.shooting_constraints(traj)) == 15 res = zeros(30) - @test isapprox(Corleone.shooting_constraints!(res[10:24], traj), [ - 1.375754960969482, - 0.22357357513551113, - 1.2417260108009396, - 1.3757549609694817, - 0.22357357513551046, - 1.2417260108009387, - 1.3757549609694821, - 0.2235735751355138, - 1.2417260108009425, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - ], atol=1e-10) + @test isapprox( + Corleone.shooting_constraints!(res[10:24], traj), [ + 1.375754960969482, + 0.22357357513551113, + 1.2417260108009396, + 1.3757549609694817, + 0.22357357513551046, + 1.2417260108009387, + 1.3757549609694821, + 0.2235735751355138, + 1.2417260108009425, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + ], atol = 1.0e-10 + ) @views Corleone.shooting_constraints!(res[10:24], traj) - @test isapprox(res[10:24], [ - 1.375754960969482, - 0.22357357513551113, - 1.2417260108009396, - 1.3757549609694817, - 0.22357357513551046, - 1.2417260108009387, - 1.3757549609694821, - 0.2235735751355138, - 1.2417260108009425, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - ], atol=1e-10) + @test isapprox( + res[10:24], [ + 1.375754960969482, + 0.22357357513551113, + 1.2417260108009396, + 1.3757549609694817, + 0.22357357513551046, + 1.2417260108009387, + 1.3757549609694821, + 0.2235735751355138, + 1.2417260108009425, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + ], atol = 1.0e-10 + ) end @testset "Parallel" begin @@ -211,7 +217,7 @@ end traj, _ = layer(nothing, ps, st) @test isempty(ps.interval_1.u0) @test ps.interval_2.u0 == [0.5, 3.0, -5.0] - @test isapprox(ps.interval_3.u0, [0.2875960819428889, 0.27699894224421623, -1.088467272254529], atol=1e-10) + @test isapprox(ps.interval_3.u0, [0.2875960819428889, 0.27699894224421623, -1.088467272254529], atol = 1.0e-10) @test ps.interval_4.u0 == u0 end end From 015d654652445cecd921f6ef46133e77acae1f3c Mon Sep 17 00:00:00 2001 From: JuliusMartensen Date: Fri, 12 Jun 2026 07:59:06 +0200 Subject: [PATCH 63/73] Add StableRNGs compat bound to Project.toml --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 83d0f031..bd0c97b5 100644 --- a/Project.toml +++ b/Project.toml @@ -47,6 +47,7 @@ Random = "1" RecursiveArrayTools = "3.37.0" Reexport = "1.2.2" SafeTestsets = "0.1" +StableRNGs = "1" SciMLBase = "2.141.0, 3" SciMLStructures = "1.7" SymbolicIndexingInterface = "0.3.43" From 2c42d948d1ef2789554428686b4723f965821fd2 Mon Sep 17 00:00:00 2001 From: Chris Rackauckas - Beep Boop Edition Date: Sun, 14 Jun 2026 23:54:29 +0000 Subject: [PATCH 64/73] Use SciMLTesting v1.2 run_tests dispatcher (#85) Replace the hand-written GROUP dispatcher in test/runtests.jl with a single SciMLTesting.run_tests call (explicit-args mode, the monorepo shape). The Core body @safetestset-includes the existing test/core/ files; Examples and QA become env-bearing group specs that activate test/examples and test/qa (the v1.2 activate_group_env replicates the old activate_examples_env/activate_qa_env). A curated all = ["Core"] keeps GROUP="All" running only the light Core group, and sublib_env = "CORLEONE_TEST_GROUP" + lib_dir keep sublibrary routing semantics. The explicit sublibrary pre-step (detect_sublibrary_group + the Julia < 1.11 transitive [sources] develop walk + withenv("CORLEONE_TEST_GROUP") Pkg.test) is preserved verbatim ahead of the run_tests call, so the sublibrary Pkg.test path is byte-for-byte unchanged. The exact set of tests run under each GROUP value (All, Core, Examples, QA, and sublibrary names) is unchanged. Add SciMLTesting to the root test target (keep Pkg + SafeTestsets, since the sublib pre-step still uses Pkg) and to the examples/qa sub-environments. Widen SafeTestsets compat to "0.1, 1". test_groups.toml is unchanged. Co-authored-by: ChrisRackauckas-Claude Co-authored-by: Claude Opus 4.8 (1M context) --- Project.toml | 6 +- test/examples/Project.toml | 4 +- test/qa/Project.toml | 4 ++ test/runtests.jl | 131 ++++++++++++++----------------------- 4 files changed, 60 insertions(+), 85 deletions(-) diff --git a/Project.toml b/Project.toml index bd0c97b5..1c2946e3 100644 --- a/Project.toml +++ b/Project.toml @@ -46,10 +46,11 @@ Pkg = "1" Random = "1" RecursiveArrayTools = "3.37.0" Reexport = "1.2.2" -SafeTestsets = "0.1" +SafeTestsets = "0.1, 1" StableRNGs = "1" SciMLBase = "2.141.0, 3" SciMLStructures = "1.7" +SciMLTesting = "1" SymbolicIndexingInterface = "0.3.43" Test = "1" julia = "1.10" @@ -60,8 +61,9 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" +SciMLTesting = "09d9d899-5365-40a9-917a-5f67fddea283" StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["ComponentArrays", "Test", "OrdinaryDiffEqTsit5", "LinearAlgebra", "SafeTestsets", "Pkg", "StableRNGs"] +test = ["ComponentArrays", "Test", "OrdinaryDiffEqTsit5", "LinearAlgebra", "SafeTestsets", "SciMLTesting", "Pkg", "StableRNGs"] diff --git a/test/examples/Project.toml b/test/examples/Project.toml index ffc7850e..4c7c8588 100644 --- a/test/examples/Project.toml +++ b/test/examples/Project.toml @@ -13,6 +13,7 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" SciMLSensitivity = "1ed8b502-d754-442c-8d5d-10ac956f44a1" +SciMLTesting = "09d9d899-5365-40a9-917a-5f67fddea283" SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" @@ -28,9 +29,10 @@ OptimizationMOI = "1" OrdinaryDiffEqBDF = "1.14.0, 2" OrdinaryDiffEqTsit5 = "1, 2" Random = "1" -SafeTestsets = "0.1" +SafeTestsets = "0.1, 1" SciMLBase = "2.141.0, 3" SciMLSensitivity = "7.87" +SciMLTesting = "1" SymbolicIndexingInterface = "0.3.43" Test = "1" julia = "1.10" diff --git a/test/qa/Project.toml b/test/qa/Project.toml index a1d89045..29584c4a 100644 --- a/test/qa/Project.toml +++ b/test/qa/Project.toml @@ -1,6 +1,8 @@ [deps] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" Corleone = "2751e0db-33e9-4374-b36d-b7219e1e6b40" +SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" +SciMLTesting = "09d9d899-5365-40a9-917a-5f67fddea283" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [sources] @@ -9,5 +11,7 @@ Corleone = {path = "../.."} [compat] Aqua = "0.8" Corleone = "0.0.3" +SafeTestsets = "0.1, 1" +SciMLTesting = "1" Test = "1" julia = "1.10" diff --git a/test/runtests.jl b/test/runtests.jl index 601ae0b0..b94e4527 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,8 +1,9 @@ using Pkg -using Test -using SafeTestsets +using SafeTestsets, Test +using SciMLTesting -const GROUP = get(ENV, "GROUP", "All") +const GROUP = current_group() +const LIB_DIR = joinpath(dirname(@__DIR__), "lib") # Centralized sublibrary CI (SciML/.github sublibrary-project-tests.yml@v1) tests # each lib/ via the project model and never routes through this file. This @@ -10,47 +11,15 @@ const GROUP = get(ENV, "GROUP", "All") # a sublibrary (e.g. local `GROUP=CorleoneOED julia test/runtests.jl`): the bare # sublibrary name selects that sublibrary's "Core" group and "_" # selects a named group. We then activate the sublibrary's own test environment -# and hand off to its runtests.jl via CORLEONE_TEST_GROUP. Otherwise the -# main-package suite below runs: GROUP="All" (the local default) and "Core" run -# the light Core group; "Examples" and "QA" each activate their own dep-adding -# test/ environment. -const LIB_DIR = joinpath(@__DIR__, "..", "lib") +# and hand off to its runtests.jl via CORLEONE_TEST_GROUP. The sublibrary Pkg.test +# is done explicitly here (rather than via run_tests's built-in lib_dir path) so the +# Julia < 1.11 transitive [sources] develop walk is preserved verbatim. Otherwise +# the main-package suite below runs via run_tests: GROUP="All"/"Core" run the light +# Core group; "Examples" and "QA" each activate their own test/ environment. +base_group, test_group = detect_sublibrary_group(GROUP, LIB_DIR) -# QA (Aqua) runs in an isolated environment (test/qa) so its tooling deps never -# enter the main test target's resolve. On Julia < 1.11 the [sources] table is -# ignored, so develop the package by path to test the PR branch code. -function activate_qa_env() - Pkg.activate(joinpath(@__DIR__, "qa")) - Pkg.develop(Pkg.PackageSpec(path = joinpath(@__DIR__, ".."))) - return Pkg.instantiate() -end - -# Examples (dep-adding group): the example scripts need the heavy optimal-control -# stack (ModelingToolkit, Ipopt/OptimizationMOI, SciMLSensitivity), isolated in -# test/examples so they never enter the main test target's resolve. On Julia -# < 1.11 the [sources] table is ignored, so develop Corleone by path. -function activate_examples_env() - Pkg.activate(joinpath(@__DIR__, "examples")) - Pkg.develop(Pkg.PackageSpec(path = joinpath(@__DIR__, ".."))) - return Pkg.instantiate() -end - -# Scan underscores right-to-left to find the longest matching sublibrary prefix, -# returning (sublibrary, test_group) where test_group defaults to "Core". -function _detect_sublibrary_group(group, lib_dir) - isdir(joinpath(lib_dir, group)) && return (group, "Core") - for i in length(group):-1:1 - if group[i] == '_' && isdir(joinpath(lib_dir, group[1:(i - 1)])) - return (group[1:(i - 1)], group[(i + 1):end]) - end - end - return (group, "Core") -end - -const _SUBLIB, _SUB_GROUP = _detect_sublibrary_group(GROUP, LIB_DIR) - -if isdir(joinpath(LIB_DIR, _SUBLIB)) - sublib_path = joinpath(LIB_DIR, _SUBLIB) +if !isempty(base_group) && isdir(joinpath(LIB_DIR, base_group)) + sublib_path = joinpath(LIB_DIR, base_group) Pkg.activate(sublib_path) # On Julia < 1.11 the [sources] table in Project.toml is ignored, so develop # the local path dependencies (e.g. Corleone) to test the PR branch code. @@ -82,51 +51,49 @@ if isdir(joinpath(LIB_DIR, _SUBLIB)) end # Hand the resolved test group to the sublibrary runtests.jl, which reads # CORLEONE_TEST_GROUP (matching the SublibraryCI group-env-name). - withenv("CORLEONE_TEST_GROUP" => _SUB_GROUP) do - Pkg.test(_SUBLIB; allow_reresolve = true) + withenv("CORLEONE_TEST_GROUP" => test_group) do + Pkg.test(base_group; allow_reresolve = true) end else - @testset "Corleone.jl" begin + run_tests(; # Core: light tests that resolve in the main test target's environment. - # The default GROUP="All" runs only these no-extra-dep groups; the - # dep-adding groups below (Examples, QA) each activate their own - # isolated test/ environment and run only when selected. - if GROUP == "All" || GROUP == "Core" + core = function () @safetestset "Local controls" begin include("core/local_controls.jl") end - @safetestset "Multiple shooting" begin + return @safetestset "Multiple shooting" begin include("core/multiple_shooting.jl") end - end - - # Examples: the optimal-control example scripts pull in the heavy - # MTK + Ipopt/MOI + SciMLSensitivity stack, isolated in test/examples - # so those deps never enter the light main test target's resolve. - if GROUP == "Examples" - activate_examples_env() - @testset "Examples" begin - @safetestset "Lotka" begin - include("examples/lotka_oc.jl") - end - @safetestset "Lotka MS" begin - include("examples/lotka_ms.jl") - end - @safetestset "Lotka MTK" begin - include("examples/mtk.jl") - end - end - end - - if GROUP == "QA" - activate_qa_env() - @safetestset "Code quality (Aqua.jl)" include("qa/qa.jl") - end - end + end, + groups = Dict( + # Examples: the optimal-control example scripts pull in the heavy + # MTK + Ipopt/MOI + SciMLSensitivity stack, isolated in test/examples + # so those deps never enter the light main test target's resolve. + "Examples" => (; + env = joinpath(@__DIR__, "examples"), + body = function () + @safetestset "Lotka" begin + include("examples/lotka_oc.jl") + end + @safetestset "Lotka MS" begin + include("examples/lotka_ms.jl") + end + return @safetestset "Lotka MTK" begin + include("examples/mtk.jl") + end + end, + ), + ), + qa = (; + env = joinpath(@__DIR__, "qa"), + body = function () + return @safetestset "Code quality (Aqua.jl)" include("qa/qa.jl") + end, + ), + # The original ran only the Core group for the default GROUP="All"; the + # dep-adding Examples and QA groups ran only when explicitly selected. + all = ["Core"], + sublib_env = "CORLEONE_TEST_GROUP", + lib_dir = LIB_DIR, + ) end - -# What to test? -# local_controls.jl: -# - construction of index_grid, get_subvector_indices -> Julius -# general: -# - more convergence? Lotka OED From 6335ba5ddff4438545776b849addc4868973ad10 Mon Sep 17 00:00:00 2001 From: Christopher Rackauckas Date: Tue, 23 Jun 2026 01:32:47 +0000 Subject: [PATCH 65/73] Release v0.0.4 (#100) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 1c2946e3..f1414d96 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Corleone" uuid = "2751e0db-33e9-4374-b36d-b7219e1e6b40" authors = ["Carl Julius Martensen ", "Christoph Plate ", "and contributors"] -version = "0.0.3" +version = "0.0.4" [deps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" From 8806428e4459a227722ac0d2bd08c8c628a0a920 Mon Sep 17 00:00:00 2001 From: Chris Rackauckas - Beep Boop Edition Date: Wed, 24 Jun 2026 12:02:58 +0000 Subject: [PATCH 66/73] Bump sublibrary Corleone compat 0.0.3 -> 0.0.4 to match released parent (#101) PR #100 released parent Corleone v0.0.4 but did not update the sublibraries' Corleone [compat], which still pinned "0.0.3". For pre-1.0 versions the caret range "0.0.3" means [0.0.3, 0.0.4), so the path-sourced parent at 0.0.4 has an empty intersection with that compat. The downgrade-sublibraries lanes (lib/CorleoneOED, lib/OptimalControlBenchmarks) fail during the test-sandbox resolve with: ERROR: LoadError: empty intersection between Corleone@0.0.4 and project compatibility 0.0.3 On Julia 1.10 (lts) [sources] is ignored and the parent is develop'd by path at 0.0.4, so the conflict is unavoidable until compat admits 0.0.4. Also bumps the matching Corleone compat in the QA and examples test sub-projects (which path- source the parent) so the QA/Examples lanes don't inherit the same wall. Co-authored-by: ChrisRackauckas-Claude Co-authored-by: Claude Opus 4.8 (1M context) --- lib/CorleoneOED/Project.toml | 2 +- lib/CorleoneOED/test/qa/Project.toml | 2 +- lib/OptimalControlBenchmarks/Project.toml | 2 +- lib/OptimalControlBenchmarks/test/qa/Project.toml | 2 +- test/examples/Project.toml | 2 +- test/qa/Project.toml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/CorleoneOED/Project.toml b/lib/CorleoneOED/Project.toml index 9d50eb7f..7cdfae71 100644 --- a/lib/CorleoneOED/Project.toml +++ b/lib/CorleoneOED/Project.toml @@ -27,7 +27,7 @@ CorleoneOEDOptimizationExtension = ["Optimization", "ComponentArrays"] CorleoneOEDZygoteExtension = "Zygote" [compat] -Corleone = "0.0.3" +Corleone = "0.0.4" ComponentArrays = "0.15" DocStringExtensions = "0.9" ForwardDiff = "1.3" diff --git a/lib/CorleoneOED/test/qa/Project.toml b/lib/CorleoneOED/test/qa/Project.toml index aef4671d..da4640a0 100644 --- a/lib/CorleoneOED/test/qa/Project.toml +++ b/lib/CorleoneOED/test/qa/Project.toml @@ -10,7 +10,7 @@ CorleoneOED = {path = "../.."} [compat] Aqua = "0.8" -Corleone = "0.0.3" +Corleone = "0.0.4" CorleoneOED = "0.0.4" Test = "1" julia = "1.10" diff --git a/lib/OptimalControlBenchmarks/Project.toml b/lib/OptimalControlBenchmarks/Project.toml index 5c16ce3f..b20e35dd 100644 --- a/lib/OptimalControlBenchmarks/Project.toml +++ b/lib/OptimalControlBenchmarks/Project.toml @@ -33,7 +33,7 @@ UnoSolver = "1baa60ac-02f7-4b39-a7a8-2f4f58486b05" [compat] ComponentArrays = "0.15" -Corleone = "0.0.3" +Corleone = "0.0.4" DocStringExtensions = "0.9" ForwardDiff = "1.3" Ipopt = "1" diff --git a/lib/OptimalControlBenchmarks/test/qa/Project.toml b/lib/OptimalControlBenchmarks/test/qa/Project.toml index f821e30a..afbecffa 100644 --- a/lib/OptimalControlBenchmarks/test/qa/Project.toml +++ b/lib/OptimalControlBenchmarks/test/qa/Project.toml @@ -10,7 +10,7 @@ OptimalControlBenchmarks = {path = "../.."} [compat] Aqua = "0.8" -Corleone = "0.0.3" +Corleone = "0.0.4" OptimalControlBenchmarks = "0.0.3" Test = "1" julia = "1.10" diff --git a/test/examples/Project.toml b/test/examples/Project.toml index 4c7c8588..f0b4450b 100644 --- a/test/examples/Project.toml +++ b/test/examples/Project.toml @@ -19,7 +19,7 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] ComponentArrays = "0.15" -Corleone = "0.0.3" +Corleone = "0.0.4" ForwardDiff = "1.2" Ipopt = "1.10" LuxCore = "1.4" diff --git a/test/qa/Project.toml b/test/qa/Project.toml index 29584c4a..cc6e3d6b 100644 --- a/test/qa/Project.toml +++ b/test/qa/Project.toml @@ -10,7 +10,7 @@ Corleone = {path = "../.."} [compat] Aqua = "0.8" -Corleone = "0.0.3" +Corleone = "0.0.4" SafeTestsets = "0.1, 1" SciMLTesting = "1" Test = "1" From b9e0c9b052f83f190179a84ccac96077fee6642d Mon Sep 17 00:00:00 2001 From: Christopher Rackauckas Date: Fri, 26 Jun 2026 21:33:26 +0000 Subject: [PATCH 67/73] Release v0.0.5 (#104) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index f1414d96..698e3827 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Corleone" uuid = "2751e0db-33e9-4374-b36d-b7219e1e6b40" authors = ["Carl Julius Martensen ", "Christoph Plate ", "and contributors"] -version = "0.0.4" +version = "0.0.5" [deps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" From 013ecb47aec0aeb85a0190a4cb6a466e5da03469 Mon Sep 17 00:00:00 2001 From: Christopher Rackauckas Date: Fri, 26 Jun 2026 21:57:54 +0000 Subject: [PATCH 68/73] Release CorleoneOED v0.0.5 (#105) --- lib/CorleoneOED/Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CorleoneOED/Project.toml b/lib/CorleoneOED/Project.toml index 7cdfae71..c313b4b4 100644 --- a/lib/CorleoneOED/Project.toml +++ b/lib/CorleoneOED/Project.toml @@ -1,7 +1,7 @@ name = "CorleoneOED" uuid = "6590a4b1-d036-41fe-a5b4-03a980244101" authors = ["Christoph Plate", "Carl Julius Martensen", "Sebastian Sager"] -version = "0.0.4" +version = "0.0.5" [deps] Corleone = "2751e0db-33e9-4374-b36d-b7219e1e6b40" From bbcffe94c020d0a463ef487dbccd580668a098ea Mon Sep 17 00:00:00 2001 From: Chris Rackauckas - Beep Boop Edition Date: Sun, 28 Jun 2026 22:55:09 +0000 Subject: [PATCH 69/73] Bump sublibrary/test Corleone compat 0.0.4 -> 0.0.5 to match released parent (#106) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PRs #104/#105 released parent Corleone v0.0.5 but did not update the sublibraries' / test sub-projects' Corleone [compat], which still pinned "0.0.4". For pre-1.0 versions the caret range "0.0.4" means [0.0.4, 0.0.5), so the path-sourced parent at 0.0.5 ([sources] Corleone = {path = "../.."}) has an empty intersection with that compat. The downgrade-sublibraries lanes (lib/CorleoneOED, lib/OptimalControlBenchmarks) — and the regular Sublibrary CI / QA lanes — fail during the test-sandbox resolve with: ERROR: LoadError: empty intersection between Corleone@0.0.5 and project compatibility 0.0.4 The downgrade root (main) lane is unaffected because the root package does not depend on itself. This mirrors PR #101 (the same 0.0.3 -> 0.0.4 bump after the v0.0.4 parent release). Also bumps the now-stale CorleoneOED self-compat (0.0.4 -> 0.0.5) in lib/CorleoneOED/test/qa/Project.toml, since CorleoneOED was released as v0.0.5 (#105) and is path-sourced there. OptimalControlBenchmarks stays at 0.0.3 (matches its current version). Verified locally on Julia 1.10.11 (lts): Pkg.develop of the path-sourced parent throws the empty-intersection error on the unmodified tree and succeeds (Corleone v0.0.5 accepted) with this change. Ignore until reviewed by @ChrisRackauckas. Co-authored-by: ChrisRackauckas-Claude --- lib/CorleoneOED/Project.toml | 2 +- lib/CorleoneOED/test/qa/Project.toml | 4 ++-- lib/OptimalControlBenchmarks/Project.toml | 2 +- lib/OptimalControlBenchmarks/test/qa/Project.toml | 2 +- test/examples/Project.toml | 2 +- test/qa/Project.toml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/CorleoneOED/Project.toml b/lib/CorleoneOED/Project.toml index c313b4b4..be540552 100644 --- a/lib/CorleoneOED/Project.toml +++ b/lib/CorleoneOED/Project.toml @@ -27,7 +27,7 @@ CorleoneOEDOptimizationExtension = ["Optimization", "ComponentArrays"] CorleoneOEDZygoteExtension = "Zygote" [compat] -Corleone = "0.0.4" +Corleone = "0.0.5" ComponentArrays = "0.15" DocStringExtensions = "0.9" ForwardDiff = "1.3" diff --git a/lib/CorleoneOED/test/qa/Project.toml b/lib/CorleoneOED/test/qa/Project.toml index da4640a0..ba585c7d 100644 --- a/lib/CorleoneOED/test/qa/Project.toml +++ b/lib/CorleoneOED/test/qa/Project.toml @@ -10,7 +10,7 @@ CorleoneOED = {path = "../.."} [compat] Aqua = "0.8" -Corleone = "0.0.4" -CorleoneOED = "0.0.4" +Corleone = "0.0.5" +CorleoneOED = "0.0.5" Test = "1" julia = "1.10" diff --git a/lib/OptimalControlBenchmarks/Project.toml b/lib/OptimalControlBenchmarks/Project.toml index b20e35dd..d317325d 100644 --- a/lib/OptimalControlBenchmarks/Project.toml +++ b/lib/OptimalControlBenchmarks/Project.toml @@ -33,7 +33,7 @@ UnoSolver = "1baa60ac-02f7-4b39-a7a8-2f4f58486b05" [compat] ComponentArrays = "0.15" -Corleone = "0.0.4" +Corleone = "0.0.5" DocStringExtensions = "0.9" ForwardDiff = "1.3" Ipopt = "1" diff --git a/lib/OptimalControlBenchmarks/test/qa/Project.toml b/lib/OptimalControlBenchmarks/test/qa/Project.toml index afbecffa..6efbae1d 100644 --- a/lib/OptimalControlBenchmarks/test/qa/Project.toml +++ b/lib/OptimalControlBenchmarks/test/qa/Project.toml @@ -10,7 +10,7 @@ OptimalControlBenchmarks = {path = "../.."} [compat] Aqua = "0.8" -Corleone = "0.0.4" +Corleone = "0.0.5" OptimalControlBenchmarks = "0.0.3" Test = "1" julia = "1.10" diff --git a/test/examples/Project.toml b/test/examples/Project.toml index f0b4450b..f99bc33b 100644 --- a/test/examples/Project.toml +++ b/test/examples/Project.toml @@ -19,7 +19,7 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] ComponentArrays = "0.15" -Corleone = "0.0.4" +Corleone = "0.0.5" ForwardDiff = "1.2" Ipopt = "1.10" LuxCore = "1.4" diff --git a/test/qa/Project.toml b/test/qa/Project.toml index cc6e3d6b..a2f51f21 100644 --- a/test/qa/Project.toml +++ b/test/qa/Project.toml @@ -10,7 +10,7 @@ Corleone = {path = "../.."} [compat] Aqua = "0.8" -Corleone = "0.0.4" +Corleone = "0.0.5" SafeTestsets = "0.1, 1" SciMLTesting = "1" Test = "1" From 4036eeaa4211c188c3314428f287adfec99107f0 Mon Sep 17 00:00:00 2001 From: Chris Rackauckas - Beep Boop Edition Date: Sun, 5 Jul 2026 09:00:15 +0000 Subject: [PATCH 70/73] Fix sublibrary QA (Aqua) and Downgrade CI red lanes (#108) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix sublibrary QA (Aqua) and Downgrade CI red lanes Sublibrary CI was red on both the QA (Aqua.test_all) and Downgrade lanes for lib/CorleoneOED and lib/OptimalControlBenchmarks. QA (genuine Aqua violations, fixed at the source, no ignore-lists): CorleoneOED - unbound_args: drop the unused `DISCRETE` type param from the two `update_fim(::OEDLayer{<:Any, SAMPLED, FIXED, ...})` methods in src/oed.jl, and the unused `SPLIT` param from the two `update_fim(::MultiExperimentLayer{DISCRETE, FIXED, <:Any, ...})` methods in src/multiexperiments.jl (first/third positional params are `<:Any`, so the declared vars never bind). - ambiguities: add the disambiguating `get_sampling_sums(!)(::OEDLayer{<:Any, false, <:Any, <:MultipleShootingLayer}, ..., st::NamedTuple{fields})` methods that Aqua suggests (SAMPLED=false has no sampling, so `[]`/`nothing`). - piracy: move the `shooting_constraints(!)(::AbstractVector{ <:Trajectory})` methods out of CorleoneOED into the owning package Corleone (src/multiple_shooting.jl), next to the single-Trajectory methods — both the function and `Trajectory` are Corleone's, so the vector dispatch belongs there, not in a downstream package. - stale_deps: StableRNGs is used only in test/core, not src -> move it from [deps] to [extras]+[targets].test (it was silently pulled from the parent dev tree before). - deps_compat: add `Pkg = "1"` compat (Pkg is a test extra). OptimalControlBenchmarks - stale_deps: remove OptimizationLBFGSB, Ipopt, UnoSolver, OrdinaryDiffEq, Runic from [deps] (none imported in src; the module uses OrdinaryDiffEqTsit5 and takes the optimizer as an argument). Ipopt stays in [extras] as a benchmark-only optimizer. - deps_compat: add missing [compat] for BenchmarkTools, CairoMakie, IntervalSets, ModelingToolkit, ModelingToolkitBase, and `Pkg = "1"`. Downgrade (both sublibs path-source the parent, so they must not declare a lower floor than the parent for a shared dep): - SciMLBase "2, 3" -> "2.141.0, 3" (match parent Corleone floor) - SymbolicIndexingInterface "0.3" -> "0.3.43" (match parent floor) Co-Authored-By: Chris Rackauckas * Fix downgrade dead-ends in sublib compat floors The sublibrary downgrade lanes still failed after the previous floor bumps because several direct-dep compat floors were below what the resolved graph actually needs at min-resolution. CorleoneOED / OptimalControlBenchmarks: - Reexport "1.2" -> "1.2.2": the parent Corleone (path-sourced) requires Reexport >= 1.2.2, but the merged downgrade resolve strips Corleone and picked Reexport 1.2.0 from the sublib floor. The Pkg.test sandbox then re-resolves WITH the parent and rejects the pinned 1.2.0 as Unsatisfiable ("restricted to 1.2.0 by an explicit requirement -- no versions left"). Matching the parent's floor makes the downgrade pick 1.2.2 up front. - DocStringExtensions "0.9" -> "0.9.3": same class of latent dead-end (parent floor is 0.9.3; 0.9.0-0.9.2 exist in the registry). OptimalControlBenchmarks only: - ModelingToolkit "11" -> "11.2.0": MTK 11.0.0/11.1.0 call Base.ispublic unguarded in @import_mtkbase; Base.ispublic only exists on Julia >= 1.11, so precompile dies on the LTS (1.10) downgrade lane with "UndefVarError: ispublic not defined". MTK 11.2.0 is the first release that guards the call with @static if VERSION >= v"1.11". - Symbolics "7" -> "7.2.0": at floor, ModelingToolkitBase 1.0.0 and ModelingToolkitTearing 1.0.2 declare no Symbolics lower bound, so the resolver picks Symbolics 7.1.1. MTKTearing calls fixpoint_sub(iv, dict, Shift) positionally, but 7.1.1's fixpoint_sub takes the operator only as a keyword; the positional OP arg was added in Symbolics 7.2.0. Without the bump, MTK's precompile workload dies with "MethodError: no method matching fixpoint_sub(..., ::Type{Shift})". Validated at floor (Julia 1.10, GROUP=Core, the downgrade lane's group): - CorleoneOED: 1D Example 36/36, Lotka Volterra 125/125, LV SVD 17/17, tests passed. - OptimalControlBenchmarks: 12 deps (incl. MTK/MTKBase/Tearing) precompiled cleanly, tests passed. Co-Authored-By: Chris Rackauckas --------- Co-authored-by: ChrisRackauckas-Claude --- lib/CorleoneOED/Project.toml | 13 +++++++------ lib/CorleoneOED/src/multiexperiments.jl | 19 ++----------------- lib/CorleoneOED/src/oed.jl | 6 ++++-- lib/OptimalControlBenchmarks/Project.toml | 22 +++++++++++----------- src/multiple_shooting.jl | 13 +++++++++++++ 5 files changed, 37 insertions(+), 36 deletions(-) diff --git a/lib/CorleoneOED/Project.toml b/lib/CorleoneOED/Project.toml index be540552..77b8ed9a 100644 --- a/lib/CorleoneOED/Project.toml +++ b/lib/CorleoneOED/Project.toml @@ -13,7 +13,6 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" SciMLStructures = "53ae85a6-f571-4167-b2af-e1d143709226" -StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5" Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" @@ -29,7 +28,7 @@ CorleoneOEDZygoteExtension = "Zygote" [compat] Corleone = "0.0.5" ComponentArrays = "0.15" -DocStringExtensions = "0.9" +DocStringExtensions = "0.9.3" ForwardDiff = "1.3" Ipopt = "1" LinearAlgebra = "1" @@ -37,13 +36,14 @@ LuxCore = "1.4" Optimization = "5" OptimizationMOI = "1" OrdinaryDiffEqTsit5 = "1.5, 2" +Pkg = "1" Random = "1" -Reexport = "1.2" +Reexport = "1.2.2" SafeTestsets = "0.1" -SciMLBase = "2, 3" +SciMLBase = "2.141.0, 3" SciMLStructures = "1.7" StableRNGs = "1" -SymbolicIndexingInterface = "0.3" +SymbolicIndexingInterface = "0.3.43" Symbolics = "7" Test = "1" Zygote = "0.7" @@ -57,10 +57,11 @@ OptimizationMOI = "fd9f6733-72f4-499f-8506-86b2bdd0dea1" OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" +StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [sources.Corleone] path = "../.." [targets] -test = ["ComponentArrays", "Ipopt", "Optimization", "OptimizationMOI", "OrdinaryDiffEqTsit5", "Pkg", "SafeTestsets", "Test"] +test = ["ComponentArrays", "Ipopt", "Optimization", "OptimizationMOI", "OrdinaryDiffEqTsit5", "Pkg", "SafeTestsets", "StableRNGs", "Test"] diff --git a/lib/CorleoneOED/src/multiexperiments.jl b/lib/CorleoneOED/src/multiexperiments.jl index a5eb614d..5db60356 100644 --- a/lib/CorleoneOED/src/multiexperiments.jl +++ b/lib/CorleoneOED/src/multiexperiments.jl @@ -170,7 +170,7 @@ Corleone.get_number_of_shooting_constraints(multi::MultiExperimentLayer{<:Any, < Corleone.get_number_of_shooting_constraints(multi::MultiExperimentLayer{<:Any, <:Any, true, <:MultipleShootingLayer}) = sum(map(Corleone.get_number_of_shooting_constraints, multi.layers)) Corleone.get_number_of_shooting_constraints(multi::MultiExperimentLayer{<:Any, <:Any, <:Any, <:SingleShootingLayer}) = 0 -function update_fim(oed::MultiExperimentLayer{DISCRETE, FIXED, <:Any, <:SingleShootingLayer}, experiments, st::NamedTuple) where {DISCRETE, FIXED, SPLIT} +function update_fim(oed::MultiExperimentLayer{DISCRETE, FIXED, <:Any, <:SingleShootingLayer}, experiments, st::NamedTuple) where {DISCRETE, FIXED} FIM = sum( map(experiments) do experiment fisher_information(oed, nothing, experiment.ps, experiment.st)[1] @@ -184,7 +184,7 @@ function update_fim(oed::MultiExperimentLayer{DISCRETE, FIXED, <:Any, <:SingleSh end -function update_fim(oed::MultiExperimentLayer{DISCRETE, FIXED, <:Any, <:MultipleShootingLayer}, experiments, st::NamedTuple) where {DISCRETE, FIXED, SPLIT} +function update_fim(oed::MultiExperimentLayer{DISCRETE, FIXED, <:Any, <:MultipleShootingLayer}, experiments, st::NamedTuple) where {DISCRETE, FIXED} FIM = sum( map(experiments) do experiment fisher_information(oed, nothing, experiment.ps, experiment.st)[1] @@ -328,18 +328,3 @@ function Corleone.get_block_structure(layer::MultiExperimentLayer{<:Any, <:Any, return block_structure end - -function Corleone.shooting_constraints(trajs::AbstractVector{<:Trajectory}) - return reduce(vcat, reduce(vcat, shooting_violations.(trajs))) -end - -function Corleone.shooting_constraints!(res::AbstractVector, trajs::AbstractVector{<:Trajectory}) - i = 0 - for traj in trajs - for subvec in traj.shooting, j in eachindex(subvec) - i += 1 - res[i] = subvec[j] - end - end - return res -end diff --git a/lib/CorleoneOED/src/oed.jl b/lib/CorleoneOED/src/oed.jl index f08e9a7b..666ad417 100644 --- a/lib/CorleoneOED/src/oed.jl +++ b/lib/CorleoneOED/src/oed.jl @@ -141,7 +141,7 @@ function OEDLayer{DISCRETE}(layer::SingleShootingLayer, args...; measurements = return OEDLayer{DISCRETE, SAMPLED, FIXED, typeof(newlayer), typeof(observed)}(newlayer, observed, samplings) end -function update_fim(oed::OEDLayer{<:Any, SAMPLED, FIXED, <:SingleShootingLayer}, experiments, st::NamedTuple) where {DISCRETE, SAMPLED, FIXED} +function update_fim(oed::OEDLayer{<:Any, SAMPLED, FIXED, <:SingleShootingLayer}, experiments, st::NamedTuple) where {SAMPLED, FIXED} FIM = sum( map(experiments) do experiment fisher_information(oed, nothing, experiment.ps, experiment.st)[1] @@ -151,7 +151,7 @@ function update_fim(oed::OEDLayer{<:Any, SAMPLED, FIXED, <:SingleShootingLayer}, return merge(st, (; F_init = FIM + st.F_init)) end -function update_fim(oed::OEDLayer{<:Any, SAMPLED, FIXED, <:MultipleShootingLayer}, experiments, st::NamedTuple) where {DISCRETE, SAMPLED, FIXED} +function update_fim(oed::OEDLayer{<:Any, SAMPLED, FIXED, <:MultipleShootingLayer}, experiments, st::NamedTuple) where {SAMPLED, FIXED} FIM = sum( map(experiments) do experiment fisher_information(oed, nothing, experiment.ps, experiment.st)[1] @@ -458,6 +458,8 @@ end get_sampling_sums(::OEDLayer{<:Any, false}, x, ps, st) = [] get_sampling_sums!(res, ::OEDLayer{<:Any, false}, x, ps, st) = nothing +get_sampling_sums(::OEDLayer{<:Any, false, <:Any, <:Corleone.MultipleShootingLayer}, x, ps, st::NamedTuple{fields}) where {fields} = [] +get_sampling_sums!(res, ::OEDLayer{<:Any, false, <:Any, <:Corleone.MultipleShootingLayer}, x, ps, st::NamedTuple{fields}) where {fields} = nothing __get_subsets(active_controls::AbstractVector, indices) = active_controls[indices] __get_subsets(index_grid::AbstractMatrix, indices) = index_grid[indices, :] diff --git a/lib/OptimalControlBenchmarks/Project.toml b/lib/OptimalControlBenchmarks/Project.toml index d317325d..812e3ede 100644 --- a/lib/OptimalControlBenchmarks/Project.toml +++ b/lib/OptimalControlBenchmarks/Project.toml @@ -11,46 +11,46 @@ Corleone = "2751e0db-33e9-4374-b36d-b7219e1e6b40" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" -Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LuxCore = "bb33d45b-7691-41d6-9220-0943567d0623" ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" ModelingToolkitBase = "7771a370-6774-4173-bd38-47e70ca0b839" Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba" -OptimizationLBFGSB = "22f7324a-a79d-40f2-bebe-3af60c77bd15" OptimizationMOI = "fd9f6733-72f4-499f-8506-86b2bdd0dea1" -OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" -Runic = "62bfec6d-59d7-401d-8490-b29ee721c001" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" SciMLStructures = "53ae85a6-f571-4167-b2af-e1d143709226" StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5" Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" -UnoSolver = "1baa60ac-02f7-4b39-a7a8-2f4f58486b05" [compat] +BenchmarkTools = "1" +CairoMakie = "0.15" ComponentArrays = "0.15" Corleone = "0.0.5" -DocStringExtensions = "0.9" +DocStringExtensions = "0.9.3" ForwardDiff = "1.3" +IntervalSets = "0.7" Ipopt = "1" LinearAlgebra = "1" LuxCore = "1.4" +ModelingToolkit = "11.2.0" +ModelingToolkitBase = "1" Optimization = "5" OptimizationMOI = "1" -OrdinaryDiffEq = "6, 7" OrdinaryDiffEqTsit5 = "1.5, 2" +Pkg = "1" Random = "1" -Reexport = "1.2" +Reexport = "1.2.2" SafeTestsets = "0.1" -SciMLBase = "2, 3" +SciMLBase = "2.141.0, 3" SciMLStructures = "1.7" StableRNGs = "1" -SymbolicIndexingInterface = "0.3" -Symbolics = "7" +SymbolicIndexingInterface = "0.3.43" +Symbolics = "7.2.0" Test = "1" julia = "1.10" diff --git a/src/multiple_shooting.jl b/src/multiple_shooting.jl index 309dd94e..33a5672d 100644 --- a/src/multiple_shooting.jl +++ b/src/multiple_shooting.jl @@ -296,6 +296,19 @@ end shooting_constraints!(res, traj::Trajectory) = res +shooting_constraints(trajs::AbstractVector{<:Trajectory}) = reduce(vcat, reduce(vcat, shooting_violations.(trajs))) + +function shooting_constraints!(res::AbstractVector, trajs::AbstractVector{<:Trajectory}) + i = 0 + for traj in trajs + for subvec in traj.shooting, j in eachindex(subvec) + i += 1 + res[i] = subvec[j] + end + end + return res +end + """ $(SIGNATURES) From a479c1d4439644bf726fd9a15c3d7dc874ce587b Mon Sep 17 00:00:00 2001 From: Chris Rackauckas - Beep Boop Edition Date: Mon, 6 Jul 2026 12:49:20 +0000 Subject: [PATCH 71/73] QA: run_qa v1.6 form + ExplicitImports (root + sublibs) (#102) * QA: run_qa v1.6 form + ExplicitImports (root + sublibs) Convert the root and both lib/ sublibrary QA environments from the hand-rolled `@testset "Aqua" Aqua.test_all(Mod)` to the SciMLTesting v1.6 `run_qa(Mod; explicit_imports = true, ...)` dispatcher, enabling the six ExplicitImports checks alongside the unchanged Aqua run. Bump SciMLTesting compat to "1.6" per QA env; ExplicitImports is pulled transitively. The sublib [sources]/develop wiring is preserved exactly. Per-env ExplicitImports curation: - Corleone: no_implicit_imports broken (bare-`using` deps, tracked); ignore :ADTypes for via_owners and the deliberate Base/SciML internal names for qualified-accesses-are-public. - CorleoneOED: no_implicit_imports broken (tracked); ignore Base/SciML internals plus Corleone's own unexported helpers for qualified-accesses-are-public. - OptimalControlBenchmarks: allow_unanalyzable (dynamic include in problem_registry.jl) for the implicit/stale checks; ignore :inputs (MTK re-export) and :default_rng/:initialstates. All six EI checks pass. The CorleoneOED and OptimalControlBenchmarks QA lanes are already red on master from genuine pre-existing Aqua failures; this conversion runs the same Aqua.test_all with no new disables and does not mask them. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) * QA: sweep redundant public-API EI ignores after SciMLTesting 1.7 SciMLTesting 1.7 runs the two public-API ExplicitImports checks (all_qualified_accesses_are_public / all_explicit_imports_are_public) only on Julia >= 1.11, and several base libs now declare more names public (SciMLBase 3.24, Symbolics 7.28+, SymbolicUtils 4.36, ModelingToolkit 11.29, ArrayInterface 7.26, CommonSolve 0.2.9). Many per-repo public-API ignore entries are now redundant. Emptied both public-API ignore-lists in all three qa.jl files and restored, verified on Julia 1.12 against the registered releases, only the names the public checks still flag as non-public: - root (Corleone): kept 9 (ADTypes/AbstractDEAlgorithm/AbstractDEProblem/ EnsembleAlgorithm/get_colorizers from SciMLBase, AbstractVecOrTuple/Splat from Base, Tunable/canonicalize from SciMLStructures); dropped 10. - lib/CorleoneOED: kept 18 (SciMLBase/SciMLStructures/SymbolicUtils/Symbolics/ ForwardDiff internals + Corleone's own unexported helpers); dropped 6. - lib/OptimalControlBenchmarks: dropped both ignores entirely (inputs, default_rng, initialstates are now all public); kept the via-owners ignore. Other ignores (no_implicit_imports broken-marker, *_via_owners, allow_unanalyzable) left untouched. Verified Julia 1.10 (public checks skipped) and 1.12 (public checks run): ExplicitImports group green-or-broken, 0 EI hard failures, on root + both sublibs. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --------- Co-authored-by: ChrisRackauckas-Claude Co-authored-by: Claude Opus 4.8 (1M context) --- lib/CorleoneOED/test/qa/Project.toml | 2 ++ lib/CorleoneOED/test/qa/qa.jl | 34 +++++++++++++++---- .../test/qa/Project.toml | 2 ++ lib/OptimalControlBenchmarks/test/qa/qa.jl | 21 ++++++++---- test/qa/Project.toml | 2 +- test/qa/qa.jl | 33 ++++++++++++++---- 6 files changed, 75 insertions(+), 19 deletions(-) diff --git a/lib/CorleoneOED/test/qa/Project.toml b/lib/CorleoneOED/test/qa/Project.toml index ba585c7d..616805df 100644 --- a/lib/CorleoneOED/test/qa/Project.toml +++ b/lib/CorleoneOED/test/qa/Project.toml @@ -2,6 +2,7 @@ Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" Corleone = "2751e0db-33e9-4374-b36d-b7219e1e6b40" CorleoneOED = "6590a4b1-d036-41fe-a5b4-03a980244101" +SciMLTesting = "09d9d899-5365-40a9-917a-5f67fddea283" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [sources] @@ -12,5 +13,6 @@ CorleoneOED = {path = "../.."} Aqua = "0.8" Corleone = "0.0.5" CorleoneOED = "0.0.5" +SciMLTesting = "1.6" Test = "1" julia = "1.10" diff --git a/lib/CorleoneOED/test/qa/qa.jl b/lib/CorleoneOED/test/qa/qa.jl index c9f196fb..bc60355d 100644 --- a/lib/CorleoneOED/test/qa/qa.jl +++ b/lib/CorleoneOED/test/qa/qa.jl @@ -1,8 +1,30 @@ +using SciMLTesting using CorleoneOED -using Aqua +using Test -@testset "Aqua" begin - Aqua.test_all( - CorleoneOED - ) -end +run_qa( + CorleoneOED; + explicit_imports = true, + # CorleoneOED pulls Corleone and its other deps in with bare `using`, so it + # leans on a large set of implicit imports. Converting every one to an + # explicit import is a sizable refactor tracked in SciML/Corleone.jl#103. + ei_broken = (:no_implicit_imports,), + ei_kwargs = (; + # Names still not declared public in their owning modules: SciMLBase + # internals (`AbstractDEAlgorithm`, `AbstractDEProblem`, `get_colorizers`), + # SciMLStructures internals (`Tunable`, `canonicalize`), SymbolicUtils + # internal (`Code`), Symbolics internals (`getdefaultval`, `setdefaultval`, + # `variables`), ForwardDiff internal (`jacobian`), and Corleone's own + # as-yet-unexported helpers reached through `Corleone.*`. + all_qualified_accesses_are_public = (; + ignore = ( + :AbstractDEAlgorithm, :AbstractDEProblem, :Code, :Tunable, + :build_index_grid, :canonicalize, :get_block_structure, + :get_bounds, :get_colorizers, + :get_number_of_shooting_constraints, :get_timegrid, :getdefaultval, + :jacobian, :retrieve_symbol_cache, :setdefaultval, + :shooting_constraints, :shooting_constraints!, :variables, + ), + ), + ), +) diff --git a/lib/OptimalControlBenchmarks/test/qa/Project.toml b/lib/OptimalControlBenchmarks/test/qa/Project.toml index 6efbae1d..1f38b948 100644 --- a/lib/OptimalControlBenchmarks/test/qa/Project.toml +++ b/lib/OptimalControlBenchmarks/test/qa/Project.toml @@ -2,6 +2,7 @@ Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" Corleone = "2751e0db-33e9-4374-b36d-b7219e1e6b40" OptimalControlBenchmarks = "d147904a-1eb9-11f1-01e7-bdf88e0f9445" +SciMLTesting = "09d9d899-5365-40a9-917a-5f67fddea283" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [sources] @@ -12,5 +13,6 @@ OptimalControlBenchmarks = {path = "../.."} Aqua = "0.8" Corleone = "0.0.5" OptimalControlBenchmarks = "0.0.3" +SciMLTesting = "1.6" Test = "1" julia = "1.10" diff --git a/lib/OptimalControlBenchmarks/test/qa/qa.jl b/lib/OptimalControlBenchmarks/test/qa/qa.jl index d09e9dbe..b5de8aa1 100644 --- a/lib/OptimalControlBenchmarks/test/qa/qa.jl +++ b/lib/OptimalControlBenchmarks/test/qa/qa.jl @@ -1,8 +1,17 @@ +using SciMLTesting using OptimalControlBenchmarks -using Aqua +using Test -@testset "Aqua" begin - Aqua.test_all( - OptimalControlBenchmarks - ) -end +run_qa( + OptimalControlBenchmarks; + explicit_imports = true, + ei_kwargs = (; + # `problem_registry.jl` registers problems through a dynamic `include`, + # which ExplicitImports cannot follow, so the module is unanalyzable for + # the implicit/stale checks. + no_implicit_imports = (; allow_unanalyzable = (OptimalControlBenchmarks,)), + no_stale_explicit_imports = (; allow_unanalyzable = (OptimalControlBenchmarks,)), + # `inputs` is re-exported by ModelingToolkit from ModelingToolkitBase. + all_explicit_imports_via_owners = (; ignore = (:inputs,)), + ), +) diff --git a/test/qa/Project.toml b/test/qa/Project.toml index a2f51f21..6a5a1841 100644 --- a/test/qa/Project.toml +++ b/test/qa/Project.toml @@ -12,6 +12,6 @@ Corleone = {path = "../.."} Aqua = "0.8" Corleone = "0.0.5" SafeTestsets = "0.1, 1" -SciMLTesting = "1" +SciMLTesting = "1.6" Test = "1" julia = "1.10" diff --git a/test/qa/qa.jl b/test/qa/qa.jl index 5e14cab7..57ffabf7 100644 --- a/test/qa/qa.jl +++ b/test/qa/qa.jl @@ -1,8 +1,29 @@ +using SciMLTesting using Corleone -using Aqua +using Test -@testset "Aqua" begin - Aqua.test_all( - Corleone - ) -end +run_qa( + Corleone; + explicit_imports = true, + # Corleone pulls all of its deps in with bare `using`, so the package leans + # on a large set of implicit imports. Converting every one to an explicit + # import is a sizable refactor tracked in SciML/Corleone.jl#103. + ei_broken = (:no_implicit_imports,), + ei_kwargs = (; + # `ADTypes` is reached through `SciMLBase.ADTypes.AbstractADType`; ADTypes + # is not a direct Corleone dependency, so the access goes via SciMLBase. + all_qualified_accesses_via_owners = (; ignore = (:ADTypes,)), + # Names still not declared public in their owning modules: Base internals + # (`AbstractVecOrTuple`, `Splat`), SciMLBase internals (`ADTypes`, + # `AbstractDEAlgorithm`, `AbstractDEProblem`, `EnsembleAlgorithm`, + # `get_colorizers`), and SciMLStructures internals (`Tunable`, + # `canonicalize`). + all_qualified_accesses_are_public = (; + ignore = ( + :ADTypes, :AbstractDEAlgorithm, :AbstractDEProblem, + :AbstractVecOrTuple, :EnsembleAlgorithm, :Splat, :Tunable, + :canonicalize, :get_colorizers, + ), + ), + ), +) From fd49949163a456f27f532c89e3ac665e68f048fc Mon Sep 17 00:00:00 2001 From: Chris Rackauckas - Beep Boop Edition Date: Wed, 8 Jul 2026 11:26:30 +0000 Subject: [PATCH 72/73] Document public API coverage (#109) * Document public API coverage Co-Authored-By: Chris Rackauckas * Render benchmark exports in API docs Co-Authored-By: Chris Rackauckas --------- Co-authored-by: ChrisRackauckas-Claude --- docs/Project.toml | 1 + docs/make.jl | 4 +- docs/src/api.md | 16 ++++ lib/CorleoneOED/src/multiexperiments.jl | 22 ++++++ lib/CorleoneOED/src/oed.jl | 77 +++++++++++++++++++ .../src/problem_registry.jl | 9 +++ .../src/run_benchmarks.jl | 14 ++++ src/local_controls.jl | 20 ++--- src/multiple_shooting.jl | 8 +- 9 files changed, 155 insertions(+), 16 deletions(-) diff --git a/docs/Project.toml b/docs/Project.toml index c58e4e46..664a45d5 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -16,6 +16,7 @@ ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba" OptimizationLBFGSB = "22f7324a-a79d-40f2-bebe-3af60c77bd15" OptimizationMOI = "fd9f6733-72f4-499f-8506-86b2bdd0dea1" +OptimalControlBenchmarks = "d147904a-1eb9-11f1-01e7-bdf88e0f9445" OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5" diff --git a/docs/make.jl b/docs/make.jl index a5a79897..e4cca33b 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,6 +1,6 @@ using Pkg -using Documenter, Corleone +using Documenter, Corleone, CorleoneOED, OptimalControlBenchmarks using DocumenterInterLinks using DocumenterCitations using Literate @@ -18,7 +18,7 @@ bib = CitationBibliography(joinpath(@__DIR__, "src", "assets", "bibliography.bib makedocs( sitename = "Corleone.jl", authors = "Carl Julius Martensen, Christoph Plate, et al.", - modules = [Corleone], + modules = [Corleone, CorleoneOED, OptimalControlBenchmarks], format = Documenter.HTML( assets = ["assets/favicon.ico"], canonical = "https://docs.sciml.ai/Corleone/stable/", diff --git a/docs/src/api.md b/docs/src/api.md index 9e9dc07b..75b0d01d 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -1,6 +1,22 @@ # API Reference +## Corleone + ```@autodocs Modules = [Corleone] Order = [:type, :function] ``` + +## CorleoneOED + +```@autodocs +Modules = [CorleoneOED] +Order = [:type, :function] +``` + +## OptimalControlBenchmarks + +```@autodocs +Modules = [OptimalControlBenchmarks] +Order = [:type, :function] +``` diff --git a/lib/CorleoneOED/src/multiexperiments.jl b/lib/CorleoneOED/src/multiexperiments.jl index 5db60356..fcd1b408 100644 --- a/lib/CorleoneOED/src/multiexperiments.jl +++ b/lib/CorleoneOED/src/multiexperiments.jl @@ -1,6 +1,8 @@ """ $(TYPEDEF) + Generalization of OEDLayer to multiple experiments that can be jointly optimized. + # Fields $(FIELDS) """ @@ -41,6 +43,26 @@ function Base.show(io::IO, oed::MultiExperimentLayer{DISCRETE, FIXED, SPLIT}) wh end end +""" +$(SIGNATURES) + +Constructs a multi-experiment OED layer from one differential equation problem. + +# Arguments +- `prob`: Differential equation problem shared by the experiments. +- `alg`: Differential equation solver algorithm. +- `nexp`: Number of experiments, or pass a vector of parameter-index vectors to split + parameters across experiments. +- `shooting_points`: Optional multiple-shooting points. + +# Keywords +- `params`: Parameter indices included in each experiment. +- `measurements`: Optional measurement controls. +- `observed`: Observation map `(u, p, t) -> y`. + +# Returns +A `MultiExperimentLayer` whose parameters and states are grouped by experiment. +""" function MultiExperimentLayer{DISCRETE}( prob::SciMLBase.AbstractDEProblem, alg::SciMLBase.AbstractDEAlgorithm, nexp::Int; params = eachindex(prob.p), measurements = [], observed = default_observed, kwargs... diff --git a/lib/CorleoneOED/src/oed.jl b/lib/CorleoneOED/src/oed.jl index 666ad417..344f8b35 100644 --- a/lib/CorleoneOED/src/oed.jl +++ b/lib/CorleoneOED/src/oed.jl @@ -20,6 +20,10 @@ default_observed = (u, p, t) -> u """ $(TYPEDEF) +Wraps a Corleone shooting layer with the augmented dynamics needed for optimal experimental +design. The `DISCRETE` type parameter selects discrete or continuous information accumulation, +and optional measurement controls can restrict where observations are collected. + # Fields $(FIELDS) """ @@ -54,6 +58,24 @@ function Base.show(io::IO, oed::OEDLayer{DISCRETE, SAMPLED, FIXED}) where {DISCR return Base.show(io, "text/plain", isa(layer, SingleShootingLayer) ? layer.problem : layer.layer.problem) end +""" +$(SIGNATURES) + +Constructs an `OEDLayer` from a differential equation problem and solver algorithm. + +# Arguments +- `prob`: Differential equation problem whose parameters are considered for experimental design. +- `alg`: Differential equation solver algorithm. + +# Keywords +- `params`: Parameter indices included in the Fisher information matrix. +- `measurements`: Optional `ControlParameter`s that define measurement schedules. +- `observed`: Observation map `(u, p, t) -> y`. + +# Returns +An `OEDLayer` that can be initialized with `LuxCore.setup` and evaluated like the wrapped +Corleone layer. +""" function OEDLayer{DISCRETE}(prob::SciMLBase.AbstractDEProblem, alg::SciMLBase.AbstractDEAlgorithm; params = eachindex(prob.p), measurements = [], observed = default_observed, kwargs...) where {DISCRETE} layer = SingleShootingLayer(prob, alg; kwargs...) return OEDLayer{DISCRETE}(layer; params = params, measurements = measurements, observed = observed, kwargs...) @@ -339,6 +361,23 @@ function __fisher_information(oed::OEDLayer{true, true, true}, traj::Trajectory, return observation_grid(controls, Gs) end +""" +$(SIGNATURES) + +Computes the Fisher information matrix for an `OEDLayer` at parameters `ps` and state `st`. + +# Arguments +- `oed`: Optimal experimental design layer. +- `x`: External input passed through the Lux layer interface. +- `ps`: Layer parameters, typically from `LuxCore.initialparameters` or `LuxCore.setup`. +- `st`: Layer state, typically from `LuxCore.initialstates` or `LuxCore.setup`. + +# Keywords +- `add_initial`: Include previously accumulated information stored in `st`. + +# Returns +A tuple `(F, st)` containing the Fisher information matrix and updated layer state. +""" fisher_information(oed::OEDLayer, x, ps, st::NamedTuple; add_initial = true) = begin traj, st = oed(x, ps, st) F = add_initial ? st.F_init + sum(__fisher_information(oed, traj)) : sum(__fisher_information(oed, traj)) @@ -414,6 +453,15 @@ fisher_information(oed::OEDLayer{false, true, true}, x, ps, st::NamedTuple; add_ return F, st end +""" +$(SIGNATURES) + +Returns the parameter sensitivities of the observed outputs for an OED trajectory. + +# Returns +For trajectory inputs, the sensitivity arrays are returned directly. For layer inputs, the +return value is `(sensitivities, st)` with the updated layer state. +""" sensitivities(oed::OEDLayer, traj::Trajectory) = oed.observed.sensitivities(traj) sensitivities(oed::OEDLayer, x, ps, st::NamedTuple) = begin @@ -421,6 +469,15 @@ sensitivities(oed::OEDLayer, x, ps, st::NamedTuple) = begin sensitivities(oed, traj), st end +""" +$(SIGNATURES) + +Evaluates the observation equations associated with an `OEDLayer`. + +# Returns +For trajectory inputs, observed values are returned directly. For layer inputs, the return +value is `(observed, st)` with the updated layer state. +""" observed_equations(oed::OEDLayer, traj::Trajectory) = oed.observed.observed(traj) observed_equations(oed::OEDLayer, x, ps, st::NamedTuple) = begin @@ -430,6 +487,16 @@ end _local_information_gain(oed::OEDLayer, traj::Trajectory) = oed.observed.local_weighted_sensitivity(traj) +""" +$(SIGNATURES) + +Computes local information gain contributions from each observed quantity along the OED +trajectory. + +# Returns +A tuple `(gains, st)` containing per-observation information matrices and the updated layer +state. +""" local_information_gain(oed::OEDLayer, x, ps, st::NamedTuple) = begin traj, st = oed(x, ps, st) # This returns hx G but stacked as a matrix [h_1_x G; h_2_x G; ...] @@ -442,6 +509,16 @@ local_information_gain(oed::OEDLayer, x, ps, st::NamedTuple) = begin end, st end +""" +$(SIGNATURES) + +Computes global information gain contributions scaled by the inverse final Fisher information +matrix. + +# Returns +A tuple `(gains, st)` containing per-observation information matrices and the updated layer +state. +""" global_information_gain(oed::OEDLayer, x, ps, st::NamedTuple) = begin traj, st = oed(x, ps, st) F_tf, st = fisher_information(oed, x, ps, st) diff --git a/lib/OptimalControlBenchmarks/src/problem_registry.jl b/lib/OptimalControlBenchmarks/src/problem_registry.jl index 675c1f5a..7719bbae 100644 --- a/lib/OptimalControlBenchmarks/src/problem_registry.jl +++ b/lib/OptimalControlBenchmarks/src/problem_registry.jl @@ -1,3 +1,12 @@ +""" + load_benchmarks() + +Loads all benchmark problem constructors from the `problems` directory. + +# Returns +A vector of benchmark constructor functions. Each function accepts a benchmark grid +configuration and returns the corresponding optimal control problem data. +""" function load_benchmarks() benchmarks = [] problems_dir = joinpath(@__DIR__, "problems") diff --git a/lib/OptimalControlBenchmarks/src/run_benchmarks.jl b/lib/OptimalControlBenchmarks/src/run_benchmarks.jl index 87dbbbe1..80da9b61 100644 --- a/lib/OptimalControlBenchmarks/src/run_benchmarks.jl +++ b/lib/OptimalControlBenchmarks/src/run_benchmarks.jl @@ -1,5 +1,19 @@ using BenchmarkTools +""" + run_all(benchmarks, optimizer, grids) + +Runs each benchmark problem with the Corleone benchmark solver. + +# Arguments +- `benchmarks`: Iterable of benchmark constructor functions, such as the result of + `load_benchmarks()`. +- `optimizer`: Optimization solver passed to `solve_with_corleone`. +- `grids`: Grid configuration passed to each benchmark constructor. + +# Returns +A vector of named tuples containing each benchmark name and measured runtime placeholder. +""" function run_all(benchmarks, optimizer, grids) results = [] diff --git a/src/local_controls.jl b/src/local_controls.jl index cea9a0c5..83fc5c12 100644 --- a/src/local_controls.jl +++ b/src/local_controls.jl @@ -56,22 +56,22 @@ get_timegrid(parameters::ControlParameter, tspan = (-Inf, Inf)) = begin t[idx] end -``` +""" $(SIGNATURES) Computes the number of discretized controls restricted to given `tspan`. -``` +""" function control_length(parameters::ControlParameter; tspan = nothing, kwargs...) (; t) = parameters idx = isnothing(tspan) ? eachindex(t) : findall(tspan[1] .<= t .< tspan[2]) return size(idx, 1) end -``` +""" $(SIGNATURES) Returns discretized controls of ControlParameter `params` restricted to given `tspan`. -``` +""" function get_controls(::Random.AbstractRNG, parameters::ControlParameter{<:Any, <:AbstractArray}; raw = false, tspan = nothing, kwargs...) (; t, controls) = parameters raw && return controls @@ -86,11 +86,11 @@ function get_controls(rng::Random.AbstractRNG, parameters::ControlParameter{<:An return parameters.controls(rng, t[idx], bounds) end -``` +""" $(SIGNATURES) Returns bounds of discretized controls restricted to given `tspan`. -``` +""" function get_bounds(parameters::ControlParameter{<:Any, <:Any, <:Tuple}; tspan = nothing, kwargs...) (; t) = parameters idx = isnothing(tspan) ? eachindex(t) : findall(tspan[1] .<= t .< tspan[2]) @@ -194,10 +194,10 @@ function collect_tspans(controls::ControlParameter...; tspan = (-Inf, Inf), subd return tuple(fullgrid...) end -``` +""" $(SIGNATURES) Collect all discretized `controls` in a flat vector. -``` +""" function collect_local_controls(rng, controls::ControlParameter...; kwargs...) return reduce( vcat, map(controls) do control @@ -206,10 +206,10 @@ function collect_local_controls(rng, controls::ControlParameter...; kwargs...) ) end -``` +""" $(SIGNATURES) Collect all lower and upper bounds of discretized `controls` in flat vectors. -``` +""" function collect_local_control_bounds(controls::ControlParameter...; kwargs...) bounds = map(controls) do control get_bounds(control; kwargs...) diff --git a/src/multiple_shooting.jl b/src/multiple_shooting.jl index 33a5672d..da50f1db 100644 --- a/src/multiple_shooting.jl +++ b/src/multiple_shooting.jl @@ -54,12 +54,12 @@ function default_initialization(rng::Random.AbstractRNG, shooting::MultipleShoot return NamedTuple{names}(vals) end -``` +""" $(METHODLIST) Constructs a `MultipleShootingLayer` from given `AbstractDEProblem` `prob` with suitable integration method `alg` and vector of shooting points `tpoints`. -``` +""" function MultipleShootingLayer(prob, alg, tpoints::AbstractVector; kwargs...) return MultipleShootingLayer(prob, alg, tpoints...; kwargs...) end @@ -327,10 +327,10 @@ function get_block_structure(mslayer::MultipleShootingLayer) return vcat(0, cumsum(ps_lengths)) end -``` +""" $(SIGNATURES) Extracts lower and upper bounds of all optimization variables in the `MultipleShootingLayer`. -``` +""" function get_bounds(mslayer::MultipleShootingLayer) (; layer, shooting_intervals) = mslayer names = ntuple(i -> Symbol(:interval, "_", i), length(shooting_intervals)) From 7180b21c87d8cee6b37ce8723eb0e6fdd3751619 Mon Sep 17 00:00:00 2001 From: Chris Rackauckas - Beep Boop Edition Date: Wed, 8 Jul 2026 12:37:56 +0000 Subject: [PATCH 73/73] Resolve in-repo docs dependencies (#110) Co-authored-by: ChrisRackauckas-Claude --- docs/Project.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/Project.toml b/docs/Project.toml index 664a45d5..c15a80af 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -21,3 +21,7 @@ OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5" YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6" + +[sources] +CorleoneOED = {path = "../lib/CorleoneOED"} +OptimalControlBenchmarks = {path = "../lib/OptimalControlBenchmarks"}