diff --git a/available_problems_cache.txt b/available_problems_cache.txt new file mode 100644 index 0000000..46a2828 --- /dev/null +++ b/available_problems_cache.txt @@ -0,0 +1 @@ +dielectrophoretic_particle diff --git a/ext/JuMPModels/cart_pendulum.jl b/ext/JuMPModels/cart_pendulum.jl index f92340c..72b15f7 100644 --- a/ext/JuMPModels/cart_pendulum.jl +++ b/ext/JuMPModels/cart_pendulum.jl @@ -21,13 +21,13 @@ function OptimalControlProblems.cart_pendulum(::JuMPBackend; nh::Int64=100) @variables( model, begin - 0.0 <= tf - ddx - -max_x <= x[0:nh] <= max_x - -max_v <= dx[0:nh] <= max_v - theta[0:nh] - omega[0:nh] - -max_f <= Fex[0:nh] <= max_f + 0.0 <= tf, (start = 0.1) + ddx, (start = 0.1) + -max_x <= x[0:nh] <= max_x, (start = 0.1) + -max_v <= dx[0:nh] <= max_v, (start = 0.1) + theta[0:nh], (start = 0.1) + omega[0:nh], (start = 0.1) + -max_f <= Fex[0:nh] <= max_f, (start = 0.1) end ) diff --git a/ext/JuMPModels/double_oscillator.jl b/ext/JuMPModels/double_oscillator.jl index 8e46bed..6074e14 100644 --- a/ext/JuMPModels/double_oscillator.jl +++ b/ext/JuMPModels/double_oscillator.jl @@ -19,11 +19,11 @@ function OptimalControlProblems.double_oscillator(::JuMPBackend; nh::Int=100) @variables( model, begin - x1[0:nh] - x2[0:nh] - x3[0:nh] - x4[0:nh] - -1.0 <= u[0:nh] <= 1.0 + x1[0:nh], (start = 0.1) + x2[0:nh], (start = 0.1) + x3[0:nh], (start = 0.1) + x4[0:nh], (start = 0.1) + -1.0 <= u[0:nh] <= 1.0, (start = 0.1) end ) diff --git a/ext/JuMPModels/ducted_fan.jl b/ext/JuMPModels/ducted_fan.jl index 4791275..2ae6c00 100644 --- a/ext/JuMPModels/ducted_fan.jl +++ b/ext/JuMPModels/ducted_fan.jl @@ -14,14 +14,14 @@ function OptimalControlProblems.ducted_fan(::JuMPBackend; nh::Int=100) model = Model() - @variable(model, x1[0:nh]) - @variable(model, v1[0:nh]) - @variable(model, x2[0:nh]) - @variable(model, v2[0:nh]) - @variable(model, -deg2rad(30.0) <= α[0:nh] <= deg2rad(30.0)) # radian - @variable(model, vα[0:nh]) - @variable(model, -5.0 <= u1[0:nh] <= 5.0) # [nh] - @variable(model, 0.0 <= u2[0:nh] <= 17.0) # [nh] + @variable(model, x1[0:nh], start = 0.1) + @variable(model, v1[0:nh], start = 0.1) + @variable(model, x2[0:nh], start = 0.1) + @variable(model, v2[0:nh], start = 0.1) + @variable(model, -deg2rad(30.0) <= α[0:nh] <= deg2rad(30.0), start = 0.1) # radian + @variable(model, vα[0:nh], start = 0.1) + @variable(model, -5.0 <= u1[0:nh] <= 5.0, start = 0.1) # [nh] + @variable(model, 0.0 <= u2[0:nh] <= 17.0, start = 0.1) # [nh] @variable(model, 0 <= tf, start = 1.0) @objective(model, Min, tf / nh * sum(2 * u1[t]^2 + u2[t]^2 for t in 0:nh) + μ * tf) diff --git a/ext/JuMPModels/electrical_vehicle.jl b/ext/JuMPModels/electrical_vehicle.jl index 147527f..d672320 100644 --- a/ext/JuMPModels/electrical_vehicle.jl +++ b/ext/JuMPModels/electrical_vehicle.jl @@ -18,9 +18,9 @@ function OptimalControlProblems.electrical_vehicle(::JuMPBackend; nh::Int=100) model = Model() - @variable(model, x[0:nh]) - @variable(model, v[0:nh]) - @variable(model, u[0:nh]) + @variable(model, x[0:nh], start = 0.1) + @variable(model, v[0:nh], start = 0.1) + @variable(model, u[0:nh], start = 0.1) @objective(model, Min, sum(b1 * u[t] * v[t] + b2 * u[t]^2 for t in 0:nh)) diff --git a/ext/JuMPModels/glider.jl b/ext/JuMPModels/glider.jl index bce5482..f7e31d3 100644 --- a/ext/JuMPModels/glider.jl +++ b/ext/JuMPModels/glider.jl @@ -30,7 +30,7 @@ function OptimalControlProblems.glider(::JuMPBackend; nh::Int64=100) @variables( model, begin - 0 <= t_f, (start = 1.0) + 0 <= tf, (start = 1.0) 0.0 <= x[k=0:nh], (start = x_0 + vx_0 * (k / nh)) y[k=0:nh], (start = y_0 + (k / nh) * (y_f - y_0)) 0.0 <= vx[k=0:nh], (start = vx_0) @@ -44,7 +44,7 @@ function OptimalControlProblems.glider(::JuMPBackend; nh::Int64=100) @expressions( model, begin - step, t_f / nh + step, tf / nh r[i=0:nh], (x[i] / r_0 - 2.5)^2 u[i=0:nh], u_c * (1 - r[i]) * exp(-r[i]) w[i=0:nh], vy[i] - u[i] diff --git a/ext/JuMPModels/moonlander.jl b/ext/JuMPModels/moonlander.jl index e144cf3..1eecf27 100644 --- a/ext/JuMPModels/moonlander.jl +++ b/ext/JuMPModels/moonlander.jl @@ -23,14 +23,14 @@ function OptimalControlProblems.moonlander( @variables( model, begin - 0.0 <= step + 0.0 <= step, (start = 0.1) # state variables - p1[k=0:nh] - p2[k=0:nh] - dp1[k=0:nh] - dp2[k=0:nh] - theta[k=0:nh] - dtheta[k=0:nh] + p1[k=0:nh], (start = 0.1) + p2[k=0:nh], (start = 0.1) + dp1[k=0:nh], (start = 0.1) + dp2[k=0:nh], (start = 0.1) + theta[k=0:nh], (start = 0.1) + dtheta[k=0:nh], (start = 0.1) # control variables 0 <= F1[k=0:nh] <= max_thrust, (start = 5.0) 0 <= F2[k=0:nh] <= max_thrust, (start = 5.0) diff --git a/ext/JuMPModels/quadrotor.jl b/ext/JuMPModels/quadrotor.jl index 1dae5cd..8259566 100644 --- a/ext/JuMPModels/quadrotor.jl +++ b/ext/JuMPModels/quadrotor.jl @@ -22,17 +22,17 @@ function OptimalControlProblems.quadrotor(::JuMPBackend; nh::Int64=60) @variables( model, begin - 0.0 <= tf - p1[0:nh] - p2[0:nh] - p3[0:nh] - v1[0:nh] - v2[0:nh] - v3[0:nh] + 0.0 <= tf, (start = 0.1) + p1[0:nh], (start = 0.1) + p2[0:nh], (start = 0.1) + p3[0:nh], (start = 0.1) + v1[0:nh], (start = 0.1) + v2[0:nh], (start = 0.1) + v3[0:nh], (start = 0.1) atmin <= at[0:nh] <= atmax, (start = 10.0) - -pi / 2 <= ϕ[0:nh] <= pi / 2 - -pi / 2 <= θ[0:nh] <= pi / 2 - ψ[0:nh] + -pi / 2 <= ϕ[0:nh] <= pi / 2, (start = 0.1) + -pi / 2 <= θ[0:nh] <= pi / 2, (start = 0.1) + ψ[0:nh], (start = 0.1) end ) diff --git a/ext/JuMPModels/steering.jl b/ext/JuMPModels/steering.jl index f99650a..6a509a9 100644 --- a/ext/JuMPModels/steering.jl +++ b/ext/JuMPModels/steering.jl @@ -23,8 +23,11 @@ function OptimalControlProblems.steering(::JuMPBackend; nh::Int64=100) model = JuMP.Model() @variable(model, u_min <= u[i=1:(nh + 1)] <= u_max, start = 0.0) # control - @variable(model, x[i=1:(nh + 1), j=1:4], start = gen_x0(i, j)) # state - @variable(model, tf, start = 1.0) # final time + @variable(model, x1[i=1:(nh + 1)], start = gen_x0(i, 1)) # state x1 + @variable(model, x2[i=1:(nh + 1)], start = gen_x0(i, 2)) # state x2 + @variable(model, x3[i=1:(nh + 1)], start = gen_x0(i, 3)) # state x3 + @variable(model, x4[i=1:(nh + 1)], start = gen_x0(i, 4)) # state x4 + @variable(model, tf, start = 1.0) # final time @expression(model, h, tf / nh) # step size @objective(model, Min, tf) @@ -35,18 +38,21 @@ function OptimalControlProblems.steering(::JuMPBackend; nh::Int64=100) @constraints( model, begin - con_x1[i=1:nh], x[i + 1, 1] == x[i, 1] + 0.5 * h * (x[i, 3] + x[i + 1, 3]) - con_x2[i=1:nh], x[i + 1, 2] == x[i, 2] + 0.5 * h * (x[i, 4] + x[i + 1, 4]) - con_x3[i=1:nh], - x[i + 1, 3] == x[i, 3] + 0.5 * h * (a * cos(u[i]) + a * cos(u[i + 1])) - con_x4[i=1:nh], - x[i + 1, 4] == x[i, 4] + 0.5 * h * (a * sin(u[i]) + a * sin(u[i + 1])) + con_x1[i=1:nh], x1[i + 1] == x1[i] + 0.5 * h * (x3[i] + x3[i + 1]) + con_x2[i=1:nh], x2[i + 1] == x2[i] + 0.5 * h * (x4[i] + x4[i + 1]) + con_x3[i=1:nh], x3[i + 1] == x3[i] + 0.5 * h * (a * cos(u[i]) + a * cos(u[i + 1])) + con_x4[i=1:nh], x4[i + 1] == x4[i] + 0.5 * h * (a * sin(u[i]) + a * sin(u[i + 1])) end ) # Boundary conditions - @constraint(model, [j = 1:4], x[1, j] == xs[j]) - @constraint(model, [j = 2:4], x[nh + 1, j] == xf[j]) + @constraint(model, x1[1] == xs[1]) + @constraint(model, x2[1] == xs[2]) + @constraint(model, x3[1] == xs[3]) + @constraint(model, x4[1] == xs[4]) + @constraint(model, x2[nh + 1] == xf[2]) + @constraint(model, x3[nh + 1] == xf[3]) + @constraint(model, x4[nh + 1] == xf[4]) return model -end +end \ No newline at end of file diff --git a/ext/JuMPModels/truck_trailer.jl b/ext/JuMPModels/truck_trailer.jl index 73f47b3..f3bb310 100644 --- a/ext/JuMPModels/truck_trailer.jl +++ b/ext/JuMPModels/truck_trailer.jl @@ -40,16 +40,16 @@ function OptimalControlProblems.truck_trailer( model, begin # Final time - 0.0 <= tf + 0.0 <= tf, (start = 0.1) # State variables - x2[0:nh] - y2[0:nh] + x2[0:nh], (start = 0.1) + y2[0:nh], (start = 0.1) -pi / 2 <= theta0[0:nh] <= pi / 2, (start = 0.1) -pi / 2 <= theta1[0:nh] <= pi / 2, (start = 0.0) theta2[0:nh], (start = 0.0) # Control variables -0.2 * speedf <= v0[0:nh] <= 0.2 * speedf, (start = -0.2) - -pi / 6 <= delta0[0:nh] <= pi / 6 + -pi / 6 <= delta0[0:nh] <= pi / 6, (start = 0.1) end ) diff --git a/ext/JuMPModels/vanderpol.jl b/ext/JuMPModels/vanderpol.jl index e293044..fda1442 100644 --- a/ext/JuMPModels/vanderpol.jl +++ b/ext/JuMPModels/vanderpol.jl @@ -13,9 +13,9 @@ function OptimalControlProblems.vanderpol(::JuMPBackend; nh::Int=100) @variables( model, begin - x1[0:nh] - x2[0:nh] - u[0:nh] + x1[0:nh], (start = 0.1) + x2[0:nh], (start = 0.1) + u[0:nh], (start = 0.1) step[0:nh] == tf / nh end ) diff --git a/ext/MetaData/beam.jl b/ext/MetaData/beam.jl index 276b0c6..8025eff 100644 --- a/ext/MetaData/beam.jl +++ b/ext/MetaData/beam.jl @@ -1 +1,11 @@ -beam_meta = Dict(:name => "beam", :nh => 100, :nvar => 404, :ncon => 305, :minimize => true) +beam_meta = Dict( + :name => "beam", + :nh => 100, + :nvar => 404, + :ncon => 305, + :minimize => true, + :state_name => ["x1", "x2"], + :costate_name => ["con_x1", "con_x2"], + :control_name => ["u"], + :time => ("final_time", "tf", 1.0) +) \ No newline at end of file diff --git a/ext/MetaData/bioreactor.jl b/ext/MetaData/bioreactor.jl index 06d013b..eb16cd7 100644 --- a/ext/MetaData/bioreactor.jl +++ b/ext/MetaData/bioreactor.jl @@ -1,3 +1,11 @@ bioreactor_meta = Dict( - :name => "bioreactor", :nh => 100, :nvar => 505, :ncon => 404, :minimize => false -) + :name => "bioreactor", + :nh => 100, + :nvar => 505, + :ncon => 404, + :minimize => false, + :state_name => ["y", "s", "b"], + :costate_name => ["con_y", "con_s", "con_b"], + :control_name => ["u"], + :time => ("final_time", "T", 300.0) # T = 10 * N where N = 30 by default +) \ No newline at end of file diff --git a/ext/MetaData/cart_pendulum.jl b/ext/MetaData/cart_pendulum.jl index 1f674e4..e74aa86 100644 --- a/ext/MetaData/cart_pendulum.jl +++ b/ext/MetaData/cart_pendulum.jl @@ -1,3 +1,11 @@ cart_pendulum_meta = Dict( - :name => "cart_pendulum", :nh => 100, :nvar => 507, :ncon => 405, :minimize => true -) + :name => "cart_pendulum", + :nh => 100, + :nvar => 507, + :ncon => 405, + :minimize => true, + :state_name => ["x", "dx", "theta", "omega"], + :costate_name => ["d_x", "d_dx", "d_theta", "d_omega"], + :control_name => "Fex", + :time => ("final_time", "tf", nothing) +) \ No newline at end of file diff --git a/ext/MetaData/chain.jl b/ext/MetaData/chain.jl index f09be65..8e93ae5 100644 --- a/ext/MetaData/chain.jl +++ b/ext/MetaData/chain.jl @@ -1,3 +1,11 @@ chain_meta = Dict( - :name => "chain", :nh => 100, :nvar => 404, :ncon => 305, :minimize => true -) + :name => "chain", + :nh => 100, + :nvar => 404, + :ncon => 305, + :minimize => true, + :state_name => ["x1", "x2", "x3"], + :costate_name => ["con_x1", "con_x2", "con_x3"], + :control_name => ["u"], + :time => ("final_time", "tf", 1.0) +) \ No newline at end of file diff --git a/ext/MetaData/dielectrophoretic_particle.jl b/ext/MetaData/dielectrophoretic_particle.jl index b0552c3..0084aed 100644 --- a/ext/MetaData/dielectrophoretic_particle.jl +++ b/ext/MetaData/dielectrophoretic_particle.jl @@ -4,4 +4,8 @@ dielectrophoretic_particle_meta = Dict( :nvar => 304, :ncon => 203, :minimize => true, -) + :state_name => ["x", "y"], + :costate_name => ["con_x", "con_y"], + :control_name => "u", + :time => ("final_time", "tf", nothing) +) \ No newline at end of file diff --git a/ext/MetaData/double_oscillator.jl b/ext/MetaData/double_oscillator.jl index 67b37d9..d413fd9 100644 --- a/ext/MetaData/double_oscillator.jl +++ b/ext/MetaData/double_oscillator.jl @@ -1,7 +1,11 @@ double_oscillator_meta = Dict( :name => "double_oscillator", - :nh => nothing, + :nh => 100, #:nh => nothing, :nvar => nothing, :ncon => nothing, :minimize => true, -) + :state_name => ["x1", "x2", "x3", "x4"], + :costate_name => ["con_x1", "con_x2", "con_x3", "con_x4"], + :control_name => "u", + :time => ("final_time", "tf", 2π) +) \ No newline at end of file diff --git a/ext/MetaData/ducted_fan.jl b/ext/MetaData/ducted_fan.jl index b7022db..de038e1 100644 --- a/ext/MetaData/ducted_fan.jl +++ b/ext/MetaData/ducted_fan.jl @@ -1,7 +1,11 @@ ducted_fan_meta = Dict( :name => "ducted_fan", - :nh => nothing, + :nh => 100, # :nh => nothing, :nvar => nothing, :ncon => nothing, :minimize => true, -) + :state_name => ["x1", "v1", "x2", "v2", "α", "vα"], + :costate_name => ["con_x1", "con_v1", "con_x2", "con_v2", "con_α", "con_vα"], + :control_name => ["u1", "u2"], + :time => ("final_time", "tf", nothing) +) \ No newline at end of file diff --git a/ext/MetaData/electrical_vehicle.jl b/ext/MetaData/electrical_vehicle.jl index 50085ee..dee8d42 100644 --- a/ext/MetaData/electrical_vehicle.jl +++ b/ext/MetaData/electrical_vehicle.jl @@ -1,7 +1,11 @@ electrical_vehicle_meta = Dict( :name => "electrical_vehicle", - :nh => nothing, + :nh => 100, #:nh => nothing, :nvar => nothing, :ncon => nothing, :minimize => true, -) + :state_name => ["x", "v"], + :costate_name => ["cond_x", "cond_v"], + :control_name => "u", + :time => ("final_time", "tf", 1.0) +) \ No newline at end of file diff --git a/ext/MetaData/glider.jl b/ext/MetaData/glider.jl index 2495ebb..be8a91d 100644 --- a/ext/MetaData/glider.jl +++ b/ext/MetaData/glider.jl @@ -1,3 +1,11 @@ glider_meta = Dict( - :name => "glider", :nh => 100, :nvar => 506, :ncon => 407, :minimize => false -) + :name => "glider", + :nh => 100, + :nvar => 506, + :ncon => 407, + :minimize => false, + :state_name => ["x", "y", "vx", "vy"], + :costate_name => ["x_eqn", "y_eqn", "vx_eqn", "vy_eqn"], + :control_name => "cL", + :time => ("final_time", "tf", nothing) +) \ No newline at end of file diff --git a/ext/MetaData/insurance.jl b/ext/MetaData/insurance.jl index 1f0084e..87c1485 100644 --- a/ext/MetaData/insurance.jl +++ b/ext/MetaData/insurance.jl @@ -1,3 +1,11 @@ insurance_meta = Dict( - :name => "insurance", :nh => 100, :nvar => 910, :ncon => 809, :minimize => false -) + :name => "insurance", + :nh => 100, #:nh => nothing, + :nvar => nothing, + :ncon => nothing, + :minimize => false, + :state_name => ["I", "m", "x3"], + :costate_name => ["con_dI", "con_dm", "con_dx3"], + :control_name => ["h", "R", "H", "U", "dUdR"], + :time => ("final_time", "tf", 10) +) \ No newline at end of file diff --git a/ext/MetaData/jackson.jl b/ext/MetaData/jackson.jl index bde0add..32302f6 100644 --- a/ext/MetaData/jackson.jl +++ b/ext/MetaData/jackson.jl @@ -1,3 +1,11 @@ jackson_meta = Dict( - :name => "jackson", :nh => 100, :nvar => 404, :ncon => 303, :minimize => false -) + :name => "jackson", + :nh => 100, + :nvar => 404, + :ncon => 303, + :minimize => false, + :state_name => ["a", "b", "x3"], + :costate_name => ["con_da", "con_db", "con_dx3"], + :control_name => ["u"], + :time => ("final_time", "tf", 4.0) +) \ No newline at end of file diff --git a/ext/MetaData/moonlander.jl b/ext/MetaData/moonlander.jl index 0a17cdc..d526688 100644 --- a/ext/MetaData/moonlander.jl +++ b/ext/MetaData/moonlander.jl @@ -1,3 +1,11 @@ moonlander_meta = Dict( - :name => "moonlander", :nh => 100, :nvar => 610, :ncon => 809, :minimize => true -) + :name => "moonlander", + :nh => 100, + :nvar => 610, + :ncon => 809, + :minimize => true, + :state_name => ["p1", "p2", "dp1", "dp2", "theta", "dtheta"], + :costate_name => ["d_p1", "d_p2", "d_dp1", "d_dp2", "d_theta", "d_dtheta"], + :control_name => ["F1", "F2"], + :time => ("final_time", "tf", nothing) +) \ No newline at end of file diff --git a/ext/MetaData/quadrotor.jl b/ext/MetaData/quadrotor.jl index 2da328b..5abf6c2 100644 --- a/ext/MetaData/quadrotor.jl +++ b/ext/MetaData/quadrotor.jl @@ -1,7 +1,11 @@ quadrotor_meta = Dict( :name => "quadrotor", - :nh => nothing, + :nh => 60, #:nh => nothing, :nvar => nothing, :ncon => nothing, :minimize => true, -) + :state_name => ["p1", "p2", "p3", "v1", "v2", "v3"], + :costate_name => ["d_p1", "d_p2", "d_p3", "d_v1", "d_v2", "d_v3"], + :control_name => ["at", "ϕ", "θ", "ψ"], + :time => ("final_time", "tf", nothing) +) \ No newline at end of file diff --git a/ext/MetaData/robbins.jl b/ext/MetaData/robbins.jl index ddb9fbd..b5332bd 100644 --- a/ext/MetaData/robbins.jl +++ b/ext/MetaData/robbins.jl @@ -1,3 +1,11 @@ robbins_meta = Dict( - :name => "robbins", :nh => 100, :nvar => 505, :ncon => 407, :minimize => true -) + :name => "robbins", + :nh => 100, + :nvar => 505, + :ncon => 407, + :minimize => true, + :state_name => ["x1", "x2", "x3"], + :costate_name => ["con_x1", "con_x2", "con_dx3"], + :control_name => ["u"], + :time => ("final_time", "tf", 10) +) \ No newline at end of file diff --git a/ext/MetaData/robot.jl b/ext/MetaData/robot.jl index 653962d..070488f 100644 --- a/ext/MetaData/robot.jl +++ b/ext/MetaData/robot.jl @@ -1,3 +1,11 @@ robot_meta = Dict( - :name => "robot", :nh => 100, :nvar => 910, :ncon => 612, :minimize => true -) + :name => "robot", + :nh => 100, + :nvar => 910, + :ncon => 612, + :minimize => true, + :state_name => ["rho", "rho_dot", "the", "the_dot", "phi", "phi_dot"], + :costate_name => ["con_rho", "con_rho_dot", "con_the", "con_the_dot", "con_phi", "con_phi_dot"], + :control_name => ["u_rho", "u_the", "u_phi"], + :time => ("final_time", "tf", nothing) +) \ No newline at end of file diff --git a/ext/MetaData/rocket.jl b/ext/MetaData/rocket.jl index 0ec4c5b..243214c 100644 --- a/ext/MetaData/rocket.jl +++ b/ext/MetaData/rocket.jl @@ -1,3 +1,11 @@ rocket_meta = Dict( - :name => "rocket", :nh => 100, :nvar => 405, :ncon => 304, :minimize => false -) + :name => "rocket", + :nh => 100, + :nvar => 405, + :ncon => 304, + :minimize => false, + :state_name => ["h", "v", "m"], + :costate_name => ["con_dh", "con_dv", "con_dm"], + :control_name => ["T"], + :time => ("step", "step", nothing) +) \ No newline at end of file diff --git a/ext/MetaData/space_shuttle.jl b/ext/MetaData/space_shuttle.jl index 6bf6d84..5689ca8 100644 --- a/ext/MetaData/space_shuttle.jl +++ b/ext/MetaData/space_shuttle.jl @@ -1,7 +1,11 @@ space_shuttle_meta = Dict( :name => "space_shuttle", - :nh => nothing, + :nh => 503, #:nh => nothing, :nvar => nothing, :ncon => nothing, :minimize => false, -) + :state_name => ["scaled_h", "ϕ", "θ", "scaled_v", "γ", "ψ"], + :costate_name => ["con_dh", "con_dϕ", "con_dθ", "con_dv", "con_dγ", "con_dψ"], + :control_name => ["α", "β"], + :time => ("step", "Δt", nothing) +) \ No newline at end of file diff --git a/ext/MetaData/steering.jl b/ext/MetaData/steering.jl index afb1fe9..8eeb049 100644 --- a/ext/MetaData/steering.jl +++ b/ext/MetaData/steering.jl @@ -1,3 +1,11 @@ steering_meta = Dict( - :name => "steering", :nh => 100, :nvar => 506, :ncon => 408, :minimize => true -) + :name => "steering", + :nh => 100, + :nvar => 506, + :ncon => 408, + :minimize => true, + :state_name => ["x1", "x2", "x3", "x4"], + :costate_name => ["con_x1", "con_x2", "con_x3", "con_x4"], + :control_name => "u", + :time => ("final_time", "tf", nothing) +) \ No newline at end of file diff --git a/ext/MetaData/truck_trailer.jl b/ext/MetaData/truck_trailer.jl index 79f4e13..f0f16ec 100644 --- a/ext/MetaData/truck_trailer.jl +++ b/ext/MetaData/truck_trailer.jl @@ -1,3 +1,11 @@ truck_trailer_meta = Dict( - :name => "truck", :nh => nothing, :nvar => nothing, :ncon => nothing, :minimize => true -) + :name => "truck_trailer", + :nh => 100, #:nh => nothing, + :nvar => nothing, + :ncon => nothing, + :minimize => true, + :state_name => ["x2", "y2", "theta2", "theta1", "theta0"], + :costate_name => ["d_x2", "d_y2", "d_theta2", "d_theta1", "d_theta0"], + :control_name => ["v0", "delta0"], + :time => ("final_time", "tf", nothing) +) \ No newline at end of file diff --git a/ext/MetaData/vanderpol.jl b/ext/MetaData/vanderpol.jl index 47d6bb4..d216423 100644 --- a/ext/MetaData/vanderpol.jl +++ b/ext/MetaData/vanderpol.jl @@ -1,3 +1,11 @@ vanderpol_meta = Dict( - :name => "vanderpol", :nh => 100, :nvar => 404, :ncon => 303, :minimize => true -) + :name => "vanderpol", + :nh => 100, + :nvar => 404, + :ncon => 303, + :minimize => true, + :state_name => ["x1", "x2"], + :costate_name => ["con_x1", "con_x2"], + :control_name => "u", + :time => ("final_time", "tf", 2.0) +) \ No newline at end of file diff --git a/ext/OptimalControlModels/cart_pendulum.jl b/ext/OptimalControlModels/cart_pendulum.jl index 8c0ab55..892d58d 100644 --- a/ext/OptimalControlModels/cart_pendulum.jl +++ b/ext/OptimalControlModels/cart_pendulum.jl @@ -90,7 +90,10 @@ function OptimalControlProblems.cart_pendulum(::OptimalControlBackend; nh::Int=1 end # initial guess - init = () + xinit = t -> [0.1, 0.1, 0.1, 0.1] # [x1, dx, theta, omega] + uinit = [0.1] # [Fex] + varinit = [0.1, 0.1] # [tf, ddx] + init = (state=xinit, control=uinit, variable=varinit) # NLPModel + DOCP docp, nlp = direct_transcription(ocp; init=init, grid_size=nh) diff --git a/ext/OptimalControlModels/double_oscillator.jl b/ext/OptimalControlModels/double_oscillator.jl index 8db56f3..2922e9a 100644 --- a/ext/OptimalControlModels/double_oscillator.jl +++ b/ext/OptimalControlModels/double_oscillator.jl @@ -55,7 +55,9 @@ function OptimalControlProblems.double_oscillator(::OptimalControlBackend; nh::I end # Initial guess - init = () + xinit = t -> [0.1, 0.1, 0.1, 0.1] # [x1, x2, x3, x4] + uinit = [0.1] # [u] + init = (state=xinit, control=uinit) # NLPModel + DOCP docp, nlp = direct_transcription(ocp; init=init, grid_size=nh) diff --git a/ext/OptimalControlModels/ducted_fan.jl b/ext/OptimalControlModels/ducted_fan.jl index 067022e..6a3cb52 100644 --- a/ext/OptimalControlModels/ducted_fan.jl +++ b/ext/OptimalControlModels/ducted_fan.jl @@ -77,7 +77,10 @@ function OptimalControlProblems.ducted_fan(::OptimalControlBackend; nh::Int=100) end # Initial guess - init = () + xinit = t -> [0.1, 0.1, 0.1, 0.1, 0.1, 0.1] # [x1, v1, x2, v2, α, vα] + uinit = [0.1, 0.1] # [u1, u2] + varinit = [1.0] # [tf] + init = (state=xinit, control=uinit, variable=varinit) # NLPModel + DOCP docp, nlp = direct_transcription(ocp; init=init, grid_size=nh) diff --git a/ext/OptimalControlModels/electrical_vehicle.jl b/ext/OptimalControlModels/electrical_vehicle.jl index 0941e20..549636e 100644 --- a/ext/OptimalControlModels/electrical_vehicle.jl +++ b/ext/OptimalControlModels/electrical_vehicle.jl @@ -59,7 +59,9 @@ function OptimalControlProblems.electrical_vehicle(::OptimalControlBackend; nh:: end ## Initial guess - init = () + xinit = t -> [0.1, 0.1] # [pos, v] + uinit = [0.1] # [u] + init = (state=xinit, control=uinit) # NLPModel + DOCP docp, nlp = direct_transcription(ocp; init=init, grid_size=nh) diff --git a/ext/OptimalControlModels/insurance.jl b/ext/OptimalControlModels/insurance.jl index 6296793..0bac86d 100644 --- a/ext/OptimalControlModels/insurance.jl +++ b/ext/OptimalControlModels/insurance.jl @@ -71,7 +71,10 @@ function OptimalControlProblems.insurance(::OptimalControlBackend; nh::Int=100) end # Initial guess - init = () + xinit = t -> [0.1, 0.1, 0.1] # [I, m, x3] + uinit = [0.1, 0.1, 0.1, 0.1, 0.1] # [h, R, H, U, dUdR] + varinit = [0.1] # [P] + init = (state=xinit, control=uinit, variable=varinit) # NLPModel + DOCP docp, nlp = direct_transcription(ocp; init=init, grid_size=nh) diff --git a/ext/OptimalControlModels/jackson.jl b/ext/OptimalControlModels/jackson.jl index b11bafa..f8e9edf 100644 --- a/ext/OptimalControlModels/jackson.jl +++ b/ext/OptimalControlModels/jackson.jl @@ -27,7 +27,9 @@ function OptimalControlProblems.jackson(::OptimalControlBackend; nh::Int=100, N: end # Initial guess - init = () + xinit = t -> [0.1, 0.1, 0.1] # [a, b, x3] + uinit = [0.1] # [u] + init = (state=xinit, control=uinit) # NLPModel + DOCP docp, nlp = direct_transcription(ocp; init=init, grid_size=nh) diff --git a/ext/OptimalControlModels/moonlander.jl b/ext/OptimalControlModels/moonlander.jl index f3b90ef..4af69ec 100644 --- a/ext/OptimalControlModels/moonlander.jl +++ b/ext/OptimalControlModels/moonlander.jl @@ -86,7 +86,10 @@ function OptimalControlProblems.moonlander( end # Initial guess - init = (control=[5.0, 5.0],) + xinit = t -> [0.1, 0.1, 0.1, 0.1, 0.1, 0.1] # [p1, p2, dp1, dp2, theta, dtheta] + uinit = [5.0, 5.0] # [F1, F2] + varinit = [0.1] # [tf] + init = (state=xinit, control=uinit, variable=varinit) # NLPModel + DOCP docp, nlp = direct_transcription(ocp; init=init, grid_size=nh) diff --git a/ext/OptimalControlModels/quadrotor.jl b/ext/OptimalControlModels/quadrotor.jl index 111e466..aafd0b7 100644 --- a/ext/OptimalControlModels/quadrotor.jl +++ b/ext/OptimalControlModels/quadrotor.jl @@ -108,7 +108,10 @@ function OptimalControlProblems.quadrotor(::OptimalControlBackend; nh::Int=60) end # Initial guess - init = (control=[10, 0.0, 0.0, 0.0],) + xinit = t -> [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1] # [p1, p2, p3, v1, v2, v3, ϕ, θ] + uinit = [10.0, 0.1, 0.1, 0.1] # [at, ϕ_dot, θ_dot, ψ] + varinit = [0.1] # [tf] + init = (state=xinit, control=uinit, variable=varinit) # NLPModel + DOCP docp, nlp = direct_transcription(ocp; init=init, grid_size=nh) diff --git a/ext/OptimalControlModels/robbins.jl b/ext/OptimalControlModels/robbins.jl index 199fa74..973eac6 100644 --- a/ext/OptimalControlModels/robbins.jl +++ b/ext/OptimalControlModels/robbins.jl @@ -21,7 +21,9 @@ function OptimalControlProblems.robbins(::OptimalControlBackend; nh::Int=100, N: end # Initial guess - init = () + xinit = t -> [0.1, 0.1, 0.1] # [x1, x2, x3] + uinit = [0.1] # [u] + init = (state=xinit, control=uinit) # NLPModel + DOCP docp, nlp = direct_transcription(ocp; init=init, grid_size=nh) diff --git a/ext/OptimalControlModels/truck_trailer.jl b/ext/OptimalControlModels/truck_trailer.jl index 0c15866..32ab3f7 100644 --- a/ext/OptimalControlModels/truck_trailer.jl +++ b/ext/OptimalControlModels/truck_trailer.jl @@ -131,7 +131,10 @@ function OptimalControlProblems.truck_trailer( end # Initial guess - init = (state=[0, 0, 0.1, 0.0, 0.0, -0.2, 0],) + xinit = t -> [0.1, 0.1, 0.1, 0.0, 0.0, -0.2, 0.1] # [x2, y2, theta0, theta1, theta2, v0, delta0] + uinit = [0.1, 0.1] # [d_v0, d_delta0] + varinit = [0.1] # [tf] + init = (state=xinit, control=uinit, variable=varinit) # NLPModel + DOCP docp, nlp = direct_transcription(ocp; init=init, grid_size=nh) diff --git a/ext/OptimalControlModels/vanderpol.jl b/ext/OptimalControlModels/vanderpol.jl index 250e8fc..b21d059 100644 --- a/ext/OptimalControlModels/vanderpol.jl +++ b/ext/OptimalControlModels/vanderpol.jl @@ -18,7 +18,9 @@ function OptimalControlProblems.vanderpol(::OptimalControlBackend; nh::Int=100) end # Initial guess - init = () + xinit = t -> [0.1, 0.1] # [x1, x2] + uinit = [0.1] # [u] + init = (state=xinit, control=uinit) # NLPModel + DOCP docp, nlp = direct_transcription(ocp; init=init, grid_size=nh) diff --git a/src/OptimalControlProblems.jl b/src/OptimalControlProblems.jl index 6bd2710..f4f1e75 100644 --- a/src/OptimalControlProblems.jl +++ b/src/OptimalControlProblems.jl @@ -37,6 +37,10 @@ const infos = [ :nvar :ncon :minimize + :state_name + :costate_name + :control_name + :time ] const types = [ @@ -45,6 +49,10 @@ const types = [ Union{Int,Nothing}, Union{Int,Nothing}, Union{Bool,Nothing}, + Union{Vector{String}, String, Nothing}, + Union{Vector{String}, String, Nothing}, + Union{Vector{String}, String, Nothing}, + Union{Tuple{String, String, Union{Real,Nothing}}, Nothing}, ] """ @@ -71,6 +79,33 @@ for i in 1:number_of_problems end end -export JuMPBackend, OptimalControlBackend +# ------- Available Problems Function ------- +""" + available_problems() + +Returns the list of problems that are currently working based on the last test execution. +The list is read from a cache file that gets updated every time tests are run. +If no cache exists, returns an empty list with a warning to run tests first. +""" +function available_problems() + cache_file = joinpath(dirname(@__FILE__), "..", "available_problems_cache.txt") + if isfile(cache_file) + try + content = read(cache_file, String) + if !isempty(strip(content)) + # Parse symbols from the file + lines = split(strip(content), '\n') + return [Symbol(strip(line)) for line in lines if !isempty(strip(line))] + end + catch e + @warn "Error reading the cache: $e" + end + end + # Default list if the cache does not exist or is empty + @warn "Available problems cache not found. Run the tests to update the list." + return Symbol[] +end + +export JuMPBackend, OptimalControlBackend, available_problems end diff --git a/test/Project.toml b/test/Project.toml index cc8a380..4abba3e 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,16 +1,21 @@ [deps] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" CTBase = "54762871-cc72-4466-b8e8-f6c8b58076cd" +Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" NLPModelsIpopt = "f4238b75-b362-5c4c-b852-0801c9a21d71" OptimalControl = "5f98b655-cc9a-415a-b60e-744165666948" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] Aqua = "0.8" CTBase = "0.16" +Interpolations = "0.15, 0.16" Ipopt = "1.9" JuMP = "1.25" NLPModelsIpopt = "0.10" OptimalControl = "1.0" +Plots = "1.40" +Test = "1" diff --git a/test/figures/beam.png b/test/figures/beam.png new file mode 100644 index 0000000..a5f3640 Binary files /dev/null and b/test/figures/beam.png differ diff --git a/test/figures/bioreactor.png b/test/figures/bioreactor.png new file mode 100644 index 0000000..0b87c82 Binary files /dev/null and b/test/figures/bioreactor.png differ diff --git a/test/figures/chain.png b/test/figures/chain.png new file mode 100644 index 0000000..a05a08f Binary files /dev/null and b/test/figures/chain.png differ diff --git a/test/figures/dielectrophoretic_particle.png b/test/figures/dielectrophoretic_particle.png new file mode 100644 index 0000000..af5eacf Binary files /dev/null and b/test/figures/dielectrophoretic_particle.png differ diff --git a/test/figures/double_oscillator.png b/test/figures/double_oscillator.png new file mode 100644 index 0000000..61bf8fc Binary files /dev/null and b/test/figures/double_oscillator.png differ diff --git a/test/figures/ducted_fan.png b/test/figures/ducted_fan.png new file mode 100644 index 0000000..d37faf0 Binary files /dev/null and b/test/figures/ducted_fan.png differ diff --git a/test/figures/electrical_vehicle.png b/test/figures/electrical_vehicle.png new file mode 100644 index 0000000..12194f2 Binary files /dev/null and b/test/figures/electrical_vehicle.png differ diff --git a/test/figures/insurance.png b/test/figures/insurance.png new file mode 100644 index 0000000..0c16710 Binary files /dev/null and b/test/figures/insurance.png differ diff --git a/test/figures/jackson.png b/test/figures/jackson.png new file mode 100644 index 0000000..86fc181 Binary files /dev/null and b/test/figures/jackson.png differ diff --git a/test/figures/quadrotor.png b/test/figures/quadrotor.png new file mode 100644 index 0000000..1d77879 Binary files /dev/null and b/test/figures/quadrotor.png differ diff --git a/test/figures/robbins.png b/test/figures/robbins.png new file mode 100644 index 0000000..8dfccc0 Binary files /dev/null and b/test/figures/robbins.png differ diff --git a/test/figures/robot.png b/test/figures/robot.png new file mode 100644 index 0000000..cb5b44b Binary files /dev/null and b/test/figures/robot.png differ diff --git a/test/figures/rocket.png b/test/figures/rocket.png new file mode 100644 index 0000000..60a2758 Binary files /dev/null and b/test/figures/rocket.png differ diff --git a/test/figures/steering.png b/test/figures/steering.png new file mode 100644 index 0000000..ab40d57 Binary files /dev/null and b/test/figures/steering.png differ diff --git a/test/figures/vanderpol.png b/test/figures/vanderpol.png new file mode 100644 index 0000000..ef29d8e Binary files /dev/null and b/test/figures/vanderpol.png differ diff --git a/test/runtests.jl b/test/runtests.jl index e53a05e..7615c0c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -6,8 +6,11 @@ using NLPModelsIpopt using OptimalControl using OptimalControlProblems using Test +using Plots +using Interpolations +include("utils.jl") -# Parameters +# Parameters for the solvers const tol = 1e-6 const mu_strategy = "adaptive" const sb = "yes" @@ -15,18 +18,71 @@ const constr_viol_tol = 1e-6 const max_iter = 1000 const max_wall_time = 500.0 -# -@testset verbose = true showtiming = true "OptimalControlProblems tests" begin +# Collect all the problem from OptimalControlProblems +all_names = names(OptimalControlProblems; all=true) +functions_list = filter( + x -> + isdefined(OptimalControlProblems, x) && + isa(getfield(OptimalControlProblems, x), Function) && + !startswith(string(x), "#") && + !(x in [:eval, :include, :available_problems]), + all_names, +) + +# Remove from the tests the problems that are not working +pbs_with_issues = [ + :glider, :moonlander, # issues with OptimalControl + :cart_pendulum, :truck_trailer, # issues with JuMP + :space_shuttle, # not the same probleme between JuMP (tf not fixed) and OptimalControl (tf fixed) +] +functions_list = setdiff(functions_list, pbs_with_issues) + +# The list of all the problems to test +const list_of_problems = deepcopy(functions_list) + +# The final list of problems for which the tests pass +list_of_problems_final = deepcopy(functions_list) + +# Tests +const verbose = true # print or not details during tests +@testset "OptimalControlProblems tests" verbose=verbose showtiming=true begin + for name in ( :aqua, - :JuMP, - :OptimalControl, + :JuMP, # convergence tests for JuMP models + :OptimalControl, # convergence tests for OptimalControl models + :Comparison, # comparison between OptimalControl and JuMP ) - @testset "$(name)" begin + @testset "$(name)" verbose=verbose begin test_name = Symbol(:test_, name) println("Testing: " * string(name)) include("$(test_name).jl") @eval $test_name() end end + + @testset "available_problems" verbose=verbose begin + if list_of_problems_final == available_problems() + @test list_of_problems_final == available_problems() + else + @test list_of_problems_final == available_problems() broken=true + end + end + +end + +# +println("List of problems working:", list_of_problems_final) + +# Sauvegarder la liste des problèmes fonctionnels dans un fichier de cache +cache_file = joinpath(@__DIR__, "..", "available_problems_cache.txt") +try + open(cache_file, "w") do f + for problem in list_of_problems_final + println(f, string(problem)) + end + end + println("Cache des problèmes disponibles mis à jour: $cache_file") +catch e + @warn "Impossible de sauvegarder le cache des problèmes: $e" end diff --git a/test/test_Comparison.jl b/test/test_Comparison.jl new file mode 100644 index 0000000..69f1c1b --- /dev/null +++ b/test/test_Comparison.jl @@ -0,0 +1,338 @@ +function test_Comparison() + + # Comparison Parameters + ε = 1e-2 + p = 2 + + # options for solvers + kwargs_init = Dict( + :print_level => 0, + :tol => tol, + :mu_strategy => mu_strategy, + :sb => sb, + :constr_viol_tol => constr_viol_tol, + :max_iter => 0, + :max_wall_time => max_wall_time, + ) + + kwargs = Dict( + :print_level => 0, + :tol => tol, + :mu_strategy => mu_strategy, + :sb => sb, + :constr_viol_tol => constr_viol_tol, + :max_iter => max_iter, + :max_wall_time => max_wall_time, + ) + + println() + println("\033[1m###########################\033[0m") + println("\033[1m####### COMPARISON ########\033[0m") + println("\033[1m###########################\033[0m") + println() + + for f in list_of_problems + nh = OptimalControlProblems.metadata[f][:nh] + @testset "$(string(f))" verbose = verbose begin + println("############ TEST $f #############") + println() + + #================== INIT ======================# + + ########## OptimalControl ########## + + # Set up the OptimalControl model + docp_init, OC_model_init = OptimalControlProblems.eval(f)(OptimalControlBackend()) + + # Solve the problem + nlp_sol_init = NLPModelsIpopt.ipopt(OC_model_init; kwargs_init...) + + # Build the solution + sol_init = build_OCP_solution(docp_init; primal=nlp_sol_init.solution, dual=nlp_sol_init.multipliers) + + # Retrieves values of variables + x_init_oc = state(sol_init) + p_init_oc = costate(sol_init) + u_init_oc = control(sol_init) + + ############### JuMP ############### + + # Set up the JuMP model + JuMP_init_model = OptimalControlProblems.eval(f)(JuMPBackend()) + set_optimizer(JuMP_init_model, Ipopt.Optimizer) + set_silent(JuMP_init_model) + set_optimizer_attribute(JuMP_init_model, "tol", tol) + set_optimizer_attribute(JuMP_init_model, "constr_viol_tol", constr_viol_tol) + set_optimizer_attribute(JuMP_init_model, "max_iter", 0) + set_optimizer_attribute(JuMP_init_model, "mu_strategy", mu_strategy) + set_optimizer_attribute(JuMP_init_model, "linear_solver", "mumps") + set_optimizer_attribute(JuMP_init_model, "max_wall_time", max_wall_time) + set_optimizer_attribute(JuMP_init_model, "sb", sb) + + # Solve the model + optimize!(JuMP_init_model) + + # Retrieves values of variables + time_data, time_var_name, time_value = OptimalControlProblems.metadata[f][:time] + if time_data == "final_time" + if time_value !== nothing + tf_init = time_value + else + tf_init = value.(JuMP_init_model[Symbol(time_var_name)]) + end + h_init = tf_init / nh + elseif time_data == "step" + if time_value !== nothing + h_init = time_value + else + h_init = value.(JuMP_init_model[Symbol(time_var_name)]) + end + tf_init = h_init * nh + end + t_init = Vector((0:nh) * h_init) + + x_vars = OptimalControlProblems.metadata[f][:state_name] + p_vars = OptimalControlProblems.metadata[f][:costate_name] + u_vars = OptimalControlProblems.metadata[f][:control_name] + + x_jmp_vars_init = [JuMP.value.(JuMP_init_model[Symbol(xv)]) for xv in x_vars] + inds_x_init = axes(x_jmp_vars_init[1], 1) + x_jmp_init = [[x_jmp_vars_init[j][i] for j in 1:length(x_vars)] for i in inds_x_init] + + u_jmp_vars_init = [JuMP.value.(JuMP_init_model[Symbol(uv)]) for uv in u_vars] + inds_u_init = axes(u_jmp_vars_init[1], 1) + u_jmp_init = [[u_jmp_vars_init[j][i] for j in 1:length(u_vars)] for i in inds_u_init] + + p_jmp_vars_init = [JuMP.dual.(JuMP_init_model[Symbol(pv)]) for pv in p_vars] + inds_p_init = axes(p_jmp_vars_init[1], 1) + p_jmp_init = -[[p_jmp_vars_init[j][i] for j in 1:length(p_vars)] for i in inds_p_init] + p_jmp_init = costateInterpolation(p_jmp_init, t_init) + + ############ TEST ############ + @testset "init" verbose=verbose begin + print("Init:\n") + for k in 1:length(x_jmp_init[1]) + dist_x_init = abs(x_init_oc(0)[k] - x_jmp_init[1][k]) + print(" Test x$k : ") + @testset "x$k" verbose=verbose begin + if !(dist_x_init < ε) + print("$dist_x_init < $ε \033[1;33mTest Broken\033[0m\n") + @test dist_x_init < ε broken=true + global list_of_problems_final + list_of_problems_final = setdiff(list_of_problems_final, [f]) + else + print("$dist_x_init < $ε \033[1;32mTest Passed\033[0m\n") + @test dist_x_init < ε + end + end + end + + for k in 1:length(p_jmp_init[1]) + dist_p_init = abs(p_init_oc(0)[k] - p_jmp_init[1][k]) + print(" Test p$k : ") + @testset "p$k" verbose=verbose begin + if !(dist_p_init < ε) + print("$dist_p_init < $ε \033[1;33mTest Broken\033[0m\n") + @test dist_p_init < ε broken=true + else + print("$dist_p_init < $ε \033[1;32mTest Passed\033[0m\n") + @test dist_p_init < ε + end + end + end + + for k in 1:length(u_jmp_init[1]) + dist_u_init = abs(u_init_oc(0)[k] - u_jmp_init[1][k]) + print(" Test u$k : ") + @testset "u$k" verbose=verbose begin + if !(dist_u_init < ε) + print("$dist_u_init < $ε \033[1;33mTest Broken\033[0m\n") + @test dist_u_init < ε broken=true + global list_of_problems_final + list_of_problems_final = setdiff(list_of_problems_final, [f]) + else + print("$dist_u_init < $ε \033[1;32mTest Passed\033[0m\n") + @test dist_u_init < ε + end + end + end + end + + #======================= END INIT ========================# + + #======================= Lp + OBJECTIVE =================# + + ########## OptimalControl ########## + + # Set up the OptimalControl model + docp, OC_model = OptimalControlProblems.eval(f)(OptimalControlBackend()) + + # Solve the problem + nlp_sol = NLPModelsIpopt.ipopt(OC_model; kwargs...) + + # Build the solution + sol = build_OCP_solution(docp; primal=nlp_sol.solution, dual=nlp_sol.multipliers) + + # Retrieves values of variables + x_oc = state(sol) + p_oc = costate(sol) + u_oc = control(sol) + obj_oc = nlp_sol.objective + + ############### JuMP ############### + + # Set up the JuMP model + JuMP_model = OptimalControlProblems.eval(f)(JuMPBackend()) + set_optimizer(JuMP_model, Ipopt.Optimizer) + set_silent(JuMP_model) + set_optimizer_attribute(JuMP_model, "tol", tol) + set_optimizer_attribute(JuMP_model, "constr_viol_tol", constr_viol_tol) + set_optimizer_attribute(JuMP_model, "max_iter", max_iter) + set_optimizer_attribute(JuMP_model, "mu_strategy", mu_strategy) + set_optimizer_attribute(JuMP_model, "linear_solver", "mumps") + set_optimizer_attribute(JuMP_model, "max_wall_time", max_wall_time) + set_optimizer_attribute(JuMP_model, "sb", sb) + + # Solve the model + optimize!(JuMP_model) + + time_data, time_var_name, time_value = OptimalControlProblems.metadata[f][:time] + if time_data == "final_time" + if time_value !== nothing + tf = time_value + else + tf = value.(JuMP_model[Symbol(time_var_name)]) + end + h = tf / nh + elseif time_data == "step" + if time_value !== nothing + h = time_value + else + h = value.(JuMP_model[Symbol(time_var_name)]) + end + tf = h * nh + end + t = Vector((0:nh) * h) + + x_jmp_vars = [JuMP.value.(JuMP_model[Symbol(xv)]) for xv in x_vars] + inds_x = axes(x_jmp_vars[1], 1) + x_jmp = [[x_jmp_vars[j][i] for j in 1:length(x_vars)] for i in inds_x] + + u_jmp_vars = [JuMP.value.(JuMP_model[Symbol(uv)]) for uv in u_vars] + inds_u = axes(u_jmp_vars[1], 1) + u_jmp = [[u_jmp_vars[j][i] for j in 1:length(u_vars)] for i in inds_u] + + p_jmp_vars = [JuMP.dual.(JuMP_model[Symbol(pv)]) for pv in p_vars] + inds_p = axes(p_jmp_vars[1], 1) + p_jmp = -[[p_jmp_vars[j][i] for j in 1:length(p_vars)] for i in inds_p] + p_jmp = costateInterpolation(p_jmp, t) + + obj_jmp = objective_value(JuMP_model) + + dist_obj = abs(obj_oc - obj_jmp) + @testset "objective" verbose=verbose begin + print("Objective:\n") + print(" Test objective : ") + if !(dist_obj < ε) + print("$dist_obj < $ε \033[1;33mTest Broken\033[0m\n") + @test dist_obj < ε broken=true + global list_of_problems_final + list_of_problems_final = setdiff(list_of_problems_final, [f]) + else + print("$dist_obj < $ε \033[1;32mTest Passed\033[0m\n") + @test dist_obj < ε + end + end + + plots_x = Vector{Any}() + plots_p = Vector{Any}() + plots_u = Vector{Any}() + + @testset "norm_L$p" verbose=verbose begin + print("Norm_L$p:\n") + for k in 1:length(x_jmp[1]) + dist_x_Lp = norm_Lp([x_oc((i - 1) * h)[k] - x_jmp[i][k] for i in 1:nh+1], p, h) + print(" Test x$k : ") + @testset "x$k" verbose = verbose begin + if !(dist_x_Lp < ε) + print("$dist_x_Lp < $ε \033[1;33mTest Broken\033[0m\n") + @test dist_x_Lp < ε broken=true + global list_of_problems_final + list_of_problems_final = setdiff(list_of_problems_final, [f]) + else + print("$dist_x_Lp < $ε \033[1;32mTest Passed\033[0m\n") + @test dist_x_Lp < ε + end + end + px = plot(plot(sol)[k]; line=2, label="OptimalControl") # OptimalControl + px = plot!(t, [x_jmp[i][k] for i in 1:nh+1]; xlabel="t", ylabel=string(x_vars[k]), legend=false, line=2, color="red", linestyle=:dash, label="JuMP") # JuMP + push!(plots_x, px) + end + + for k in 1:length(p_jmp[1]) + dist_p_Lp = norm_Lp([p_oc((i - 1) * h)[k] - p_jmp[i][k] for i in 1:nh+1], p, h) + print(" Test p$k : ") + @testset "p$k" verbose = verbose begin + if !(dist_p_Lp < ε) + print("$dist_p_Lp < $ε \033[1;33mTest Broken\033[0m\n") + @test dist_p_Lp < ε broken=true + global list_of_problems_final + list_of_problems_final = setdiff(list_of_problems_final, [f]) + else + print("$dist_p_Lp < $ε \033[1;32mTest Passed\033[0m\n") + @test dist_p_Lp < ε + end + end + pp = plot(plot(sol)[length(x_jmp[1])+k]; line=2, label="OptimalControl") # OptimalControl + pp = plot!(t, [p_jmp[i][k] for i in 1:nh+1]; xlabel="t", ylabel="p_" * string(x_vars[k]), legend=false, line=2, color="red", linestyle=:dash, label="JuMP") # JuMP + push!(plots_p, pp) + end + + for k in 1:length(u_jmp[1]) + dist_u_Lp = norm_Lp([u_oc((i - 1) * h)[k] - u_jmp[i][k] for i in 1:nh+1], p, h) + print(" Test u$k : ") + @testset "u$k" verbose = verbose begin + if !(dist_u_Lp < ε) + print("$dist_u_Lp < $ε \033[1;33mTest Broken\033[0m\n") + @test dist_u_Lp < ε broken=true + global list_of_problems_final + list_of_problems_final = setdiff(list_of_problems_final, [f]) + else + print("$dist_u_Lp < $ε \033[1;32mTest Passed\033[0m\n") + @test dist_u_Lp < ε + end + end + pu = plot(plot(sol)[length(x_jmp[1])+length(p_jmp[1])+k]; line=2, label="OptimalControl") # OptimalControl + pu = plot!(t, [u_jmp[i][k] for i in 1:nh+1]; xlabel="t", ylabel=string(u_vars[k]), legend=false, line=2, color="red", linestyle=:dash, label="JuMP") # JuMP + push!(plots_u, pu) + end + end + + all_plots = vcat(plots_x, plots_p, plots_u) + n = length(all_plots) + + figdir = joinpath(@__DIR__, "figures") + isdir(figdir) || mkpath(figdir) + + fig = plot(all_plots..., layout=(n, 1), size=(900, 220 * n), suptitle="Comparison $f", titlefont=font(18)) + display(fig) + + savefig(fig, joinpath(figdir, "$f" * ".png")) + + println() + println("############ END TEST $f #############") + println() + + #================== END Lp + OBJECTIVE =====================# + + end + end + + println() + println("\033[1m###########################\033[0m") + println("\033[1m##### END COMPARISON ######\033[0m") + println("\033[1m###########################\033[0m") + println() + + +end \ No newline at end of file diff --git a/test/test_JuMP.jl b/test/test_JuMP.jl index 7ed6f69..c80c250 100644 --- a/test/test_JuMP.jl +++ b/test/test_JuMP.jl @@ -1,21 +1,14 @@ # test_JuMP_optimality function test_JuMP() - # Collecting all the OptimalControlProblems.JuMPModels models - all_names = names(OptimalControlProblems; all=true) - functions_list = filter( - x -> - isdefined(OptimalControlProblems, x) && - isa(getfield(OptimalControlProblems, x), Function) && - !startswith(string(x), "#") && - !(x in [:eval, :include]), - all_names, - ) - pbs_with_issues = [:cart_pendulum, :truck_trailer] - functions_list = setdiff(functions_list, pbs_with_issues) + println() + println("\033[1m###########################\033[0m") + println("\033[1m### TEST CONVERGED JuMP ###\033[0m") + println("\033[1m###########################\033[0m") + println() - for f in functions_list - @testset "$(f)" begin + for f in list_of_problems + @testset "$(f)" verbose=verbose begin println(" $f:") # Set up the model model = OptimalControlProblems.eval(f)(JuMPBackend()) @@ -33,11 +26,22 @@ function test_JuMP() print(" Second solve: "); @time optimize!(model) # Test that the solver found an optimal solution println(" termination_status = $(termination_status(model))\n") - if f == :truck_trailer || f == :quadrotor - @test (termination_status(model) == MOI.LOCALLY_INFEASIBLE) || (termination_status(model) == MOI.ITERATION_LIMIT) - else + if termination_status(model) == MOI.LOCALLY_SOLVED @test termination_status(model) == MOI.LOCALLY_SOLVED + print("JuMP: $f converged : \033[1;32mTest Passed\033[0m\n") + else + @test termination_status(model) == MOI.LOCALLY_SOLVED broken=true + print("JuMP : $f converged : \033[1;33mTest Broken\033[0m\n") + global list_of_problems_final + list_of_problems_final = setdiff(list_of_problems_final, [f]) end end end + + println() + println("\033[1m###############################\033[0m") + println("\033[1m### END TEST CONVERGED JuMP ###\033[0m") + println("\033[1m###############################\033[0m") + println() + end diff --git a/test/test_OptimalControl.jl b/test/test_OptimalControl.jl index 698719e..70f6cf7 100644 --- a/test/test_OptimalControl.jl +++ b/test/test_OptimalControl.jl @@ -1,18 +1,5 @@ # test_OptimalControl_optimality function test_OptimalControl() - # Collecting all the OptimalControlProblems.OptimalControlModels models - all_names = names(OptimalControlProblems; all=true) - functions_list = filter( - x -> - isdefined(OptimalControlProblems, x) && - isa(getfield(OptimalControlProblems, x), Function) && - !startswith(string(x), "#") && - !(x in [:eval, :include]), - all_names, - ) - - pbs_with_issues = [:glider, :moonlander] - functions_list = setdiff(functions_list, pbs_with_issues) kwargs = Dict( :print_level => 0, @@ -24,21 +11,37 @@ function test_OptimalControl() :max_wall_time => max_wall_time, ) - for f in functions_list + println() + println("\033[1m#########################################\033[0m") + println("\033[1m##### TEST CONVERGED OptimalControl #####\033[0m") + println("\033[1m#########################################\033[0m") + println() + + for f in list_of_problems println(" $f:") - @testset "$(f)" begin + @testset "$(f)" verbose=verbose begin # Set up the model _, model = OptimalControlProblems.eval(f)(OptimalControlBackend()) print(" First solve: "); @time sol = NLPModelsIpopt.ipopt(model; kwargs...) print(" Second solve: "); @time sol = NLPModelsIpopt.ipopt(model; kwargs...) println(" sol.status = $(sol.status)\n") # Test that the solver found an optimal solution - if f == :truck_trailer || - f == :space_shuttle - @test (sol.status == :infeasible) || (sol.status == :max_iter) - else + if sol.status == :first_order @test sol.status == :first_order + print("OptimalControl : $f converged : \033[1;32mTest Passed\033[0m\n") + else + @test sol.status == :first_order broken=true + print("OptimalControl : $f converged : \033[1;33mTest Broken\033[0m\n") + global list_of_problems_final + list_of_problems_final = setdiff(list_of_problems_final, [f]) end end end + + println() + println("\033[1m#########################################\033[0m") + println("\033[1m### END TEST CONVERGED OptimalControl ###\033[0m") + println("\033[1m#########################################\033[0m") + println() + end diff --git a/test/test_aqua.jl b/test/test_aqua.jl index 4f004e2..e430f32 100644 --- a/test/test_aqua.jl +++ b/test/test_aqua.jl @@ -1,4 +1,10 @@ function test_aqua() + + println() + println("\033[1m###########################\033[0m") + println("\033[1m######## TEST AQUA ########\033[0m") + println("\033[1m###########################\033[0m") + println() @testset "Aqua.jl" begin Aqua.test_all( OptimalControlProblems; @@ -10,4 +16,10 @@ function test_aqua() # do not warn about ambiguities in dependencies Aqua.test_ambiguities(OptimalControlProblems) end + + println() + println("\033[1m###########################\033[0m") + println("\033[1m##### END TEST AQUA #######\033[0m") + println("\033[1m###########################\033[0m") + println() end diff --git a/test/utils.jl b/test/utils.jl new file mode 100644 index 0000000..1d6fa0a --- /dev/null +++ b/test/utils.jl @@ -0,0 +1,47 @@ +function costateInterpolation(p, t) + nx = size(p[1])[1] + n_h = length(t)[1] + res = [zeros(Float64, nx) for _ in 1:n_h] + for j in 1:nx + pj = Interpolations.linear_interpolation(t[1:end-1], [p[i][j] for i in 1:n_h-1], extrapolation_bc=Interpolations.Line()) + f = t -> pj(t) + for i in 1:n_h + res[i][j] = f(t[i]) + end + end + return res +end + +function prettytime(t) + if t < 1e3 + value, units = t, "ns" + elseif t < 1e6 + value, units = t / 1e3, "μs" + elseif t < 1e9 + value, units = t / 1e6, "ms" + else + value, units = t / 1e9, "s" + end + return string(value, " ", units) +end + +### compute norm_Lp for u : R → R + +function norm_Lp(u, p, dt) + if p == Inf + if isa(u[1], Number) + return maximum(abs, u) + else + return maximum([maximum(abs, ui) for ui in u]) + end + end + nu = length(u) + if nu < 2 + return (sum(abs.(u[1]) .^ p) * dt)^(1 / p) + end + s = 0.0 + for i in 1:nu-1 + s += 0.5 * (sum(abs.(u[i]) .^ p) + sum(abs.(u[i+1]) .^ p)) + end + return (s * dt)^(1 / p) +end \ No newline at end of file