diff --git a/src/ctparser_utils.jl b/src/ctparser_utils.jl index 65c992e3..f3af294a 100644 --- a/src/ctparser_utils.jl +++ b/src/ctparser_utils.jl @@ -14,7 +14,7 @@ expr_it(e, _Expr, f) = if e isa Expr args = e.args n = length(args) - newargs = [ expr_it(e.args[i], _Expr, f) for i ∈ 1:n ] + newargs = [ expr_it(e.args[i], _Expr, f) for i ∈ 1:n ] return _Expr(e.head, newargs...) else return f(e) @@ -115,15 +115,15 @@ replace_call(e, x::Vector{Symbol}, t, y) = begin @assert length(x) == length(y) foo(x, t, y) = (h, args...) -> begin ee = Expr(h, args...) - @match ee begin + @match ee begin :( $eee($tt) ) && if tt == t end => let ch = false for i ∈ 1:length(x) - if has(eee, x[i]) - eee = subs(eee, x[i], y[i]) - ch = true + if has(eee, x[i]) + eee = subs(eee, x[i], y[i]) + ch = true # todo: unnecessary (as subs can be idempotent)? + end end - end ch ? eee : ee end _ => ee @@ -188,7 +188,7 @@ end """ $(TYPEDSIGNATURES) -Return true if e contains an `(...x...)(t)` call. +Return true if e contains a `(...x...)(t)` call. # Example ```jldoctest @@ -205,9 +205,9 @@ true has(e, x, t) = begin foo(x, t) = (h, args...) -> begin ee = Expr(h, args...) - if :yes ∈ args - :yes - else @match ee begin + if :yes ∈ args + :yes + else @match ee begin :( $eee($tt) ) => (tt == t && has(eee, x)) ? :yes : ee _ => ee end end @@ -215,6 +215,7 @@ has(e, x, t) = begin expr_it(e, foo(x, t), x -> x) == :yes end + """ $(TYPEDSIGNATURES) @@ -369,4 +370,4 @@ constraint_type(e, t, t0, tf, x, u, v) = begin _ => :variable_fun end _ => :other end -end +end \ No newline at end of file diff --git a/src/model.jl b/src/model.jl index d9ffaaf4..4c4bc551 100644 --- a/src/model.jl +++ b/src/model.jl @@ -113,7 +113,7 @@ Define the variable dimension and possibly the names of each component. # Examples ```jldoctest julia> variable!(ocp, 1, "v") -julia> variable!(ocp, 2, "v", [ "v₁", "v₂" ]) +julia> variable!(ocp, 2, "v", [ "v₁", "v₂" ]) ``` """ function variable!(ocp::OptimalControlModel, q::Dimension, name::String=__variable_name(), @@ -129,6 +129,14 @@ function variable!(ocp::OptimalControlModel, q::Dimension, name::String=__variab nothing # to force to return nothing end +function variable!(ocp::OptimalControlModel, q::Dimension, name::Symbol, components_names::Vector{Symbol}) + variable!(ocp, q, string(name), string.(components_names)) +end + +function variable!(ocp::OptimalControlModel, q::Dimension, name::Symbol, components_names::Vector{String}) + variable!(ocp, q, string(name), components_names) +end + function variable!(ocp::OptimalControlModel, q::Dimension, name::Symbol) variable!(ocp, q, string(name)) end @@ -187,6 +195,15 @@ function state!(ocp::OptimalControlModel, n::Dimension, name::String=__state_nam ocp.state_name = name nothing # to force to return nothing end + +function state!(ocp::OptimalControlModel, n::Dimension, name::Symbol, components_names::Vector{Symbol}) + state!(ocp, n, string(name), string.(components_names)) +end + +function state!(ocp::OptimalControlModel, n::Dimension, name::Symbol, components_names::Vector{String}) + state!(ocp, n, string(name), components_names) +end + function state!(ocp::OptimalControlModel, n::Dimension, name::Symbol) state!(ocp, n, string(name)) end @@ -246,6 +263,14 @@ function control!(ocp::OptimalControlModel, m::Dimension, name::String=__control nothing # to force to return nothing end +function control!(ocp::OptimalControlModel, m::Dimension, name::Symbol, components_names::Vector{Symbol}) + control!(ocp, m, string(name), string.(components_names)) +end + +function control!(ocp::OptimalControlModel, m::Dimension, name::Symbol, components_names::Vector{String}) + control!(ocp, m, string(name), components_names) +end + function control!(ocp::OptimalControlModel, m::Dimension, name::Symbol) control!(ocp, m, string(name)) end @@ -438,12 +463,12 @@ Add an `:initial`, `:final`, `:control`, `:state` or `:variable` box constraint # Examples ```jldoctest -julia> constraint!(ocp, :initial, 2:3, [ 0, 0 ], [ 1, 2 ]) +julia> constraint!(ocp, :initial, 2:3, [ 0, 0 ], [ 1, 2 ]) julia> constraint!(ocp, :final, Index(1), 0, 2) julia> constraint!(ocp, :control, Index(1), 0, 2) -julia> constraint!(ocp, :state, 2:3, [ 0, 0 ], [ 1, 2 ]) -julia> constraint!(ocp, :initial, 1:2:5, [ 0, 0, 0 ], [ 1, 2, 1 ]) -julia> constraint!(ocp, :variable, 1:2, [ 0, 0 ], [ 1, 2 ]) +julia> constraint!(ocp, :state, 2:3, [ 0, 0 ], [ 1, 2 ]) +julia> constraint!(ocp, :initial, 1:2:5, [ 0, 0, 0 ], [ 1, 2, 1 ]) +julia> constraint!(ocp, :variable, 1:2, [ 0, 0 ], [ 1, 2 ]) ``` """ function constraint!(ocp::OptimalControlModel{<: TimeDependence, V}, type::Symbol, rg::RangeConstraint, lb::ctVector, ub::ctVector, @@ -512,8 +537,8 @@ Add an `:initial` or `:final` value constraint on a range of the state, or a val # Examples ```jldoctest -julia> constraint!(ocp, :initial, 1:2:5, [ 0, 0, 0 ]) -julia> constraint!(ocp, :initial, 2:3, [ 0, 0 ]) +julia> constraint!(ocp, :initial, 1:2:5, [ 0, 0, 0 ]) +julia> constraint!(ocp, :initial, 2:3, [ 0, 0 ]) julia> constraint!(ocp, :final, Index(2), 0) julia> constraint!(ocp, :variable, 2:3, [ 0, 3 ]) ``` @@ -566,9 +591,9 @@ Add an `:initial` or `:final` value constraint on the state, or a `:variable` va # Examples ```jldoctest -julia> constraint!(ocp, :initial, [ 0, 0 ]) +julia> constraint!(ocp, :initial, [ 0, 0 ]) julia> constraint!(ocp, :final, 2) # if the state is of dimension 1 -julia> constraint!(ocp, :variable, [ 3, 0, 1 ]) +julia> constraint!(ocp, :variable, [ 3, 0, 1 ]) ``` """ function constraint!(ocp::OptimalControlModel, type::Symbol, val::ctVector, label::Symbol=__constraint_label()) @@ -618,10 +643,10 @@ Add an `:initial`, `:final`, `:control`, `:state` or `:variable` box constraint # Examples ```jldoctest -julia> constraint!(ocp, :initial, [ 0, 0, 0 ], [ 1, 2, 1 ]) -julia> constraint!(ocp, :final, [ 0, 0, 0 ], [ 1, 2, 1 ]) +julia> constraint!(ocp, :initial, [ 0, 0, 0 ], [ 1, 2, 1 ]) +julia> constraint!(ocp, :final, [ 0, 0, 0 ], [ 1, 2, 1 ]) julia> constraint!(ocp, :control, [ 0, 0 ], [ 2, 3 ]) -julia> constraint!(ocp, :state, [ 0, 0, 0 ], [ 1, 2, 1 ]) +julia> constraint!(ocp, :state, [ 0, 0, 0 ], [ 1, 2, 1 ]) julia> constraint!(ocp, :variable, 0, 1) # the variable here is of dimension 1 ``` """ @@ -687,22 +712,22 @@ julia> constraint!(ocp, :boundary, (x0, xf, v) -> x0[3]+xf[2]*v[1], 0, 1) # time independent and variable independent ocp julia> constraint!(ocp, :control, u -> 2u, 0, 1) -julia> constraint!(ocp, :state, x -> x-1, [ 0, 0, 0 ], [ 1, 2, 1 ]) +julia> constraint!(ocp, :state, x -> x-1, [ 0, 0, 0 ], [ 1, 2, 1 ]) julia> constraint!(ocp, :mixed, (x, u) -> x[1]-u, 0, 1) # time dependent and variable independent ocp julia> constraint!(ocp, :control, (t, u) -> 2u, 0, 1) -julia> constraint!(ocp, :state, (t, x) -> x-t, [ 0, 0, 0 ], [ 1, 2, 1 ]) +julia> constraint!(ocp, :state, (t, x) -> x-t, [ 0, 0, 0 ], [ 1, 2, 1 ]) julia> constraint!(ocp, :mixed, (t, x, u) -> x[1]-u, 0, 1) # time independent and variable dependent ocp julia> constraint!(ocp, :control, (u, v) -> 2u*v[1], 0, 1) -julia> constraint!(ocp, :state, (x, v) -> x-v[1], [ 0, 0, 0 ], [ 1, 2, 1 ]) +julia> constraint!(ocp, :state, (x, v) -> x-v[1], [ 0, 0, 0 ], [ 1, 2, 1 ]) julia> constraint!(ocp, :mixed, (x, u, v) -> x[1]-v[2]*u, 0, 1) # time dependent and variable dependent ocp julia> constraint!(ocp, :control, (t, u, v) -> 2u+v[2], 0, 1) -julia> constraint!(ocp, :state, (t, x, v) -> x-t*v[1], [ 0, 0, 0 ], [ 1, 2, 1 ]) +julia> constraint!(ocp, :state, (t, x, v) -> x-t*v[1], [ 0, 0, 0 ], [ 1, 2, 1 ]) julia> constraint!(ocp, :mixed, (t, x, u, v) -> x[1]*v[2]-u, 0, 1) ``` """ @@ -761,22 +786,22 @@ julia> constraint!(ocp, :boundary, (x0, xf, v) -> x0[3]+xf[2]*v[1], 0) # time independent and variable independent ocp julia> constraint!(ocp, :control, u -> 2u, 1) -julia> constraint!(ocp, :state, x -> x-1, [ 0, 0, 0 ]) +julia> constraint!(ocp, :state, x -> x-1, [ 0, 0, 0 ]) julia> constraint!(ocp, :mixed, (x, u) -> x[1]-u, 0) # time dependent and variable independent ocp julia> constraint!(ocp, :control, (t, u) -> 2u, 1) -julia> constraint!(ocp, :state, (t, x) -> x-t, [ 0, 0, 0 ]) +julia> constraint!(ocp, :state, (t, x) -> x-t, [ 0, 0, 0 ]) julia> constraint!(ocp, :mixed, (t, x, u) -> x[1]-u, 0) # time independent and variable dependent ocp julia> constraint!(ocp, :control, (u, v) -> 2u*v[1], 1) -julia> constraint!(ocp, :state, (x, v) -> x-v[2], [ 0, 0, 0 ]) +julia> constraint!(ocp, :state, (x, v) -> x-v[2], [ 0, 0, 0 ]) julia> constraint!(ocp, :mixed, (x, u) -> x[1]-u+v[1], 0) # time dependent and variable dependent ocp julia> constraint!(ocp, :control, (t, u, v) -> 2u-t*v[2], 1) -julia> constraint!(ocp, :state, (t, x, v) -> x-t+v[1], [ 0, 0, 0 ]) +julia> constraint!(ocp, :state, (t, x, v) -> x-t+v[1], [ 0, 0, 0 ]) julia> constraint!(ocp, :mixed, (t, x, u, v) -> x[1]-u*v[1], 0) ``` """ @@ -800,12 +825,12 @@ Add an `:initial`, `:final`, `:control`, `:state` or `:variable` box constraint # Examples ```jldoctest -julia> constraint!(ocp, :initial, rg=2:3, lb=[ 0, 0 ], ub=[ 1, 2 ]) +julia> constraint!(ocp, :initial, rg=2:3, lb=[ 0, 0 ], ub=[ 1, 2 ]) julia> constraint!(ocp, :final, val=Index(1), lb=0, ub=2) julia> constraint!(ocp, :control, val=Index(1), lb=0, ub=2) -julia> constraint!(ocp, :state, rg=2:3, lb=[ 0, 0 ], ub=[ 1, 2 ]) -julia> constraint!(ocp, :initial, rg=1:2:5, lb=[ 0, 0, 0 ], ub=[ 1, 2, 1 ]) -julia> constraint!(ocp, :variable, rg=1:2, lb=[ 0, 0 ], ub=[ 1, 2 ]) +julia> constraint!(ocp, :state, rg=2:3, lb=[ 0, 0 ], ub=[ 1, 2 ]) +julia> constraint!(ocp, :initial, rg=1:2:5, lb=[ 0, 0, 0 ], ub=[ 1, 2, 1 ]) +julia> constraint!(ocp, :variable, rg=1:2, lb=[ 0, 0 ], ub=[ 1, 2 ]) ``` """ function constraint!(ocp::OptimalControlModel{<: TimeDependence, <: VariableDependence}, type::Symbol; diff --git a/src/onepass.jl b/src/onepass.jl index 1fc8eec6..11455546 100644 --- a/src/onepass.jl +++ b/src/onepass.jl @@ -9,6 +9,7 @@ # - tests exceptions (parsing and semantics/runtime) # - add assert for pre/post conditions and invariants # - add tests on ParsingError + run time errors (wrapped in try ... catch's - use string to be precise) +# - currently "t ∈ [ 0+0, 1 ], time" is allowed, and compels to declare "x(0+0) == ..." """ $(TYPEDEF) @@ -66,47 +67,88 @@ Foo ``` """ parse!(p, ocp, e; log=false) = begin + # p.lnum = p.lnum + 1 p.line = string(e) for a ∈ keys(p.aliases) e = subs(e, a, p.aliases[a]) end + # @match e begin - :( $v ∈ R^$q, variable ) => p_variable!(p, ocp, v, q; log) - :( $v ∈ R , variable ) => p_variable!(p, ocp, v ; log) - :( $v , variable ) => p_variable!(p, ocp, v ; log) # todo: remove - :( $t ∈ [ $t0, $tf ], time ) => p_time!(p, ocp, t, t0, tf; log) - :( $x ∈ R^$n, state ) => p_state!(p, ocp, x, n; log) - :( $x ∈ R , state ) => p_state!(p, ocp, x ; log) - :( $x , state ) => p_state!(p, ocp, x ; log) # todo: remove - :( $u ∈ R^$m, control ) => p_control!(p, ocp, u, m; log) - :( $u ∈ R , control ) => p_control!(p, ocp, u ; log) - :( $u , control ) => p_control!(p, ocp, u ; log) # todo: remove - :( $a = $e1 ) => p_alias!(p, ocp, a, e1; log) - :( ∂($x)($t) == $e1 ) => p_dynamics!(p, ocp, x, t, e1 ; log) - :( ∂($x)($t) == $e1, $label ) => p_dynamics!(p, ocp, x, t, e1, label; log) - :( $e1 == $e2 ) => p_constraint!(p, ocp, e2 , e1, e2 ; log) - :( $e1 == $e2, $label ) => p_constraint!(p, ocp, e2 , e1, e2, label; log) - :( $e1 ≤ $e2 ≤ $e3 ) => p_constraint!(p, ocp, e1 , e2, e3 ; log) - :( $e1 ≤ $e2 ≤ $e3, $label ) => p_constraint!(p, ocp, e1 , e2, e3 , label; log) - :( $e2 ≤ $e3 ) => p_constraint!(p, ocp, nothing, e2, e3 ; log) - :( $e2 ≤ $e3, $label ) => p_constraint!(p, ocp, nothing, e2, e3 , label; log) - :( $e3 ≥ $e2 ≥ $e1 ) => p_constraint!(p, ocp, e1 , e2, e3 ; log) - :( $e3 ≥ $e2 ≥ $e1, $label ) => p_constraint!(p, ocp, e1 , e2, e3 , label; log) - :( $e2 ≥ $e1 ) => p_constraint!(p, ocp, e1 , e2, nothing ; log) - :( $e2 ≥ $e1, $label ) => p_constraint!(p, ocp, e1 , e2, nothing, label; log) - :( ∫($e1) → min ) => p_lagrange!(p, ocp, e1, :min; log) - :( ∫($e1) → max ) => p_lagrange!(p, ocp, e1, :max; log) - :( $e1 + ∫($e2) → min ) => p_bolza!(p, ocp, e1, e2 , :min; log) - :( $e1 - ∫($e2) → min ) => p_bolza!(p, ocp, e1, :( -$e2 ), :min; log) - :( $e1 + ∫($e2) → max ) => p_bolza!(p, ocp, e1, e2 , :max; log) - :( $e1 - ∫($e2) → max ) => p_bolza!(p, ocp, e1, :( -$e2 ), :max; log) - :( ∫($e2) + $e1 → min ) => p_bolza!(p, ocp, e1, e2 , :min; log) - :( ∫($e2) - $e1 → min ) => p_bolza!(p, ocp, :( -$e1 ), e2 , :min; log) - :( ∫($e2) + $e1 → max ) => p_bolza!(p, ocp, e1, e2 , :max; log) - :( ∫($e2) - $e1 → max ) => p_bolza!(p, ocp, :( -$e1 ), e2 , :max; log) - :( $e1 → min ) => p_mayer!(p, ocp, e1, :min; log) - :( $e1 → max ) => p_mayer!(p, ocp, e1, :max; log) + # aliases + :( $a = $e1 ) => + @match e1 begin + :( ($names) ∈ R^$q, variable ) => p_variable!(p, ocp, a, q; components_names=names, log) + :( [$names] ∈ R^$q, variable ) => p_variable!(p, ocp, a, q; components_names=names, log) + :( ($names) ∈ R^$n, state ) => p_state!(p, ocp, a, n; components_names=names, log) + :( [$names] ∈ R^$n, state ) => p_state!(p, ocp, a, n; components_names=names, log) + :( ($names) ∈ R^$m, control ) => p_control!(p, ocp, a, m; components_names=names, log) + :( [$names] ∈ R^$m, control ) => p_control!(p, ocp, a, m; components_names=names, log) + _ => p_alias!(p, ocp, a, e1; log) # alias + end + # variable + :( $v ∈ R^$q, variable ) => p_variable!(p, ocp, v, q; log) + :( $v ∈ R , variable ) => p_variable!(p, ocp, v ; log) + # time + :( $t ∈ [ $t0, $tf ], time ) => p_time!(p, ocp, t, t0, tf; log) + # state + :( $x ∈ R^$n, state ) => p_state!(p, ocp, x, n; log) + :( $x ∈ R , state ) => p_state!(p, ocp, x ; log) + # control + :( $u ∈ R^$m, control ) => p_control!(p, ocp, u, m; log) + :( $u ∈ R , control ) => p_control!(p, ocp, u ; log) + # dynamics + :( ∂($x)($t) == $e1 ) => p_dynamics!(p, ocp, x, t, e1 ; log) + :( ∂($x)($t) == $e1, $label ) => p_dynamics!(p, ocp, x, t, e1, label; log) + # constraints + :( $e1 == $e2 ) => p_constraint!(p, ocp, e2 , e1, e2 ; log) + :( $e1 == $e2, $label ) => p_constraint!(p, ocp, e2 , e1, e2, label; log) + :( $e1 ≤ $e2 ≤ $e3 ) => p_constraint!(p, ocp, e1 , e2, e3 ; log) + :( $e1 ≤ $e2 ≤ $e3, $label ) => p_constraint!(p, ocp, e1 , e2, e3 , label; log) + :( $e2 ≤ $e3 ) => p_constraint!(p, ocp, nothing, e2, e3 ; log) + :( $e2 ≤ $e3, $label ) => p_constraint!(p, ocp, nothing, e2, e3 , label; log) + :( $e3 ≥ $e2 ≥ $e1 ) => p_constraint!(p, ocp, e1 , e2, e3 ; log) + :( $e3 ≥ $e2 ≥ $e1, $label ) => p_constraint!(p, ocp, e1 , e2, e3 , label; log) + :( $e2 ≥ $e1 ) => p_constraint!(p, ocp, e1 , e2, nothing ; log) + :( $e2 ≥ $e1, $label ) => p_constraint!(p, ocp, e1 , e2, nothing, label; log) + # lagrange cost + :( ∫($e1) → min ) => p_lagrange!(p, ocp, e1 , :min; log) + :( - ∫($e1) → min ) => p_lagrange!(p, ocp, :( -$e1 ) , :min; log) + :( $e1 * ∫($e2) → min ) => has(e1, p.t) ? ( return __throw("time $(p.t) must not appear in $e1", p.lnum, p.line) ) : + p_lagrange!(p, ocp, :( $e1 * $e2 ), :min; log) + :( ∫($e1) → max ) => p_lagrange!(p, ocp, e1 , :max; log) + :( - ∫($e1) → max ) => p_lagrange!(p, ocp, :( -$e1 ) , :max; log) + :( $e1 * ∫($e2) → max ) => has(e1, p.t) ? ( return __throw("time $(p.t) must not appear in $e1", p.lnum, p.line) ) : + p_lagrange!(p, ocp, :( $e1 * $e2 ), :max; log) + # bolza cost + :( $e1 + ∫($e2) → min ) => p_bolza!(p, ocp, e1, e2 , :min; log) + :( $e1 + $e2 * ∫($e3) → min ) => has(e2, p.t) ? ( return __throw("time $(p.t) must not appear in $e2", p.lnum, p.line) ) : + p_bolza!(p, ocp, e1, :( $e2 * $e3 ), :min; log) + :( $e1 - ∫($e2) → min ) => p_bolza!(p, ocp, e1, :( -$e2 ) , :min; log) + :( $e1 - $e2 * ∫($e3) → min ) => has(e2, p.t) ? ( return __throw("time $(p.t) must not appear in $e2", p.lnum, p.line) ) : + p_bolza!(p, ocp, e1, :( -$e2 * $e3 ), :min; log) + :( $e1 + ∫($e2) → max ) => p_bolza!(p, ocp, e1, e2 , :max; log) + :( $e1 + $e2 * ∫($e3) → max ) => has(e2, p.t) ? ( return __throw("time $(p.t) must not appear in $e2", p.lnum, p.line) ) : + p_bolza!(p, ocp, e1, :( $e2 * $e3 ), :max; log) + :( $e1 - ∫($e2) → max ) => p_bolza!(p, ocp, e1, :( -$e2 ) , :max; log) + :( $e1 - $e2 * ∫($e3) → max ) => has(e2, p.t) ? ( return __throw("time $(p.t) must not appear in $e2", p.lnum, p.line) ) : + p_bolza!(p, ocp, e1, :( -$e2 * $e3 ), :max; log) + :( ∫($e2) + $e1 → min ) => p_bolza!(p, ocp, e1, e2 , :min; log) + :( $e2 * ∫($e3) + $e1 → min ) => has(e2, p.t) ? ( return __throw("time $(p.t) must not appear in $e2", p.lnum, p.line) ) : + p_bolza!(p, ocp, e1, :( $e2 * $e3 ), :min; log) + :( ∫($e2) - $e1 → min ) => p_bolza!(p, ocp, :( -$e1 ), e2 , :min; log) + :( $e2 * ∫($e3) - $e1 → min ) => has(e2, p.t) ? ( return __throw("time $(p.t) must not appear in $e2", p.lnum, p.line) ) : + p_bolza!(p, ocp, :( -$e1 ), :( $e2 * $e3 ), :min; log) + :( ∫($e2) + $e1 → max ) => p_bolza!(p, ocp, e1, e2 , :max; log) + :( $e2 * ∫($e3) + $e1 → max ) => has(e2, p.t) ? ( return __throw("time $(p.t) must not appear in $e2", p.lnum, p.line) ) : + p_bolza!(p, ocp, e1, :( $e2 * $e3 ), :max; log) + :( ∫($e2) - $e1 → max ) => p_bolza!(p, ocp, :( -$e1 ), e2 , :max; log) + :( $e2 * ∫($e3) - $e1 → max ) => has(e2, p.t) ? ( return __throw("time $(p.t) must not appear in $e2", p.lnum, p.line) ) : + p_bolza!(p, ocp, :( -$e1 ), :( $e2 * $e3 ), :max; log) + # mayer cost + :( $e1 → min ) => p_mayer!(p, ocp, e1, :min; log) + :( $e1 → max ) => p_mayer!(p, ocp, e1, :max; log) + # _ => begin if e isa LineNumberNode p.lnum = p.lnum - 1 @@ -114,14 +156,14 @@ parse!(p, ocp, e; log=false) = begin elseif e isa Expr && e.head == :block p.lnum = p.lnum - 1 Expr(:block, map(e -> parse!(p, ocp, e; log), e.args)...) - # !!! assumes that map is done sequentially for side effects on p + # !!! assumes that map is done sequentially for side effects on p else return __throw("unknown syntax", p.lnum, p.line) end end end end -p_variable!(p, ocp, v, q=1; log=false) = begin +p_variable!(p, ocp, v, q=1; components_names=nothing, log=false) = begin log && println("variable: $v, dim: $q") v isa Symbol || return __throw("forbidden variable name: $v", p.lnum, p.line) p.v = v @@ -129,7 +171,13 @@ p_variable!(p, ocp, v, q=1; log=false) = begin qq = q isa Integer ? q : 9 for i ∈ 1:qq p.aliases[Symbol(v, ctindices(i))] = :( $v[$i] ) end for i ∈ 1:9 p.aliases[Symbol(v, ctupperscripts(i))] = :( $v^$i ) end - __wrap(:( variable!($ocp, $q, $vv) ), p.lnum, p.line) + if (isnothing(components_names)) + __wrap(:( variable!($ocp, $q, $vv) ), p.lnum, p.line) + else + for i ∈ 1:qq p.aliases[components_names.args[i]] = :( $v[$i] ) end + ss = QuoteNode(string.(components_names.args)) + __wrap(:( variable!($ocp, $q, $vv, $ss) ), p.lnum, p.line) + end end p_alias!(p, ocp, a, e; log=false) = begin @@ -176,7 +224,7 @@ p_time!(p, ocp, t, t0, tf; log=false) = begin __wrap(code, p.lnum, p.line) end -p_state!(p, ocp, x, n=1; log=false) = begin +p_state!(p, ocp, x, n=1; components_names=nothing, log=false) = begin log && println("state: $x, dim: $n") x isa Symbol || return __throw("forbidden state name: $x", p.lnum, p.line) p.x = x @@ -185,10 +233,16 @@ p_state!(p, ocp, x, n=1; log=false) = begin for i ∈ 1:nn p.aliases[Symbol(x, ctindices(i))] = :( $x[$i] ) end for i ∈ 1:9 p.aliases[Symbol(x, ctupperscripts(i))] = :( $x^$i ) end p.aliases[Symbol(Unicode.normalize(string(x,"̇")))] = :( ∂($x) ) - __wrap(:( state!($ocp, $n, $xx) ), p.lnum, p.line) + if (isnothing(components_names)) + __wrap(:( state!($ocp, $n, $xx) ), p.lnum, p.line) + else + for i ∈ 1:nn p.aliases[components_names.args[i]] = :( $x[$i] ) end + ss = QuoteNode(string.(components_names.args)) + __wrap(:( state!($ocp, $n, $xx, $ss) ), p.lnum, p.line) + end end -p_control!(p, ocp, u, m=1; log=false) = begin +p_control!(p, ocp, u, m=1; components_names=nothing, log=false) = begin log && println("control: $u, dim: $m") u isa Symbol || return __throw("forbidden control name: $u", p.lnum, p.line) p.u = u @@ -196,7 +250,13 @@ p_control!(p, ocp, u, m=1; log=false) = begin mm = m isa Integer ? m : 9 for i ∈ 1:mm p.aliases[Symbol(u, ctindices(i))] = :( $u[$i] ) end for i ∈ 1:9 p.aliases[Symbol(u, ctupperscripts(i))] = :( $u^$i ) end - __wrap(:( control!($ocp, $m, $uu) ), p.lnum, p.line) + if (isnothing(components_names)) + __wrap(:( control!($ocp, $m, $uu) ), p.lnum, p.line) + else + for i ∈ 1:mm p.aliases[components_names.args[i]] = :( $u[$i] ) end + ss = QuoteNode(string.(components_names.args)) + __wrap(:( control!($ocp, $m, $uu, $ss) ), p.lnum, p.line) + end end p_constraint!(p, ocp, e1, e2, e3, label=gensym(); log=false) = begin @@ -298,7 +358,7 @@ p_dynamics!(p, ocp, x, t, e, label=nothing; log=false) = begin end p_lagrange!(p, ocp, e, type; log=false) = begin - log && println("objective: ∫($e) → $type") + log && println("objective (Lagrange): ∫($e) → $type") isnothing(p.x) && return __throw("state not yet declared", p.lnum, p.line) isnothing(p.u) && return __throw("control not yet declared", p.lnum, p.line) isnothing(p.t) && return __throw("time not yet declared", p.lnum, p.line) @@ -318,10 +378,11 @@ p_lagrange!(p, ocp, e, type; log=false) = begin end p_mayer!(p, ocp, e, type; log=false) = begin - log && println("objective: $e → $type") + log && println("objective (Mayer): $e → $type") isnothing(p.x) && return __throw("state not yet declared", p.lnum, p.line) isnothing(p.t0) && return __throw("time not yet declared", p.lnum, p.line) isnothing(p.tf) && return __throw("time not yet declared", p.lnum, p.line) + has(e, :∫) && return __throw("bad objective declaration resulting in a Mayer term with trailing ∫", p.lnum, p.line) gs = gensym() x0 = gensym() xf = gensym() @@ -338,7 +399,7 @@ p_mayer!(p, ocp, e, type; log=false) = begin end p_bolza!(p, ocp, e1, e2, type; log=false) = begin - log && println("objective: $e1 + ∫($e2) → $type") + log && println("objective (Bolza): $e1 + ∫($e2) → $type") isnothing(p.x) && return __throw("state not yet declared", p.lnum, p.line) isnothing(p.t0) && return __throw("time not yet declared", p.lnum, p.line) isnothing(p.tf) && return __throw("time not yet declared", p.lnum, p.line) diff --git a/src/repl.jl b/src/repl.jl index 12203803..c4d16c13 100644 --- a/src/repl.jl +++ b/src/repl.jl @@ -141,6 +141,47 @@ end # utils functions # ---------------------------------------------------------------- +function NAME_ACTION_FUNCTION(ct_repl_data::CTRepl, history::HistoryRepl) + println("") + println("Optimal control problem name: ", ct_repl_data.ocp_name) + println("Solution name: ", ct_repl_data.sol_name) +end + +function NAME_ACTION_FUNCTION(ct_repl_data::CTRepl, name::Union{Symbol, Expr}, history::HistoryRepl) + ocp_name = ct_repl_data.ocp_name + sol_name = ct_repl_data.sol_name + if isa(name, Symbol) + name = (name, Symbol(string(name, "_sol"))) + elseif isa(name, Expr) + name = (name.args[1], name.args[2]) + else + println("\nname error\n\nType HELP to see the list of commands or enter a valid expression to update the model.") + return nothing + end + ct_repl_data.ocp_name = name[1] + ct_repl_data.sol_name = name[2] + ct_repl_data.debug && println("debug> ocp name: ", ct_repl_data.ocp_name) + ct_repl_data.debug && println("debug> sol name: ", ct_repl_data.sol_name) + __add!(history, ct_repl_data) # update history + qo1 = ct_repl_data.ocp_name ≠ ocp_name ? :($(ct_repl_data.ocp_name) = "no optimal control") : :() + qs1 = ct_repl_data.sol_name ≠ sol_name ? :($(ct_repl_data.sol_name) = "no solution") : :() + qo2 = ct_repl_data.ocp_name ≠ ocp_name ? :($(ct_repl_data.ocp_name) = $(ocp_name)) : :() + qs2 = ct_repl_data.sol_name ≠ sol_name ? :($(ct_repl_data.sol_name) = $(sol_name)) : :() + name_q = (quote + $(qo1) + $(qs1) + try + $(qo2) + $(qs2) + nothing + catch e + nothing + end + end) + ct_repl_data.debug && println("debug> new name quote: ", name_q) + return name_q +end + # dict of actions associated to ct repl commands COMMANDS_ACTIONS = Dict{Symbol, Function}( :SHOW => (ct_repl_data::CTRepl, history::HistoryRepl) -> begin @@ -158,40 +199,7 @@ COMMANDS_ACTIONS = Dict{Symbol, Function}( __add!(history, ct_repl_data) # update history return nothing end, - :NAME => (ct_repl_data::CTRepl, name::Union{Symbol, Expr}, history::HistoryRepl) -> begin - ocp_name = ct_repl_data.ocp_name - sol_name = ct_repl_data.sol_name - if isa(name, Symbol) - name = (name, Symbol(string(name, "_sol"))) - elseif isa(name, Expr) - name = (name.args[1], name.args[2]) - else - println("\nname error\n\nType HELP to see the list of commands or enter a valid expression to update the model.") - return nothing - end - ct_repl_data.ocp_name = name[1] - ct_repl_data.sol_name = name[2] - ct_repl_data.debug && println("debug> ocp name: ", ct_repl_data.ocp_name) - ct_repl_data.debug && println("debug> sol name: ", ct_repl_data.sol_name) - __add!(history, ct_repl_data) # update history - qo1 = ct_repl_data.ocp_name ≠ ocp_name ? :($(ct_repl_data.ocp_name) = "no optimal control") : :() - qs1 = ct_repl_data.sol_name ≠ sol_name ? :($(ct_repl_data.sol_name) = "no solution") : :() - qo2 = ct_repl_data.ocp_name ≠ ocp_name ? :($(ct_repl_data.ocp_name) = $(ocp_name)) : :() - qs2 = ct_repl_data.sol_name ≠ sol_name ? :($(ct_repl_data.sol_name) = $(sol_name)) : :() - name_q = (quote - $(qo1) - $(qs1) - try - $(qo2) - $(qs2) - nothing - catch e - nothing - end - end) - ct_repl_data.debug && println("debug> new name quote: ", name_q) - return name_q - end, + :NAME => NAME_ACTION_FUNCTION, :UNDO => (ct_repl_data::CTRepl, history::HistoryRepl) -> begin ct_repl_data_ = __undo!(history) __copy!(ct_repl_data, ct_repl_data_) diff --git a/test/test_goddard.jl b/test/test_goddard.jl index 1955b3a7..821fdf77 100644 --- a/test/test_goddard.jl +++ b/test/test_goddard.jl @@ -16,7 +16,7 @@ x0 = [ r0, v0, m0 ] # Abstract model @def ocp begin - tf, variable + tf ∈ R, variable t ∈ [ t0, tf ], time x ∈ R³, state u ∈ R, control diff --git a/test/test_model.jl b/test/test_model.jl index 0c491d87..18040be2 100644 --- a/test/test_model.jl +++ b/test/test_model.jl @@ -34,6 +34,11 @@ function test_model() # 30 55 185 @test ocp.variable_dimension == 2 @test ocp.variable_components_names == [ "vv₁", "vv₂" ] + ocp = Model(variable=true) + variable!(ocp, 2, "uu", [ "vv₁", "vv₂" ]) + @test ocp.variable_dimension == 2 + @test ocp.variable_components_names == [ "vv₁", "vv₂" ] + ocp = Model(variable=true) @test_throws MethodError variable!(ocp, 2, [ "vv1", "vv2" ]) @@ -42,7 +47,6 @@ function test_model() # 30 55 185 @test ocp.variable_dimension == 2 @test ocp.variable_components_names == [ "vv₁", "vv₂" ] - end @testset "time, state and control set or not" begin @@ -344,6 +348,11 @@ end state!(ocp, 2, "y") @test ocp.state_dimension == 2 @test ocp.state_components_names == ["y₁", "y₂"] + + ocp = Model() + state!(ocp, 2, "y", ["z₁", "z₂"]) + @test ocp.state_dimension == 2 + @test ocp.state_components_names == ["z₁", "z₂"] end @testset "control!" begin @@ -374,6 +383,11 @@ end control!(ocp, 2, "v") @test ocp.control_dimension == 2 @test ocp.control_components_names == ["v₁", "v₂"] + + ocp = Model() + control!(ocp, 2, "u", ["v₁", "v₂"]) + @test ocp.control_dimension == 2 + @test ocp.control_components_names == ["v₁", "v₂"] end @testset "time!" begin diff --git a/test/test_onepass.jl b/test/test_onepass.jl index 8e4948be..8bf4bb81 100644 --- a/test/test_onepass.jl +++ b/test/test_onepass.jl @@ -2,887 +2,248 @@ function test_onepass() -t0 = 0 -@def o t ∈ [ t0, t0 + 4 ], time -@test o.initial_time == t0 -@test o.final_time == t0 + 4 - -@def o begin - λ ∈ R^2, variable - tf = λ₂ - t ∈ [ 0, tf ], time -end -@test o.initial_time == 0 -@test o.final_time == Index(2) - -@def o begin - t0 ∈ R, variable - t ∈ [ t0, 1 ], time -end -@test o.initial_time == Index(1) -@test o.final_time == 1 - -@def o begin - tf ∈ R, variable - t ∈ [ 0, tf ], time -end -@test o.initial_time == 0 -@test o.final_time == Index(1) +# --------------------------------------------------------------- +# --------------------------------------------------------------- +@testset "aliases" begin + + @def o begin + x = (y, z) ∈ R², state + u = (uu1, uu2, uu3) ∈ R³, control + v = (vv1, vv2) ∈ R², variable + end + @test o.state_components_names == [ "y", "z" ] + @test o.control_components_names == [ "uu1", "uu2", "uu3" ] + @test o.variable_components_names == [ "vv1", "vv2" ] + + @def o begin + x = [y, z] ∈ R², state + u = [uu1, uu2, uu3] ∈ R³, control + v = [vv1, vv2] ∈ R², variable + end + @test o.state_components_names == [ "y", "z" ] + @test o.control_components_names == [ "uu1", "uu2", "uu3" ] + @test o.variable_components_names == [ "vv1", "vv2" ] -@def o begin - v ∈ R², variable - s ∈ [ v[1], v[2] ], time -end -@test o.initial_time == Index(1) -@test o.final_time == Index(2) - -@def o begin - v ∈ R², variable - s0 = v₁ - sf = v₂ - s ∈ [ s0, sf ], time -end -@test o.initial_time == Index(1) -@test o.final_time == Index(2) + @test_throws ParsingError @def o begin # a name must be provided + (y, z) ∈ R², state + end -@test_throws IncorrectArgument @def o begin - t0 ∈ R², variable - t ∈ [ t0, 1 ], time -end + @test_throws ParsingError @def o begin # a name must be provided + (uu1, uu2, uu3) ∈ R³, control + end -@test_throws IncorrectArgument @def o begin - tf ∈ R², variable - t ∈ [ 0, tf ], time -end + @test_throws ParsingError @def o begin # a name must be provided + (vv1, vv2) ∈ R², variable + end -@test_throws ParsingError @def o begin - v, variable - t ∈ [ 0, tf[v] ], time -end + @test_throws ParsingError @def o begin # a name must be provided + [y, z] ∈ R², state + end -@test_throws ParsingError @def o begin - v, variable - t ∈ [ t0[v], 1 ], time -end + @test_throws ParsingError @def o begin # a name must be provided + [uu1, uu2, uu3] ∈ R³, control + end -@test_throws ParsingError @def o begin - v, variable - t ∈ [ t0[v], tf[v+1] ], time -end + @test_throws ParsingError @def o begin # a name must be provided + [vv1, vv2] ∈ R², variable + end -@def o begin - x ∈ R, state - u ∈ R, control -end -@test o.state_dimension == 1 -@test o.control_dimension == 1 - -@def o begin - t ∈ [ 0, 1 ], time - x ∈ R^3, state - u ∈ R^2, control - ẋ(t) == [ x[1](t) + 2u[2](t), 2x[3](t), x[1](t) + u[2](t) ] -end -@test o.state_dimension == 3 -@test o.control_dimension == 2 -x = [ 1, 2, 3 ] -u = [ -1, 2 ] -@test o.dynamics(x, u) == [ x[1] + 2u[2], 2x[3], x[1] + u[2] ] - -t0 = 0 -tf = 1 -@def o begin - t ∈ [ t0, tf ], time - x ∈ R^2, state - u ∈ R, control - x(t0) == [ -1, 0 ], (1) - x(tf) == [ 0, 0 ] - ẋ(t) == A * x(t) + B * u(t) - ∫( 0.5u(t)^2 ) → min -end -x = [ 1, 2 ] -x0 = 2 * x -xf = 3 * x -u = -1 -A = [ 0 1 - 0 0 ] -B = [ 0 - 1 ] -@test constraint(o, :eq1)(x0, xf) == x0 -@test o.dynamics(x, u) == A * x + B * u -@test o.lagrange(x, u) == 0.5u^2 -@test o.criterion == :min - -a = 1 -f(b) = begin # closure of a, local c, and @def in function - c = 3 - @def ocp begin - t ∈ [ a, b ], time - x ∈ R, state + @def o begin + t ∈ [ 0, 1 ], time + x = ( r, v ) ∈ R², state u ∈ R, control - ẋ(t) == x(t) + u(t) + b + c + d -end - ocp -end -o = f(2) -d = 4 -x = 10 -u = 20 -@test o.dynamics(x, u) == x + u + 2 + 3 + 4 - -@def o begin - t ∈ [ 0, 1 ], time - x ∈ R², state - u ∈ R, control - begin - r = x₁ - v = x₂ w = r + 2v r(0) == 0, (1) + v(0) == 1, (♡) + ẋ(t) == [ v(t), w(t)^2 ] + ∫( u(t)^2 + x₁(t) ) → min + end + x = [ 1, 2 ] + x0 = 2 * x + xf = 3 * x + u = 3 + @test constraint(o, :eq1)(x0, xf) == x0[1] + @test constraint(o, Symbol("♡"))(x0, xf) == x0[2] + @test o.dynamics(x, u) == [ x[2], (x[1] + 2x[2])^2 ] + @test o.lagrange(x, u) == u^2 + x[1] + + @def o begin + t ∈ [ 0, 1 ], time + x = [ r, v ] ∈ R², state + u ∈ R, control + w = r + 2v + r(0) == 0, (1) + v(0) == 1, (♡) + ẋ(t) == [ v(t), w(t)^2 ] + ∫( u(t)^2 + x₁(t) ) → min + end + x = [ 1, 2 ] + x0 = 2 * x + xf = 3 * x + u = 3 + @test constraint(o, :eq1)(x0, xf) == x0[1] + @test constraint(o, Symbol("♡"))(x0, xf) == x0[2] + @test o.dynamics(x, u) == [ x[2], (x[1] + 2x[2])^2 ] + @test o.lagrange(x, u) == u^2 + x[1] + + @def o begin + t ∈ [ 0, 1 ], time + x = [ r, v ] ∈ R², state + c = [ u, b ] ∈ R², control + w = r + 2v + b(t) == 0 + r(0) == 0, (1) + v(0) == 1, (♡) + ẋ(t) == [ v(t), w(t)^2 ] + ∫( u(t)^2 + b(t)^2 + x₁(t) ) → min + end + x = [ 1, 2 ] + x0 = 2 * x + xf = 3 * x + u = 3 + c = [ u, 0 ] + @test constraint(o, :eq1)(x0, xf) == x0[1] + @test constraint(o, Symbol("♡"))(x0, xf) == x0[2] + @test o.dynamics(x, c) == [ x[2], (x[1] + 2x[2])^2 ] + @test o.lagrange(x, c) == u^2 + x[1] + + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R^3, state + u = (u₁, v) ∈ R^2, control + ẋ(t) == [ x[1](t) + 2v(t), 2x[3](t), x[1](t) + v(t) ] + end + @test o.state_dimension == 3 + @test o.control_dimension == 2 + x = [ 1, 2, 3 ] + u = [ -1, 2 ] + @test o.dynamics(x, u) == [ x[1] + 2u[2], 2x[3], x[1] + u[2] ] + + t0 = .0; tf = .1 + @def ocp begin + t ∈ [ t0, tf ], time + x ∈ R^3, state + u ∈ R^3, control + r = x[1] + v = x₂ + a = x₃ + end ; + @test ocp isa OptimalControlModel + @test ocp.time_name == "t" + @test ocp.initial_time == t0 + @test ocp.final_time == tf + @test ocp.control_name == "u" + @test ocp.control_dimension == 3 + @test ocp.state_name == "x" + @test ocp.state_dimension == 3 + end - v(0) == 1, (♡) - ẋ(t) == [ v(t), w(t)^2 ] - ∫( u(t)^2 + x₁(t) ) → min -end -x = [ 1, 2 ] -x0 = 2 * x -xf = 3 * x -u = 3 -@test constraint(o, :eq1)(x0, xf) == x0[1] -@test constraint(o, Symbol("♡"))(x0, xf) == x0[2] -@test o.dynamics(x, u) == [ x[2], (x[1] + 2x[2])^2 ] -@test o.lagrange(x, u) == u^2 + x[1] - -@def o begin - t ∈ [ 0, 1 ], time - x ∈ R², state - u ∈ R, control - r = x₁ - v = x₂ - w = r + 2v - r(0) == 0, (1) - v(0) == 1, (♡) - ẋ(t) == [ v(t), w(t)^2 ] - ∫( u(t)^2 + x₁(t) ) → min -end -x = [ 1, 2 ] -x0 = 2 * x -xf = 3 * x -u = 3 -@test constraint(o, :eq1)(x0, xf) == x0[1] -@test constraint(o, Symbol("♡"))(x0, xf) == x0[2] -@test o.dynamics(x, u) == [ x[2], (x[1] + 2x[2])^2 ] -@test o.lagrange(x, u) == u^2 + x[1] - -@def o begin - z ∈ R², variable - t ∈ [ 0, 1 ], time - x ∈ R², state - u ∈ R, control - r = x₁ - v = x₂ - w = r + 2v - r(0) == 0, (1) - v(0) == 1, (♡) - ẋ(t) == [ v(t), w(t)^2 + z₁ ] - ∫( u(t)^2 + z₂ * x₁(t) ) → min -end -x = [ 1, 2 ] -x0 = 2 * [ 1, 2 ] -xf = 3 * [ 1, 2 ] -u = 3 -z = [ 4, 5 ] -@test constraint(o, :eq1)(x0, xf, z) == x0[1] -@test constraint(o, Symbol("♡"))(x0, xf, z) == x0[2] -@test o.dynamics(x, u, z) == [ x[2], (x[1] + 2x[2])^2 + z[1] ] -@test o.lagrange(x, u, z) == u^2 + z[2] * x[1] - -@def o begin - tf, variable - t ∈ [ 0, tf ], time - x ∈ R², state - u ∈ R, control - r = x₁ - v = x₂ - w = r¹ + 2v³ - r(0) + w(tf) - tf² == 0, (1) -end -tf = 2 -x0 = [ 1, 2 ] -xf = [ 3, 4 ] -@test constraint(o, :eq1)(x0, xf, tf) == x0[1] + ( xf[1] + 2xf[2]^3 ) - tf^2 - -@def o begin - t ∈ [ 0, 1 ], time - x ∈ R², state - u ∈ R, control - r = x₁ - v = x₂ - r(0)^2 + v(1) == 0, (1) - v(0) == 1, (♡) - ẋ(t) == [ v(t), r(t)^2 ] - ∫( u(t)^2 + x₁(t) ) → min -end -x0 = [ 2, 3 ] -xf = [ 4, 5 ] -x = [ 1, 2 ] -u = 3 -@test constraint(o, :eq1)(x0, xf) == x0[1]^2 + xf[2] -@test constraint(o, Symbol("♡"))(x0, xf) == x0[2] -@test o.dynamics(x, u) == [ x[2], x[1]^2 ] -@test o.lagrange(x, u) == u^2 + x[1] - -@def o begin - z ∈ R, variable - t ∈ [ 0, 1 ], time - x ∈ R², state - u ∈ R, control - r = x₁ - v = x₂ - r(0) - z == 0, (1) - v(0) == 1, (♡) - ẋ(t) == [ v(t), r(t)^2 + z ] - ∫( u(t)^2 + z * x₁(t) ) → min -end -x0 = [ 2, 3 ] -xf = [ 4, 5 ] -x = [ 1, 2 ] -u = 3 -z = 4 -@test constraint(o, :eq1)(x0, xf, z) == x0[1] - z -@test constraint(o, Symbol("♡"))(x0, xf, z) == x0[2] -@test o.dynamics(x, u, z) == [ x[2], x[1]^2 + z ] -@test o.lagrange(x, u, z) == u^2 + z * x[1] - -@def o begin - z ∈ R, variable - t ∈ [ 0, 1 ], time - x ∈ R², state - u ∈ R, control - r = x₁ - v = x₂ - 0 ≤ r(0) - z ≤ 1, (1) - 0 ≤ v(1)^2 ≤ 1, (2) - [ 0, 0 ] ≤ x(0) ≤ [ 1, 1 ], (♡) - ẋ(t) == [ v(t), r(t)^2 + z ] - ∫( u(t)^2 + z * x₁(t) ) → min -end -x0 = [ 2, 3 ] -xf = [ 4, 5 ] -x = [ 1, 2 ] -u = 3 -z = 4 -@test constraint(o, :eq1)(x0, xf, z) == x0[1] - z -@test constraint(o, :eq2)(x0, xf, z) == xf[2]^2 -@test constraint(o, Symbol("♡"))(x0, xf, z) == x0 -@test o.dynamics(x, u, z) == [ x[2], x[1]^2 + z ] -@test o.lagrange(x, u, z) == u^2 + z * x[1] - -@def o begin - z ∈ R, variable - t ∈ [ 0, 1 ], time - x ∈ R², state - u ∈ R, control - r = x₁ - v = x₂ - 1 ≥ r(0) - z ≥ 0, (1) - 1 ≥ v(1)^2 ≥ 0, (2) - [ 1, 1 ] ≥ x(0) ≥ [ 0, 0 ], (3) - ẋ(t) == [ v(t), r(t)^2 + z ] - ∫( u(t)^2 + z * x₁(t) ) → min -end -x0 = [ 2, 3 ] -xf = [ 4, 5 ] -x = [ 1, 2 ] -u = 3 -z = 4 -@test constraint(o, :eq1)(x0, xf, z) == x0[1] - z -@test constraint(o, :eq2)(x0, xf, z) == xf[2]^2 -@test constraint(o, :eq3)(x0, xf, z) == x0 -@test o.dynamics(x, u, z) == [ x[2], x[1]^2 + z ] -@test o.lagrange(x, u, z) == u^2 + z * x[1] -@test o.constraints[:eq1][3] == 0 -@test o.constraints[:eq1][4] == 1 -@test o.constraints[:eq2][3] == 0 -@test o.constraints[:eq2][4] == 1 -@test o.constraints[:eq3][3] == [ 0, 0 ] -@test o.constraints[:eq3][4] == [ 1, 1 ] - -n = 11 -m = 6 -@def o begin - t ∈ [ 0, 1 ], time - x ∈ R^n, state - u ∈ R^m, control - r = x₁ - v = x₂ - 0 ≤ r(t) ≤ 1, (1) - zeros(n) ≤ x(t) ≤ ones(n), (2) - [ 0, 0 ] ≤ x[1:2](t) ≤ [ 1, 1 ], (3) - [ 0, 0 ] ≤ x[1:2:4](t) ≤ [ 1, 1 ], (4) - 0 ≤ v(t)^2 ≤ 1, (5) - zeros(m) ≤ u(t) ≤ ones(m), (6) - [ 0, 0 ] ≤ u[1:2](t) ≤ [ 1, 1 ], (7) - [ 0, 0 ] ≤ u[1:2:4](t) ≤ [ 1, 1 ], (8) - 0 ≤ u₂(t)^2 ≤ 1, (9) - u₁(t) * x[1:2](t) == 1, (10) - 0 ≤ u₁(t) * x[1:2](t).^3 ≤ 1, (11) -end -x = Vector{Float64}(1:n) -u = 2 * Vector{Float64}(1:m) -@test constraint(o, :eq1 )(x) == x[1] -@test constraint(o, :eq2 )(x) == x -@test constraint(o, :eq3 )(x) == x[1:2] -@test constraint(o, :eq4 )(x) == x[1:2:4] -@test constraint(o, :eq5 )(x) == x[2]^2 -@test constraint(o, :eq6 )(u) == u -@test constraint(o, :eq7 )(u) == u[1:2] -@test constraint(o, :eq8 )(u) == u[1:2:4] -@test constraint(o, :eq9 )(u) == u[2]^2 -@test constraint(o, :eq10)(x, u) == u[1] * x[1:2] -@test constraint(o, :eq11)(x, u) == u[1] * x[1:2].^3 - -n = 11 -m = 6 -@def o begin - z ∈ R^2, variable - t ∈ [ 0, 1 ], time - x ∈ R^n, state - u ∈ R^m, control - r = x₁ - v = x₂ - 0 ≤ r(t) ≤ 1, (1) - zeros(n) ≤ x(t) ≤ ones(n), (2) - [ 0, 0 ] ≤ x[1:2](t) - [ z₁, 1 ] ≤ [ 1, 1 ], (3) - [ 0, 0 ] ≤ x[1:2:4](t) ≤ [ 1, 1 ], (4) - 0 ≤ v(t)^2 ≤ 1, (5) - zeros(m) ≤ u(t) ≤ ones(m), (6) - [ 0, 0 ] ≤ u[1:2](t) ≤ [ 1, 1 ], (7) - [ 0, 0 ] ≤ u[1:2:4](t) ≤ [ 1, 1 ], (8) - 0 ≤ u₂(t)^2 ≤ 1, (9) - u₁(t) * x[1:2](t) + z + f() == 1, (10) - 0 ≤ u₁(t) * x[1:2](t).^3 + z ≤ 1, (11) -end -f() = [ 1, 1 ] -z = 3 * Vector{Float64}(1:2) -x = Vector{Float64}(1:n) -u = 2 * Vector{Float64}(1:m) -@test constraint(o, :eq1 )(x, z) == x[1] -@test constraint(o, :eq2 )(x, z) == x -@test constraint(o, :eq3 )(x, z) == x[1:2] - [ z[1], 1 ] -@test constraint(o, :eq4 )(x, z) == x[1:2:4] -@test constraint(o, :eq5 )(x, z) == x[2]^2 -@test constraint(o, :eq6 )(u, z) == u -@test constraint(o, :eq7 )(u, z) == u[1:2] -@test constraint(o, :eq8 )(u, z) == u[1:2:4] -@test constraint(o, :eq9 )(u, z) == u[2]^2 -@test constraint(o, :eq10)(x, u, z) == u[1] * x[1:2] + z + f() -@test constraint(o, :eq11)(x, u, z) == u[1] * x[1:2].^3 + z - -@def o begin - s ∈ [ 0, 1 ], time - y ∈ R^4, state - w ∈ R, control - r = y₃ - v = y₄ - r(0) + v(1) → min -end -y0 = [ 1, 2, 3, 4 ] -yf = 2 * [ 1, 2, 3, 4 ] -@test is_min(o) -@test o.mayer(y0, yf) == y0[3] + yf[4] - - -@def o begin - s ∈ [ 0, 1 ], time - y ∈ R^4, state - w ∈ R, control - r = y₃ - v = y₄ - r(0) + v(1) → max -end -y0 = [ 1, 2, 3, 4 ] -yf = 2 * [ 1, 2, 3, 4 ] -@test is_max(o) -@test o.mayer(y0, yf) == y0[3] + yf[4] - -@def o begin - z ∈ R^2, variable - s ∈ [ 0, z₁ ], time - y ∈ R^4, state - w ∈ R, control - r = y₃ - v = y₄ - r(0) + v(z₁) + z₂ → min -end -z = [ 5, 6 ] -y0 = [ 1, 2, 3, 4 ] -yf = 2 * [ 1, 2, 3, 4 ] -@test is_min(o) -@test o.mayer(y0, yf, z) == y0[3] + yf[4] + z[2] - -@def o begin - z ∈ R², variable - s ∈ [ 0, z₁ ], time - y ∈ R⁴, state - w ∈ R, control - r = y₃ - v = y₄ - aa = y₁ + w² + v³ + z₂ - ẏ(s) == [ aa(s), r²(s), 0, 0 ] - r(0) + v(z₁) + z₂ → min -end -z = [ 5, 6 ] -y = [ 1, 2, 3, 4 ] -y0 = y -yf = 3y0 -w = 7 -@test o.dynamics(y, w, z) == [ y[1] + w^2 + y[4]^3 + z[2], y[3]^2, 0, 0 ] -@test o.mayer(y0, yf, z) == y0[3] + yf[4] + z[2] - -@def o begin - z ∈ R², variable - s ∈ [ 0, z₁ ], time - y ∈ R⁴, state - w ∈ R, control - r = y₃ - v = y₄ - aa = y₁(s) + v³ + z₂ - ẏ(s) == [ aa(s) + (w^2)(s), r²(s), 0, 0 ] - r(0) + v(z₁) + z₂ → min -end -z = [ 5, 6 ] -y = [ 1, 2, 3, 4 ] -y0 = y -yf = 3y0 -w = 7 -@test o.dynamics(y, w, z) == [ y[1] + w^2 + y[4]^3 + z[2], y[3]^2, 0, 0 ] -@test o.mayer(y0, yf, z) == y0[3] + yf[4] + z[2] - -@def o begin - z ∈ R², variable - s ∈ [ 0, z₁ ], time - y ∈ R⁴, state - w ∈ R, control - r = y₃ - v = y₄ - aa = y₁ - ẏ(s) == [ aa(s), r²(s) + w(s) + z₁, 0, 0 ] -end -z = [ 5, 6 ] -y = [ 1, 2, 3, 4 ] -w = 9 -@test o.dynamics(y, w, z) == [ y[1], y[3]^2 + w + z[1], 0, 0 ] - -@def o begin - z ∈ R², variable - __s ∈ [ 0, z₁ ], time - y ∈ R⁴, state - w ∈ R, control - r = y₃ - v = y₄ - aa = y₁(__s) - ẏ(__s) == [ aa(__s), r²(__s) + w(__s) + z₁, 0, 0 ] -end -z = [ 5, 6 ] -y = [ 1, 2, 3, 4 ] -w = 9 -@test_throws MethodError o.dynamics(y, w, z) - -@def o begin - z ∈ R², variable - s ∈ [ 0, z₁ ], time - y ∈ R⁴, state - w ∈ R, control - r = y₃ - v = y₄ - aa = y₁(s) + v³ + z₂ - ẏ(s) == [ aa(s) + w(s)^2, r²(s), 0, 0 ] -end -z = [ 5, 6 ] -y = [ 1, 2, 3, 4 ] -y0 = y -yf = 3y0 -ww = 19 -@test o.dynamics(y, ww, z) == [ y[1] + ww^2 + y[4]^3 + z[2], y[3]^2, 0, 0 ] - -@def o begin - z ∈ R², variable - s ∈ [ 0, z₁ ], time - y ∈ R⁴, state - w, control - r = y₃ - v = y₄ - aa = y₁ + v³ + z₂ - aa(0) + y₂(z₁) → min -end -z = [ 5, 6 ] -y0 = y -yf = 3y0 -@test o.mayer(y0, yf, z) == y0[1] + y0[4]^3 + z[2] + yf[2] - -@def o begin - z ∈ R², variable - __t ∈ [ 0, z₁ ], time - y ∈ R⁴, state - w, control - r = y₃ - v = y₄ - aa = y₁(__t) + v³ + z₂ - ẏ(__t) == [ aa(__t) + (w^2)(__t), r²(__t), 0, 0 ] - aa(0) + y₂(z₁) → min -end -z = [ 5, 6 ] -y = [ 1, 2, 3, 4 ] -y0 = y -yf = 3y0 -w = 11 -@test o.dynamics(y, w, z) == [ y[1] + w^2 + y[4]^3 + z[2], y[3]^2, 0, 0 ] -@test_throws UndefVarError o.mayer(y0, yf, z) - -@def o begin - z ∈ R², variable - __t ∈ [ 0, z₁ ], time - y ∈ R⁴, state - w, control - r = y₃ - v = y₄ - aa = y₁(0) + v³ + z₂ - ẏ(__t) == [ aa(__t) + (w^2)(__t), r²(__t), 0, 0 ] - aa(0) + y₂(z₁) → min -end -z = [ 5, 6 ] -y = [ 1, 2, 3, 4 ] -y0 = y -yf = 3y0 -w = 11 -@test_throws MethodError o.dynamics(y, w, z) -@test o.mayer(y0, yf, z) == y0[1] + y0[4]^3 + z[2] + yf[2] - -@def o begin - t ∈ [ 0, 1 ], time - x, state - u, control - (x(0) + 2x(1)) + ∫(x(t) + u(t)) → min -end -x = 1 -u = 2 -x0 = 3 -xf = 4 -@test o.mayer(x0, xf) == x0 + 2xf -@test o.lagrange(x, u) == x + u -@test o.criterion == :min - -@def o begin - t ∈ [ 0, 1 ], time - x, state - u, control - (x(0) + 2x(1)) - ∫(x(t) + u(t)) → min -end -x = 1 -u = 2 -x0 = 3 -xf = 4 -@test o.mayer(x0, xf) == x0 + 2xf -@test o.lagrange(x, u) == -(x + u) -@test o.criterion == :min - -@def o begin - t ∈ [ 0, 1 ], time - x, state - u, control - (x(0) + 2x(1)) + ∫(x(t) + u(t)) → max -end -x = 1 -u = 2 -x0 = 3 -xf = 4 -@test o.mayer(x0, xf) == x0 + 2xf -@test o.lagrange(x, u) == x + u -@test o.criterion == :max - -@def o begin - t ∈ [ 0, 1 ], time - x, state - u, control - (x(0) + 2x(1)) - ∫(x(t) + u(t)) → max -end -x = 1 -u = 2 -x0 = 3 -xf = 4 -@test o.mayer(x0, xf) == x0 + 2xf -@test o.lagrange(x, u) == -(x + u) -@test o.criterion == :max - -@def o begin - t ∈ [ 0, 1 ], time - x, state - u, control - ∫(x(t) + u(t)) + (x(0) + 2x(1)) → min -end -x = 1 -u = 2 -x0 = 3 -xf = 4 -@test o.mayer(x0, xf) == x0 + 2xf -@test o.lagrange(x, u) == x + u -@test o.criterion == :min - -@def o begin - t ∈ [ 0, 1 ], time - x, state - u, control - ∫(x(t) + u(t)) - (x(0) + 2x(1)) → min -end -x = 1 -u = 2 -x0 = 3 -xf = 4 -@test o.mayer(x0, xf) == -(x0 + 2xf) -@test o.lagrange(x, u) == x + u -@test o.criterion == :min - -@def o begin - t ∈ [ 0, 1 ], time - x, state - u, control - ∫(x(t) + u(t)) + (x(0) + 2x(1)) → max -end -x = 1 -u = 2 -x0 = 3 -xf = 4 -@test o.mayer(x0, xf) == x0 + 2xf -@test o.lagrange(x, u) == x + u -@test o.criterion == :max - -@def o begin - t ∈ [ 0, 1 ], time - x, state - u, control - ∫(x(t) + u(t)) - (x(0) + 2x(1)) → max -end -x = 1 -u = 2 -x0 = 3 -xf = 4 -@test o.mayer(x0, xf) == -(x0 + 2xf) -@test o.lagrange(x, u) == x + u -@test o.criterion == :max - -@def o begin - t ∈ [ 0, 1 ], time - x, state - u, control - x(0) + 2x(1) + ∫(x(t) + u(t)) → max -end -x = 1 -u = 2 -x0 = 3 -xf = 4 -@test_throws UndefVarError o.mayer(x0, xf) - -@def o begin - t ∈ [ 0, 1 ], time - x, state - u, control - ∫(x(t) + u(t)) - x(0) + 2x(1) → max -end -x = 1 -u = 2 -x0 = 3 -xf = 4 -@test_throws UndefVarError o.mayer(x0, xf) - -@def o begin - v ∈ R², variable - t ∈ [ 0, 1 ], time - x ∈ R, state - u ∈ R, control - x(0) - v₁ == 0, (1) - x(1) - v₁ == 0, (2) - 0 ≤ x(0) - v₁ ≤ 1, (3) - 0 ≤ x(1) - v₁ ≤ 1, (4) - x(0) + x(1) - v₂ == 0, (5) - 0 ≤ x(0) + x(1) - v₂ ≤ 1, (6) - x(t) - v₁ == 0, (7) - u(t) - v₁ == 0, (8) - z = v₁ + 2v₂ - 0 ≤ x(t) - z ≤ 1, (9) - 0 ≤ u(t) - z ≤ 1, (10) - 0 ≤ x(t) + u(t) - z ≤ 1, (11) - ẋ(t) == z * x(t) + 2u(t) - v₁ == 1, (12) - 0 ≤ v₁ ≤ 1, (13) - z == 1, (14) - 0 ≤ z ≤ 1, (15) - z * x(1) → min -end -x = 1 -x0 = 2 -xf = 3 -u = 4 -v = [ 5, 6 ] -z = v[1] + 2v[2] -@test constraint(o, :eq1)(x0, xf, v) == x0 - v[1] -@test constraint(o, :eq2)(x0, xf, v) == xf - v[1] -@test constraint(o, :eq3)(x0, xf, v) == x0 - v[1] -@test constraint(o, :eq4)(x0, xf, v) == xf - v[1] -@test constraint(o, :eq5)(x0, xf, v) == x0 + xf - v[2] -@test constraint(o, :eq6)(x0, xf, v) == x0 + xf - v[2] -@test constraint(o, :eq7)(x, v) == x - v[1] -@test constraint(o, :eq9)(x, v) == x - z -@test constraint(o, :eq10)(u, v) == u - z -@test constraint(o, :eq11)(x, u, v) == x + u - z -@test constraint(o, :eq12)(v) == v[1] -@test constraint(o, :eq13)(v) == v[1] -@test constraint(o, :eq14)(v) == v[1] + 2v[2] -@test constraint(o, :eq15)(v) == v[1] + 2v[2] - -@def o begin - v ∈ R, variable - t ∈ [ 0, 1 ], time - x ∈ R, state - u ∈ R², control - x(0) ≤ 0 - x(0) ≤ 0, (1) - x(1) ≤ 0 - x(1) ≤ 0, (2) - x³(0) ≤ 0 - x³(0) ≤ 0,   (3) - x³(1) ≤ 0 - x³(1) ≤ 0,   (4) - x(t) ≤ 0 - x(t) ≤ 0, (5) - x(t) ≤ 0 - x(t) ≤ 0, (6) - u₁(t) ≤ 0 - u₁(t) ≤ 0, (7) - u₁(t) ≤ 0 - u₁(t) ≤ 0, (8) - x³(t) ≤ 0 - x³(t) ≤ 0,   (9) - x³(t) ≤ 0 - x³(t) ≤ 0,   (10) - (u₁^3)(t) ≤ 0 - (u₁^3)(t) ≤ 0, (11) - (u₁^3)(t) ≤ 0 - (u₁^3)(t) ≤ 0,   (12) - x(t) + (u₁^3)(t) ≤ 0 - x(t) + (u₁^3)(t) ≤ 0, (13) - x(t) + (u₁^3)(t) ≤ 0 - x(t) + (u₁^3)(t) ≤ 0,   (14) - v ≤ 0 - v ≤ 0, (15) -end -@test o.constraints[:eq1 ][3] == -Inf -@test o.constraints[:eq2 ][3] == -Inf -@test o.constraints[:eq3 ][3] == -Inf -@test o.constraints[:eq4 ][3] == -Inf -@test o.constraints[:eq5 ][3] == -Inf -@test o.constraints[:eq6 ][3] == -Inf -@test o.constraints[:eq7 ][3] == -Inf -@test o.constraints[:eq8 ][3] == -Inf -@test o.constraints[:eq9 ][3] == -Inf -@test o.constraints[:eq10][3] == -Inf -@test o.constraints[:eq11][3] == -Inf -@test o.constraints[:eq12][3] == -Inf -@test o.constraints[:eq13][3] == -Inf -@test o.constraints[:eq14][3] == -Inf -@test o.constraints[:eq15][3] == -Inf -@test o.constraints[:eq1 ][4] == 0 -@test o.constraints[:eq2 ][4] == 0 -@test o.constraints[:eq3 ][4] == 0 -@test o.constraints[:eq4 ][4] == 0 -@test o.constraints[:eq5 ][4] == 0 -@test o.constraints[:eq6 ][4] == 0 -@test o.constraints[:eq7 ][4] == 0 -@test o.constraints[:eq8 ][4] == 0 -@test o.constraints[:eq9 ][4] == 0 -@test o.constraints[:eq10][4] == 0 -@test o.constraints[:eq11][4] == 0 -@test o.constraints[:eq12][4] == 0 -@test o.constraints[:eq13][4] == 0 -@test o.constraints[:eq14][4] == 0 -@test o.constraints[:eq15][4] == 0 - -@def o begin - v ∈ R, variable - t ∈ [ 0, 1 ], time - x ∈ R, state - u ∈ R², control - x(0) ≥ 0 - x(0) ≥ 0, (1) - x(1) ≥ 0 - x(1) ≥ 0, (2) - x³(0) ≥ 0 - x³(0) ≥ 0,   (3) - x³(1) ≥ 0 - x³(1) ≥ 0,   (4) - x(t) ≥ 0 - x(t) ≥ 0, (5) - x(t) ≥ 0 - x(t) ≥ 0, (6) - u₁(t) ≥ 0 - u₁(t) ≥ 0, (7) - u₁(t) ≥ 0 - u₁(t) ≥ 0, (8) - x³(t) ≥ 0 - x³(t) ≥ 0,   (9) - x³(t) ≥ 0 - x³(t) ≥ 0,   (10) - (u₁^3)(t) ≥ 0 - (u₁^3)(t) ≥ 0, (11) - (u₁^3)(t) ≥ 0 - (u₁^3)(t) ≥ 0,   (12) - x(t) + (u₁^3)(t) ≥ 0 - x(t) + (u₁^3)(t) ≥ 0, (13) - x(t) + (u₁^3)(t) ≥ 0 - x(t) + (u₁^3)(t) ≥ 0, (14) - v ≥ 0 - v ≥ 0, (15) +# --------------------------------------------------------------- +# --------------------------------------------------------------- +@testset "variable" begin + + @def o begin + λ ∈ R^2, variable + tf = λ₂ + t ∈ [ 0, tf ], time + end + @test o.initial_time == 0 + @test o.final_time == Index(2) + + @def o begin + λ = (λ₁, tf) ∈ R^2, variable + t ∈ [ 0, tf ], time + end + @test o.initial_time == 0 + @test o.final_time == Index(2) + + @def o begin + t0 ∈ R, variable + t ∈ [ t0, 1 ], time + end + @test o.initial_time == Index(1) + @test o.final_time == 1 + + @def o begin + tf ∈ R, variable + t ∈ [ 0, tf ], time + end + @test o.initial_time == 0 + @test o.final_time == Index(1) + + @def o begin + v ∈ R², variable + s ∈ [ v[1], v[2] ], time + end + @test o.initial_time == Index(1) + @test o.final_time == Index(2) + + @def o begin + v ∈ R², variable + s0 = v₁ + sf = v₂ + s ∈ [ s0, sf ], time + end + @test o.initial_time == Index(1) + @test o.final_time == Index(2) + + @test_throws IncorrectArgument @def o begin + t0 ∈ R², variable + t ∈ [ t0, 1 ], time + end + + @test_throws IncorrectArgument @def o begin + tf ∈ R², variable + t ∈ [ 0, tf ], time + end + + @test_throws ParsingError @def o begin + v, variable + t ∈ [ 0, tf[v] ], time + end + + @test_throws ParsingError @def o begin + v, variable + t ∈ [ t0[v], 1 ], time + end + + @test_throws ParsingError @def o begin + v, variable + t ∈ [ t0[v], tf[v+1] ], time + end + + t0 = .0; tf = .1 + @def ocp begin + t ∈ [ t0, tf ], time + a ∈ R, variable + end ; + @test ocp isa OptimalControlModel + @test ocp.variable_dimension == 1 + @test ocp.variable_name == "a" + + t0 = .0; tf = .1 + @def ocp begin + t ∈ [ t0, tf ], time + a ∈ R³, variable + end ; + @test ocp isa OptimalControlModel + @test ocp.variable_dimension == 3 + @test ocp.variable_name == "a" + end -@test o.constraints[:eq1 ][3] == 0 -@test o.constraints[:eq2 ][3] == 0 -@test o.constraints[:eq3 ][3] == 0 -@test o.constraints[:eq4 ][3] == 0 -@test o.constraints[:eq5 ][3] == 0 -@test o.constraints[:eq6 ][3] == 0 -@test o.constraints[:eq7 ][3] == 0 -@test o.constraints[:eq8 ][3] == 0 -@test o.constraints[:eq9 ][3] == 0 -@test o.constraints[:eq10][3] == 0 -@test o.constraints[:eq11][3] == 0 -@test o.constraints[:eq12][3] == 0 -@test o.constraints[:eq13][3] == 0 -@test o.constraints[:eq14][3] == 0 -@test o.constraints[:eq15][3] == 0 -@test o.constraints[:eq1 ][4] == Inf -@test o.constraints[:eq2 ][4] == Inf -@test o.constraints[:eq3 ][4] == Inf -@test o.constraints[:eq4 ][4] == Inf -@test o.constraints[:eq5 ][4] == Inf -@test o.constraints[:eq6 ][4] == Inf -@test o.constraints[:eq7 ][4] == Inf -@test o.constraints[:eq8 ][4] == Inf -@test o.constraints[:eq9 ][4] == Inf -@test o.constraints[:eq10][4] == Inf -@test o.constraints[:eq11][4] == Inf -@test o.constraints[:eq12][4] == Inf -@test o.constraints[:eq13][4] == Inf -@test o.constraints[:eq14][4] == Inf -@test o.constraints[:eq15][4] == Inf - -@test_throws ParsingError @def o t ∈ 1 - -# tests from ct_parser.jl - - # phase 1: minimal problems, to check all possible syntaxes - - # time +# --------------------------------------------------------------- +# --------------------------------------------------------------- +@testset "time" begin + + t0 = 0 + @def o t ∈ [ t0, t0 + 4 ], time + @test o.initial_time == t0 + @test o.final_time == t0 + 4 + + @test_throws ParsingError @def o t ∈ 1 + @def ocp t ∈ [ 0.0 , 1.0 ], time; @test ocp isa OptimalControlModel @test ocp.time_name == "t" @@ -909,11 +270,24 @@ end @test ocp.initial_time == Index(1) @test ocp.final_time == tf +end + +# --------------------------------------------------------------- +# --------------------------------------------------------------- +@testset "state / control" begin + + @def o begin + x ∈ R, state + u ∈ R, control + end + @test o.state_dimension == 1 + @test o.control_dimension == 1 + # state t0 = 1.0; tf = 1.1 @def ocp begin t ∈ [ t0, tf ], time - u, state + u ∈ R, state end ; @test ocp isa OptimalControlModel @test ocp.time_name == "t" @@ -958,7 +332,6 @@ end @test ocp.state_dimension == 1 @test ocp.state_name == "a" - t0 = 5.0; tf = 5.1 @def ocp begin t ∈ [ t0 , tf ], time @@ -971,7 +344,6 @@ end @test ocp.state_dimension == 1 @test ocp.state_name == "b" - t0 = 6.0; tf = 6.1 @def ocp begin t ∈ [ t0 , tf ], time @@ -984,7 +356,6 @@ end @test ocp.state_dimension == 9 @test ocp.state_name == "u" - n = 3 t0 = 7.0; tf = 7.1 @def ocp begin @@ -998,12 +369,11 @@ end @test ocp.state_dimension == n @test ocp.state_name == "u" - # control t0 = 1.0; tf = 1.1 @def ocp begin t ∈ [ t0, tf ], time - u, control + u ∈ R, control end ; @test ocp isa OptimalControlModel @test ocp.time_name == "t" @@ -1061,7 +431,6 @@ end @test ocp.control_dimension == 1 @test ocp.control_name == "b" - t0 = 6.0; tf = 6.1 @def ocp begin t ∈ [ t0 , tf ], time @@ -1074,7 +443,6 @@ end @test ocp.control_dimension == 9 @test ocp.control_name == "u" - n = 3 t0 = 7.0; tf = 7.1 @def ocp begin @@ -1088,118 +456,576 @@ end @test ocp.control_dimension == n @test ocp.control_name == "u" +end - # variables - t0 = .0; tf = .1 - @def ocp begin - t ∈ [ t0, tf ], time - a, variable - end ; - @test ocp isa OptimalControlModel - @test ocp.variable_dimension == 1 - @test ocp.variable_name == "a" - - t0 = .0; tf = .1 - @def ocp begin - t ∈ [ t0, tf ], time - a ∈ R³, variable - end ; - @test ocp isa OptimalControlModel - @test ocp.variable_dimension == 3 - @test ocp.variable_name == "a" - - # alias - t0 = .0; tf = .1 - @def ocp begin - t ∈ [ t0, tf ], time - x ∈ R^3, state - u ∈ R^3, control - - r = x[1] - v = x₂ - a = x₃ - end ; - @test ocp isa OptimalControlModel - @test ocp.time_name == "t" - @test ocp.initial_time == t0 - @test ocp.final_time == tf - @test ocp.control_name == "u" - @test ocp.control_dimension == 3 - @test ocp.state_name == "x" - @test ocp.state_dimension == 3 - - # objectives - t0 = .0; tf = .1 - @def ocp begin - t ∈ [ t0, tf ], time - x ∈ R^3, state - u ∈ R^3, control - ∫( 0.5u(t)^2 ) → min - end ; - @test ocp isa OptimalControlModel - - t0 = .0; tf = .1 - @def ocp begin - t ∈ [ t0, tf ], time - x ∈ R^3, state - u ∈ R^3, control - ∫( 0.5u(t)^2 ) → max - end ; - @test ocp isa OptimalControlModel - - # constraints +# --------------------------------------------------------------- +# --------------------------------------------------------------- +@testset "dynamics" begin - # minimal constraint tests - # remark: constraint are heavily tested in test_ctparser_constraints.jl - t0 = 9.0; tf = 9.1 - r0 = 1.0; r1 = 2.0 - v0 = 2.0; vmax = sqrt(2) - m0 = 3.0; mf = 1.1 - @def ocp begin - t ∈ [ t0, tf ], time + @def o begin + t ∈ [ 0, 1 ], time x ∈ R^3, state u ∈ R^2, control - - m = x₂ - - x(t0) == [ r0, v0, m0 ], (1) - 0 ≤ u[1](t) ≤ 1 , (deux) - r0 ≤ x(t)[1] ≤ r1 , (trois) - 0 ≤ x₂(t) ≤ vmax , (quatre) - mf ≤ m(t) ≤ m0 , (5) + ẋ(t) == [ x[1](t) + 2u[2](t), 2x[3](t), x[1](t) + u[2](t) ] end - @test ocp isa OptimalControlModel - @test ocp.time_name == "t" - @test ocp.initial_time == t0 - @test ocp.final_time == tf - @test ocp.control_name == "u" - @test ocp.control_dimension == 2 - @test ocp.state_name == "x" - @test ocp.state_dimension == 3 + @test o.state_dimension == 3 + @test o.control_dimension == 2 + x = [ 1, 2, 3 ] + u = [ -1, 2 ] + @test o.dynamics(x, u) == [ x[1] + 2u[2], 2x[3], x[1] + u[2] ] + @def o begin + z ∈ R², variable + s ∈ [ 0, z₁ ], time + y ∈ R⁴, state + w ∈ R, control + r = y₃ + v = y₄ + aa = y₁ + ẏ(s) == [ aa(s), r²(s) + w(s) + z₁, 0, 0 ] + end + z = [ 5, 6 ] + y = [ 1, 2, 3, 4 ] + w = 9 + @test o.dynamics(y, w, z) == [ y[1], y[3]^2 + w + z[1], 0, 0 ] + + @def o begin + z ∈ R², variable + __s ∈ [ 0, z₁ ], time + y ∈ R⁴, state + w ∈ R, control + r = y₃ + v = y₄ + aa = y₁(__s) + ẏ(__s) == [ aa(__s), r²(__s) + w(__s) + z₁, 0, 0 ] + end + z = [ 5, 6 ] + y = [ 1, 2, 3, 4 ] + w = 9 + @test_throws MethodError o.dynamics(y, w, z) + + @def o begin + z ∈ R², variable + s ∈ [ 0, z₁ ], time + y ∈ R⁴, state + w ∈ R, control + r = y₃ + v = y₄ + aa = y₁(s) + v³ + z₂ + ẏ(s) == [ aa(s) + w(s)^2, r²(s), 0, 0 ] + end + z = [ 5, 6 ] + y = [ 1, 2, 3, 4 ] + y0 = y + yf = 3y0 + ww = 19 + @test o.dynamics(y, ww, z) == [ y[1] + ww^2 + y[4]^3 + z[2], y[3]^2, 0, 0 ] + + @def o begin + z ∈ R², variable + __t ∈ [ 0, z₁ ], time + y ∈ R⁴, state + w ∈ R, control + r = y₃ + v = y₄ + aa = y₁(0) + v³ + z₂ + ẏ(__t) == [ aa(__t) + (w^2)(__t), r²(__t), 0, 0 ] + aa(0) + y₂(z₁) → min + end + z = [ 5, 6 ] + y = [ 1, 2, 3, 4 ] + y0 = y + yf = 3y0 + w = 11 + @test_throws MethodError o.dynamics(y, w, z) + @test o.mayer(y0, yf, z) == y0[1] + y0[4]^3 + z[2] + yf[2] - @def ocp begin - t ∈ [ t0, tf ], time - x ∈ R^3, state - u ∈ R^2, control +end - m = x₂ +# --------------------------------------------------------------- +# --------------------------------------------------------------- +@testset "constraints" begin - x(t0) == [ r0, v0, m0 ] - 0 ≤ u(t)[2] ≤ 1 - r0 ≤ x(t)[1] ≤ r1 - 0 ≤ x₂(t) ≤ vmax - mf ≤ m(t) ≤ m0 + @def o begin + tf ∈ R, variable + t ∈ [ 0, tf ], time + x ∈ R², state + u ∈ R, control + r = x₁ + v = x₂ + w = r¹ + 2v³ + r(0) + w(tf) - tf² == 0, (1) end - @test ocp isa OptimalControlModel - @test ocp.time_name == "t" - @test ocp.initial_time == t0 - @test ocp.final_time == tf - @test ocp.control_name == "u" - @test ocp.control_dimension == 2 - @test ocp.state_name == "x" - @test ocp.state_dimension == 3 - - # dyslexic definition: t -> u -> x -> t + tf = 2 + x0 = [ 1, 2 ] + xf = [ 3, 4 ] + @test constraint(o, :eq1)(x0, xf, tf) == x0[1] + ( xf[1] + 2xf[2]^3 ) - tf^2 + + n = 11 + m = 6 + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R^n, state + u ∈ R^m, control + r = x₁ + v = x₂ + 0 ≤ r(t) ≤ 1, (1) + zeros(n) ≤ x(t) ≤ ones(n), (2) + [ 0, 0 ] ≤ x[1:2](t) ≤ [ 1, 1 ], (3) + [ 0, 0 ] ≤ x[1:2:4](t) ≤ [ 1, 1 ], (4) + 0 ≤ v(t)^2 ≤ 1, (5) + zeros(m) ≤ u(t) ≤ ones(m), (6) + [ 0, 0 ] ≤ u[1:2](t) ≤ [ 1, 1 ], (7) + [ 0, 0 ] ≤ u[1:2:4](t) ≤ [ 1, 1 ], (8) + 0 ≤ u₂(t)^2 ≤ 1, (9) + u₁(t) * x[1:2](t) == 1, (10) + 0 ≤ u₁(t) * x[1:2](t).^3 ≤ 1, (11) + end + x = Vector{Float64}(1:n) + u = 2 * Vector{Float64}(1:m) + @test constraint(o, :eq1 )(x) == x[1] + @test constraint(o, :eq2 )(x) == x + @test constraint(o, :eq3 )(x) == x[1:2] + @test constraint(o, :eq4 )(x) == x[1:2:4] + @test constraint(o, :eq5 )(x) == x[2]^2 + @test constraint(o, :eq6 )(u) == u + @test constraint(o, :eq7 )(u) == u[1:2] + @test constraint(o, :eq8 )(u) == u[1:2:4] + @test constraint(o, :eq9 )(u) == u[2]^2 + @test constraint(o, :eq10)(x, u) == u[1] * x[1:2] + @test constraint(o, :eq11)(x, u) == u[1] * x[1:2].^3 + + n = 11 + m = 6 + @def o begin + z ∈ R^2, variable + t ∈ [ 0, 1 ], time + x ∈ R^n, state + u ∈ R^m, control + r = x₁ + v = x₂ + 0 ≤ r(t) ≤ 1, (1) + zeros(n) ≤ x(t) ≤ ones(n), (2) + [ 0, 0 ] ≤ x[1:2](t) - [ z₁, 1 ] ≤ [ 1, 1 ], (3) + [ 0, 0 ] ≤ x[1:2:4](t) ≤ [ 1, 1 ], (4) + 0 ≤ v(t)^2 ≤ 1, (5) + zeros(m) ≤ u(t) ≤ ones(m), (6) + [ 0, 0 ] ≤ u[1:2](t) ≤ [ 1, 1 ], (7) + [ 0, 0 ] ≤ u[1:2:4](t) ≤ [ 1, 1 ], (8) + 0 ≤ u₂(t)^2 ≤ 1, (9) + u₁(t) * x[1:2](t) + z + f() == 1, (10) + 0 ≤ u₁(t) * x[1:2](t).^3 + z ≤ 1, (11) + end + f() = [ 1, 1 ] + z = 3 * Vector{Float64}(1:2) + x = Vector{Float64}(1:n) + u = 2 * Vector{Float64}(1:m) + @test constraint(o, :eq1 )(x, z) == x[1] + @test constraint(o, :eq2 )(x, z) == x + @test constraint(o, :eq3 )(x, z) == x[1:2] - [ z[1], 1 ] + @test constraint(o, :eq4 )(x, z) == x[1:2:4] + @test constraint(o, :eq5 )(x, z) == x[2]^2 + @test constraint(o, :eq6 )(u, z) == u + @test constraint(o, :eq7 )(u, z) == u[1:2] + @test constraint(o, :eq8 )(u, z) == u[1:2:4] + @test constraint(o, :eq9 )(u, z) == u[2]^2 + @test constraint(o, :eq10)(x, u, z) == u[1] * x[1:2] + z + f() + @test constraint(o, :eq11)(x, u, z) == u[1] * x[1:2].^3 + z + + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R², state + u ∈ R, control + begin + r = x₁ + v = x₂ + w = r + 2v + r(0) == 0, (1) + end + v(0) == 1, (♡) + ẋ(t) == [ v(t), w(t)^2 ] + ∫( u(t)^2 + x₁(t) ) → min + end + x = [ 1, 2 ] + x0 = 2 * x + xf = 3 * x + u = 3 + @test constraint(o, :eq1)(x0, xf) == x0[1] + @test constraint(o, Symbol("♡"))(x0, xf) == x0[2] + @test o.dynamics(x, u) == [ x[2], (x[1] + 2x[2])^2 ] + @test o.lagrange(x, u) == u^2 + x[1] + + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R², state + u ∈ R, control + r = x₁ + v = x₂ + w = r + 2v + r(0) == 0, (1) + v(0) == 1, (♡) + ẋ(t) == [ v(t), w(t)^2 ] + ∫( u(t)^2 + x₁(t) ) → min + end + x = [ 1, 2 ] + x0 = 2 * x + xf = 3 * x + u = 3 + @test constraint(o, :eq1)(x0, xf) == x0[1] + @test constraint(o, Symbol("♡"))(x0, xf) == x0[2] + @test o.dynamics(x, u) == [ x[2], (x[1] + 2x[2])^2 ] + @test o.lagrange(x, u) == u^2 + x[1] + + @def o begin + z ∈ R², variable + t ∈ [ 0, 1 ], time + x ∈ R², state + u ∈ R, control + r = x₁ + v = x₂ + w = r + 2v + r(0) == 0, (1) + v(0) == 1, (♡) + ẋ(t) == [ v(t), w(t)^2 + z₁ ] + ∫( u(t)^2 + z₂ * x₁(t) ) → min + end + x = [ 1, 2 ] + x0 = 2 * [ 1, 2 ] + xf = 3 * [ 1, 2 ] + u = 3 + z = [ 4, 5 ] + @test constraint(o, :eq1)(x0, xf, z) == x0[1] + @test constraint(o, Symbol("♡"))(x0, xf, z) == x0[2] + @test o.dynamics(x, u, z) == [ x[2], (x[1] + 2x[2])^2 + z[1] ] + @test o.lagrange(x, u, z) == u^2 + z[2] * x[1] + + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R², state + u ∈ R, control + r = x₁ + v = x₂ + r(0)^2 + v(1) == 0, (1) + v(0) == 1, (♡) + ẋ(t) == [ v(t), r(t)^2 ] + ∫( u(t)^2 + x₁(t) ) → min + end + x0 = [ 2, 3 ] + xf = [ 4, 5 ] + x = [ 1, 2 ] + u = 3 + @test constraint(o, :eq1)(x0, xf) == x0[1]^2 + xf[2] + @test constraint(o, Symbol("♡"))(x0, xf) == x0[2] + @test o.dynamics(x, u) == [ x[2], x[1]^2 ] + @test o.lagrange(x, u) == u^2 + x[1] + + @def o begin + z ∈ R, variable + t ∈ [ 0, 1 ], time + x ∈ R², state + u ∈ R, control + r = x₁ + v = x₂ + r(0) - z == 0, (1) + v(0) == 1, (♡) + ẋ(t) == [ v(t), r(t)^2 + z ] + ∫( u(t)^2 + z * x₁(t) ) → min + end + x0 = [ 2, 3 ] + xf = [ 4, 5 ] + x = [ 1, 2 ] + u = 3 + z = 4 + @test constraint(o, :eq1)(x0, xf, z) == x0[1] - z + @test constraint(o, Symbol("♡"))(x0, xf, z) == x0[2] + @test o.dynamics(x, u, z) == [ x[2], x[1]^2 + z ] + @test o.lagrange(x, u, z) == u^2 + z * x[1] + + @def o begin + z ∈ R, variable + t ∈ [ 0, 1 ], time + x ∈ R², state + u ∈ R, control + r = x₁ + v = x₂ + 0 ≤ r(0) - z ≤ 1, (1) + 0 ≤ v(1)^2 ≤ 1, (2) + [ 0, 0 ] ≤ x(0) ≤ [ 1, 1 ], (♡) + ẋ(t) == [ v(t), r(t)^2 + z ] + ∫( u(t)^2 + z * x₁(t) ) → min + end + x0 = [ 2, 3 ] + xf = [ 4, 5 ] + x = [ 1, 2 ] + u = 3 + z = 4 + @test constraint(o, :eq1)(x0, xf, z) == x0[1] - z + @test constraint(o, :eq2)(x0, xf, z) == xf[2]^2 + @test constraint(o, Symbol("♡"))(x0, xf, z) == x0 + @test o.dynamics(x, u, z) == [ x[2], x[1]^2 + z ] + @test o.lagrange(x, u, z) == u^2 + z * x[1] + + @def o begin + z ∈ R, variable + t ∈ [ 0, 1 ], time + x ∈ R², state + u ∈ R, control + r = x₁ + v = x₂ + 1 ≥ r(0) - z ≥ 0, (1) + 1 ≥ v(1)^2 ≥ 0, (2) + [ 1, 1 ] ≥ x(0) ≥ [ 0, 0 ], (3) + ẋ(t) == [ v(t), r(t)^2 + z ] + ∫( u(t)^2 + z * x₁(t) ) → min + end + x0 = [ 2, 3 ] + xf = [ 4, 5 ] + x = [ 1, 2 ] + u = 3 + z = 4 + @test constraint(o, :eq1)(x0, xf, z) == x0[1] - z + @test constraint(o, :eq2)(x0, xf, z) == xf[2]^2 + @test constraint(o, :eq3)(x0, xf, z) == x0 + @test o.dynamics(x, u, z) == [ x[2], x[1]^2 + z ] + @test o.lagrange(x, u, z) == u^2 + z * x[1] + @test o.constraints[:eq1][3] == 0 + @test o.constraints[:eq1][4] == 1 + @test o.constraints[:eq2][3] == 0 + @test o.constraints[:eq2][4] == 1 + @test o.constraints[:eq3][3] == [ 0, 0 ] + @test o.constraints[:eq3][4] == [ 1, 1 ] + + @def o begin + v ∈ R², variable + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + x(0) - v₁ == 0, (1) + x(1) - v₁ == 0, (2) + 0 ≤ x(0) - v₁ ≤ 1, (3) + 0 ≤ x(1) - v₁ ≤ 1, (4) + x(0) + x(1) - v₂ == 0, (5) + 0 ≤ x(0) + x(1) - v₂ ≤ 1, (6) + x(t) - v₁ == 0, (7) + u(t) - v₁ == 0, (8) + z = v₁ + 2v₂ + 0 ≤ x(t) - z ≤ 1, (9) + 0 ≤ u(t) - z ≤ 1, (10) + 0 ≤ x(t) + u(t) - z ≤ 1, (11) + ẋ(t) == z * x(t) + 2u(t) + v₁ == 1, (12) + 0 ≤ v₁ ≤ 1, (13) + z == 1, (14) + 0 ≤ z ≤ 1, (15) + z * x(1) → min + end + x = 1 + x0 = 2 + xf = 3 + u = 4 + v = [ 5, 6 ] + z = v[1] + 2v[2] + @test constraint(o, :eq1)(x0, xf, v) == x0 - v[1] + @test constraint(o, :eq2)(x0, xf, v) == xf - v[1] + @test constraint(o, :eq3)(x0, xf, v) == x0 - v[1] + @test constraint(o, :eq4)(x0, xf, v) == xf - v[1] + @test constraint(o, :eq5)(x0, xf, v) == x0 + xf - v[2] + @test constraint(o, :eq6)(x0, xf, v) == x0 + xf - v[2] + @test constraint(o, :eq7)(x, v) == x - v[1] + @test constraint(o, :eq9)(x, v) == x - z + @test constraint(o, :eq10)(u, v) == u - z + @test constraint(o, :eq11)(x, u, v) == x + u - z + @test constraint(o, :eq12)(v) == v[1] + @test constraint(o, :eq13)(v) == v[1] + @test constraint(o, :eq14)(v) == v[1] + 2v[2] + @test constraint(o, :eq15)(v) == v[1] + 2v[2] + + @def o begin + v ∈ R, variable + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R², control + x(0) ≤ 0 + x(0) ≤ 0, (1) + x(1) ≤ 0 + x(1) ≤ 0, (2) + x³(0) ≤ 0 + x³(0) ≤ 0, (3) + x³(1) ≤ 0 + x³(1) ≤ 0, (4) + x(t) ≤ 0 + x(t) ≤ 0, (5) + x(t) ≤ 0 + x(t) ≤ 0, (6) + u₁(t) ≤ 0 + u₁(t) ≤ 0, (7) + u₁(t) ≤ 0 + u₁(t) ≤ 0, (8) + x³(t) ≤ 0 + x³(t) ≤ 0, (9) + x³(t) ≤ 0 + x³(t) ≤ 0, (10) + (u₁^3)(t) ≤ 0 + (u₁^3)(t) ≤ 0, (11) + (u₁^3)(t) ≤ 0 + (u₁^3)(t) ≤ 0, (12) + x(t) + (u₁^3)(t) ≤ 0 + x(t) + (u₁^3)(t) ≤ 0, (13) + x(t) + (u₁^3)(t) ≤ 0 + x(t) + (u₁^3)(t) ≤ 0, (14) + v ≤ 0 + v ≤ 0, (15) + end + + @test o.constraints[:eq1 ][3] == -Inf + @test o.constraints[:eq2 ][3] == -Inf + @test o.constraints[:eq3 ][3] == -Inf + @test o.constraints[:eq4 ][3] == -Inf + @test o.constraints[:eq5 ][3] == -Inf + @test o.constraints[:eq6 ][3] == -Inf + @test o.constraints[:eq7 ][3] == -Inf + @test o.constraints[:eq8 ][3] == -Inf + @test o.constraints[:eq9 ][3] == -Inf + @test o.constraints[:eq10][3] == -Inf + @test o.constraints[:eq11][3] == -Inf + @test o.constraints[:eq12][3] == -Inf + @test o.constraints[:eq13][3] == -Inf + @test o.constraints[:eq14][3] == -Inf + @test o.constraints[:eq15][3] == -Inf + @test o.constraints[:eq1 ][4] == 0 + @test o.constraints[:eq2 ][4] == 0 + @test o.constraints[:eq3 ][4] == 0 + @test o.constraints[:eq4 ][4] == 0 + @test o.constraints[:eq5 ][4] == 0 + @test o.constraints[:eq6 ][4] == 0 + @test o.constraints[:eq7 ][4] == 0 + @test o.constraints[:eq8 ][4] == 0 + @test o.constraints[:eq9 ][4] == 0 + @test o.constraints[:eq10][4] == 0 + @test o.constraints[:eq11][4] == 0 + @test o.constraints[:eq12][4] == 0 + @test o.constraints[:eq13][4] == 0 + @test o.constraints[:eq14][4] == 0 + @test o.constraints[:eq15][4] == 0 + + @def o begin + v ∈ R, variable + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R², control + x(0) ≥ 0 + x(0) ≥ 0, (1) + x(1) ≥ 0 + x(1) ≥ 0, (2) + x³(0) ≥ 0 + x³(0) ≥ 0, (3) + x³(1) ≥ 0 + x³(1) ≥ 0, (4) + x(t) ≥ 0 + x(t) ≥ 0, (5) + x(t) ≥ 0 + x(t) ≥ 0, (6) + u₁(t) ≥ 0 + u₁(t) ≥ 0, (7) + u₁(t) ≥ 0 + u₁(t) ≥ 0, (8) + x³(t) ≥ 0 + x³(t) ≥ 0, (9) + x³(t) ≥ 0 + x³(t) ≥ 0, (10) + (u₁^3)(t) ≥ 0 + (u₁^3)(t) ≥ 0, (11) + (u₁^3)(t) ≥ 0 + (u₁^3)(t) ≥ 0, (12) + x(t) + (u₁^3)(t) ≥ 0 + x(t) + (u₁^3)(t) ≥ 0, (13) + x(t) + (u₁^3)(t) ≥ 0 + x(t) + (u₁^3)(t) ≥ 0, (14) + v ≥ 0 + v ≥ 0, (15) + end + + @test o.constraints[:eq1 ][3] == 0 + @test o.constraints[:eq2 ][3] == 0 + @test o.constraints[:eq3 ][3] == 0 + @test o.constraints[:eq4 ][3] == 0 + @test o.constraints[:eq5 ][3] == 0 + @test o.constraints[:eq6 ][3] == 0 + @test o.constraints[:eq7 ][3] == 0 + @test o.constraints[:eq8 ][3] == 0 + @test o.constraints[:eq9 ][3] == 0 + @test o.constraints[:eq10][3] == 0 + @test o.constraints[:eq11][3] == 0 + @test o.constraints[:eq12][3] == 0 + @test o.constraints[:eq13][3] == 0 + @test o.constraints[:eq14][3] == 0 + @test o.constraints[:eq15][3] == 0 + @test o.constraints[:eq1 ][4] == Inf + @test o.constraints[:eq2 ][4] == Inf + @test o.constraints[:eq3 ][4] == Inf + @test o.constraints[:eq4 ][4] == Inf + @test o.constraints[:eq5 ][4] == Inf + @test o.constraints[:eq6 ][4] == Inf + @test o.constraints[:eq7 ][4] == Inf + @test o.constraints[:eq8 ][4] == Inf + @test o.constraints[:eq9 ][4] == Inf + @test o.constraints[:eq10][4] == Inf + @test o.constraints[:eq11][4] == Inf + @test o.constraints[:eq12][4] == Inf + @test o.constraints[:eq13][4] == Inf + @test o.constraints[:eq14][4] == Inf + @test o.constraints[:eq15][4] == Inf + + # minimal constraint tests + # remark: constraint are heavily tested in test_ctparser_constraints.jl + t0 = 9.0; tf = 9.1 + r0 = 1.0; r1 = 2.0 + v0 = 2.0; vmax = sqrt(2) + m0 = 3.0; mf = 1.1 + @def ocp begin + t ∈ [ t0, tf ], time + x ∈ R^3, state + u ∈ R^2, control + + m = x₂ + + x(t0) == [ r0, v0, m0 ], (1) + 0 ≤ u[1](t) ≤ 1 , (deux) + r0 ≤ x(t)[1] ≤ r1 , (trois) + 0 ≤ x₂(t) ≤ vmax , (quatre) + mf ≤ m(t) ≤ m0 , (5) + end + @test ocp isa OptimalControlModel + @test ocp.time_name == "t" + @test ocp.initial_time == t0 + @test ocp.final_time == tf + @test ocp.control_name == "u" + @test ocp.control_dimension == 2 + @test ocp.state_name == "x" + @test ocp.state_dimension == 3 + + @def ocp begin + t ∈ [ t0, tf ], time + x ∈ R^3, state + u ∈ R^2, control + + m = x₂ + + x(t0) == [ r0, v0, m0 ] + 0 ≤ u(t)[2] ≤ 1 + r0 ≤ x(t)[1] ≤ r1 + 0 ≤ x₂(t) ≤ vmax + mf ≤ m(t) ≤ m0 + end + @test ocp isa OptimalControlModel + @test ocp.time_name == "t" + @test ocp.initial_time == t0 + @test ocp.final_time == tf + @test ocp.control_name == "u" + @test ocp.control_dimension == 2 + @test ocp.state_name == "x" + @test ocp.state_dimension == 3 + + # dyslexic definition: t -> u -> x -> t u0 = 9.0; uf = 9.1 z0 = 1.0; z1 = 2.0 k0 = 2.0; kmax = sqrt(2) @@ -1209,9 +1035,7 @@ end u ∈ [ u0, uf ], time t ∈ R^3, state x ∈ R^2, control - b = t₂ - t(u0) == [ z0, k0, b0 ] 0 ≤ x[2](u) ≤ 1 z0 ≤ t(u)[1] ≤ z1 @@ -1227,41 +1051,6 @@ end @test ocp.state_name == "t" @test ocp.state_dimension == 3 - # error detections (this can be tricky -> need more work) - - # this one is detected by the generated code (and not the parser) - @test_throws CTException @def o begin - t ∈ [ t0, tf ], time - t ∈ [ t0, tf ], time - end - - # illegal constraint name (1bis), detected by the parser - t0 = 9.0; tf = 9.1 - r0 = 1.0; v0 = 2.0; m0 = 3.0 - @test_throws ParsingError @def o begin - t ∈ [ t0, tf ], time - x ∈ R^2, state - u ∈ R^2, control - - 0 ≤ u(t) ≤ 1 , (1bis) - end ; - - # t0 is unknown in the x(t0) constraint, detected by the parser - r0 = 1.0; v0 = 2.0; m0 = 3.0 - @test_throws ParsingError @def o begin - t ∈ [ 0, 1 ], time - x ∈ R^2, state - u ∈ R^2, control - - x(t0) == [ r0, v0, m0 ], (1) - 0 ≤ u(t) ≤ 1 , (1bis) - end ; - - # -#end - -# old tests from test_ctparser_constraints.jl - # # test all constraints on @def macro # @@ -1288,7 +1077,6 @@ end t ∈ [ t0, tf ], time x ∈ R^n, state u ∈ R^n, control - x(t0) == x0 x[2](t0) == x02 x[2:3](t0) == y0 @@ -1337,11 +1125,9 @@ end tf = 1.2 n = 4 @def ocp3 begin - t ∈ [ t0, tf ], time x ∈ R^n, state u ∈ R^n, control - x(tf) == xf xf_b ≤ x(tf) ≤ xf_u x[2](tf) == xf2 @@ -1370,7 +1156,6 @@ end t ∈ [ t0, tf ], time x ∈ R^n, state u ∈ R^n, control - x(tf) == xf , final_1 xf_b ≤ x(tf) ≤ xf_u , final_2 x[2](tf) == xf2 , final_3 @@ -1389,18 +1174,15 @@ end tf = 1.4 n = 2 @def ocp5 begin - t ∈ [ t0, tf ], time x ∈ R^n, state u ∈ R^n, control - x(tf) - tf*x(t0) == [ 0, 1 ] [ 0, 1 ] ≤ x(tf) - tf*x(t0) ≤ [ 1, 3 ] x[2](t0)^2 == 1 1 ≤ x[2](t0)^2 ≤ 2 x[2](tf)^2 == 1 1 ≤ x[2](tf)^2 ≤ 2 - end @test ocp5 isa OptimalControlModel @test ocp5.state_dimension == n @@ -1412,18 +1194,15 @@ end tf = 1.5 n = 2 @def ocp6 begin - t ∈ [ t0, tf ], time x ∈ R^n, state u ∈ R^n, control - x(tf) - tf*x(t0) == [ 0, 1 ] , boundary_1 [ 0, 1 ] ≤ x(tf) - tf*x(t0) ≤ [ 1, 3 ] , boundary_2 x[2](t0)^2 == 1 , boundary_3 1 ≤ x[2](t0)^2 ≤ 2 , boundary_4 x[2](tf)^2 == 1 , boundary_5 1 ≤ x[2](tf)^2 ≤ 2 , boundary_6 - end @test ocp6 isa OptimalControlModel @test ocp6.state_dimension == n @@ -1444,11 +1223,9 @@ end tf = 1.6 n = 2 @def ocp7 begin - t ∈ [ t0, tf ], time x ∈ R^n, state u ∈ R^n, control - u_b ≤ u[1](t) ≤ u_u u2_b ≤ u[1](t) ≤ u2_u v_b ≤ u[2](t) ≤ v_u @@ -1472,11 +1249,9 @@ end v_b = 5.0 v_u = 6.0 @def ocp8 begin - t ∈ [ t0, tf ], time x ∈ R^n, state u ∈ R^n, control - u_b ≤ u[2](t) ≤ u_u , control_1 u2_b ≤ u[1](t) ≤ u2_u , control_3 [ 1, v_b ] ≤ u[1:2](t) ≤ [ 2, v_u ], control_5 @@ -1489,7 +1264,6 @@ end @test ocp8.initial_time == t0 @test ocp8.final_time == tf - # more vars x_b = 10.0 x_u = 11.0 @@ -1503,11 +1277,9 @@ end tf = 1.8 n = 10 @def ocp9 begin - t ∈ [ t0, tf ], time x ∈ R^n, state u ∈ R^n, control - x_b ≤ x[3](t) ≤ x_u #x(t) == x_u x2_b ≤ x[2](t) ≤ x2_u @@ -1527,11 +1299,9 @@ end tf = 1.9 n = 11 @def ocp10 begin - t ∈ [ t0, tf ], time x ∈ R^n, state u ∈ R^n, control - x_b ≤ x[3](t) ≤ x_u , state_1 #x(t) == x_u , state_2 x2_b ≤ x[2](t) ≤ x2_u , state_3 @@ -1547,7 +1317,6 @@ end @test ocp10.initial_time == t0 @test ocp10.final_time == tf - # === mixed t0 = 0.111 tf = 1.111 @@ -1557,7 +1326,6 @@ end t ∈ [ t0, tf ], time x ∈ R^n, state u ∈ R^n, control - u[2](t) * x[1:2](t) == [ -1, 1 ] [ -1, 1 ] ≤ u[2](t) * x[1:2](t) ≤ [ 0, 2 ] end @@ -1568,11 +1336,9 @@ end @test ocp11.final_time == tf @def ocp12 begin - t ∈ [ t0, tf ], time x ∈ R^n, state u ∈ R^n, control - u[2](t) * x[1:2](t) == [ -1, 1 ] , mixed_1 [ -1, 1 ] ≤ u[2](t) * x[1:2](t) ≤ [ 0, 2 ] , mixed_2 end @@ -1582,17 +1348,14 @@ end @test ocp12.initial_time == t0 @test ocp12.final_time == tf - # === dynamics t0 = 0.112 tf = 1.112 @def ocp13 begin - t ∈ [ t0, tf ], time x ∈ R, state u ∈ R, control - ẋ(t) == 2x(t) + u(t)^2 end @test ocp13 isa OptimalControlModel @@ -1614,3 +1377,868 @@ end end end + +# --------------------------------------------------------------- +# --------------------------------------------------------------- +@testset "Lagrange cost" begin + + # -------------------------------- + # min + t0 = 0 + tf = 1 + @def o begin + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R, control + x(t0) == [ -1, 0 ], (1) + x(tf) == [ 0, 0 ] + ẋ(t) == A * x(t) + B * u(t) + ∫( 0.5u(t)^2 ) → min + end + x = [ 1, 2 ] + x0 = 2 * x + xf = 3 * x + u = -1 + A = [ 0 1 + 0 0 ] + B = [ 0 + 1 ] + @test constraint(o, :eq1)(x0, xf) == x0 + @test o.dynamics(x, u) == A * x + B * u + @test o.lagrange(x, u) == 0.5u^2 + @test o.criterion == :min + + t0 = 0 + tf = 1 + @def o begin + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R, control + x(t0) == [ -1, 0 ], (1) + x(tf) == [ 0, 0 ] + ẋ(t) == A * x(t) + B * u(t) + -∫( 0.5u(t)^2 ) → min + end + x = [ 1, 2 ] + x0 = 2 * x + xf = 3 * x + u = -1 + A = [ 0 1 + 0 0 ] + B = [ 0 + 1 ] + @test constraint(o, :eq1)(x0, xf) == x0 + @test o.dynamics(x, u) == A * x + B * u + @test o.lagrange(x, u) == -0.5u^2 + @test o.criterion == :min + + t0 = 0 + tf = 1 + @def o begin + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R, control + x(t0) == [ -1, 0 ], (1) + x(tf) == [ 0, 0 ] + ẋ(t) == A * x(t) + B * u(t) + 0.5 * ∫( u(t)^2 ) → min + end + x = [ 1, 2 ] + x0 = 2 * x + xf = 3 * x + u = -1 + A = [ 0 1 + 0 0 ] + B = [ 0 + 1 ] + @test constraint(o, :eq1)(x0, xf) == x0 + @test o.dynamics(x, u) == A * x + B * u + @test o.lagrange(x, u) == 0.5u^2 + @test o.criterion == :min + + t0 = 0 + tf = 1 + @def o begin + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R, control + x(t0) == [ -1, 0 ], (1) + x(tf) == [ 0, 0 ] + ẋ(t) == A * x(t) + B * u(t) + 0.5∫( u(t)^2 ) → min + end + x = [ 1, 2 ] + x0 = 2 * x + xf = 3 * x + u = -1 + A = [ 0 1 + 0 0 ] + B = [ 0 + 1 ] + @test constraint(o, :eq1)(x0, xf) == x0 + @test o.dynamics(x, u) == A * x + B * u + @test o.lagrange(x, u) == 0.5u^2 + @test o.criterion == :min + + t0 = 0 + tf = 1 + @def o begin + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R, control + x(t0) == [ -1, 0 ], (1) + x(tf) == [ 0, 0 ] + ẋ(t) == A * x(t) + B * u(t) + -0.5 * ∫( u(t)^2 ) → min + end + x = [ 1, 2 ] + x0 = 2 * x + xf = 3 * x + u = -1 + A = [ 0 1 + 0 0 ] + B = [ 0 + 1 ] + @test constraint(o, :eq1)(x0, xf) == x0 + @test o.dynamics(x, u) == A * x + B * u + @test o.lagrange(x, u) == -0.5u^2 + @test o.criterion == :min + + t0 = 0 + tf = 1 + @def o begin + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R, control + x(t0) == [ -1, 0 ], (1) + x(tf) == [ 0, 0 ] + ẋ(t) == A * x(t) + B * u(t) + (-0.5 + tf) * ∫( u(t)^2 ) → min + end + x = [ 1, 2 ] + x0 = 2 * x + xf = 3 * x + u = -1 + A = [ 0 1 + 0 0 ] + B = [ 0 + 1 ] + @test constraint(o, :eq1)(x0, xf) == x0 + @test o.dynamics(x, u) == A * x + B * u + @test o.lagrange(x, u) == (-0.5 + tf) * u^2 + @test o.criterion == :min + + t0 = 0 + tf = 1 + @test_throws ParsingError @def o begin # a call to the time (t, here) must not appear before the integral + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R, control + x(t0) == [ -1, 0 ], (1) + x(tf) == [ 0, 0 ] + ẋ(t) == A * x(t) + B * u(t) + (-0.5 + t) * ∫( u(t)^2 ) → min + end + + t0 = 0 + tf = 1 + @test_throws ParsingError @def o begin # a call to the time (t, here) must not appear before the integral + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R, control + x(t0) == [ -1, 0 ], (1) + x(tf) == [ 0, 0 ] + ẋ(t) == A * x(t) + B * u(t) + (-0.5 + x(t)) * ∫( u(t)^2 ) → min + end + + # ----------------------------------- + # max + t0 = 0 + tf = 1 + @def o begin + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R, control + x(t0) == [ -1, 0 ], (1) + x(tf) == [ 0, 0 ] + ẋ(t) == A * x(t) + B * u(t) + ∫( 0.5u(t)^2 ) → max + end + x = [ 1, 2 ] + x0 = 2 * x + xf = 3 * x + u = -1 + A = [ 0 1 + 0 0 ] + B = [ 0 + 1 ] + @test constraint(o, :eq1)(x0, xf) == x0 + @test o.dynamics(x, u) == A * x + B * u + @test o.lagrange(x, u) == 0.5u^2 + @test o.criterion == :max + + t0 = 0 + tf = 1 + @def o begin + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R, control + x(t0) == [ -1, 0 ], (1) + x(tf) == [ 0, 0 ] + ẋ(t) == A * x(t) + B * u(t) + -∫( 0.5u(t)^2 ) → max + end + x = [ 1, 2 ] + x0 = 2 * x + xf = 3 * x + u = -1 + A = [ 0 1 + 0 0 ] + B = [ 0 + 1 ] + @test constraint(o, :eq1)(x0, xf) == x0 + @test o.dynamics(x, u) == A * x + B * u + @test o.lagrange(x, u) == -0.5u^2 + @test o.criterion == :max + + t0 = 0 + tf = 1 + @def o begin + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R, control + x(t0) == [ -1, 0 ], (1) + x(tf) == [ 0, 0 ] + ẋ(t) == A * x(t) + B * u(t) + 0.5 * ∫( u(t)^2 ) → max + end + x = [ 1, 2 ] + x0 = 2 * x + xf = 3 * x + u = -1 + A = [ 0 1 + 0 0 ] + B = [ 0 + 1 ] + @test constraint(o, :eq1)(x0, xf) == x0 + @test o.dynamics(x, u) == A * x + B * u + @test o.lagrange(x, u) == 0.5u^2 + @test o.criterion == :max + + t0 = 0 + tf = 1 + @def o begin + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R, control + x(t0) == [ -1, 0 ], (1) + x(tf) == [ 0, 0 ] + ẋ(t) == A * x(t) + B * u(t) + 0.5∫( u(t)^2 ) → max + end + x = [ 1, 2 ] + x0 = 2 * x + xf = 3 * x + u = -1 + A = [ 0 1 + 0 0 ] + B = [ 0 + 1 ] + @test constraint(o, :eq1)(x0, xf) == x0 + @test o.dynamics(x, u) == A * x + B * u + @test o.lagrange(x, u) == 0.5u^2 + @test o.criterion == :max + + t0 = 0 + tf = 1 + @def o begin + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R, control + x(t0) == [ -1, 0 ], (1) + x(tf) == [ 0, 0 ] + ẋ(t) == A * x(t) + B * u(t) + -0.5 * ∫( u(t)^2 ) → max + end + x = [ 1, 2 ] + x0 = 2 * x + xf = 3 * x + u = -1 + A = [ 0 1 + 0 0 ] + B = [ 0 + 1 ] + @test constraint(o, :eq1)(x0, xf) == x0 + @test o.dynamics(x, u) == A * x + B * u + @test o.lagrange(x, u) == -0.5u^2 + @test o.criterion == :max + + # ----------------------------------- + t0 = .0; tf = .1 + @def ocp begin + t ∈ [ t0, tf ], time + x ∈ R^3, state + u ∈ R^3, control + ∫( 0.5u(t)^2 ) → min + end ; + @test ocp isa OptimalControlModel + + t0 = .0; tf = .1 + @def ocp begin + t ∈ [ t0, tf ], time + x ∈ R^3, state + u ∈ R^3, control + ∫( 0.5u(t)^2 ) → max + end ; + @test ocp isa OptimalControlModel + +end + + t0 = 0 + tf = 1 + @test_throws ParsingError @def o begin # a call to the time (t, here) must not appear before the integral + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R, control + x(t0) == [ -1, 0 ], (1) + x(tf) == [ 0, 0 ] + ẋ(t) == A * x(t) + B * u(t) + (-0.5 + t) * ∫( u(t)^2 ) → max + end + + t0 = 0 + tf = 1 + @test_throws ParsingError @def o begin # a call to the time (t, here) must not appear before the integral + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R, control + x(t0) == [ -1, 0 ], (1) + x(tf) == [ 0, 0 ] + ẋ(t) == A * x(t) + B * u(t) + (-0.5 + x(t)) * ∫( u(t)^2 ) → max + end + + +# --------------------------------------------------------------- +# --------------------------------------------------------------- +@testset "Bolza cost" begin + + # ------------------------------- + # min + # Mayer ± Lagrange + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + (x(0) + 3x(1)) + ∫(x(t) + u(t)) → min + end + x = 1 + u = 2 + x0 = 3 + xf = 4 + @test o.mayer(x0, xf) == x0 + 3xf + @test o.lagrange(x, u) == x + u + @test o.criterion == :min + + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + (x(0) + 2x(1)) + ∫(x(t) + u(t)) → min + end + x = 1 + u = 2 + x0 = 3 + xf = 4 + @test o.mayer(x0, xf) == x0 + 2xf + @test o.lagrange(x, u) == x + u + @test o.criterion == :min + + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + (x(0) + 2x(1)) + 2 * ∫(x(t) + u(t)) → min + end + x = 1 + u = 2 + x0 = 3 + xf = 4 + @test o.mayer(x0, xf) == x0 + 2xf + @test o.lagrange(x, u) == 2(x + u) + @test o.criterion == :min + + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + (x(0) + 2x(1)) - ∫(x(t) + u(t)) → min + end + x = 1 + u = 2 + x0 = 3 + xf = 4 + @test o.mayer(x0, xf) == x0 + 2xf + @test o.lagrange(x, u) == -(x + u) + @test o.criterion == :min + + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + (x(0) + 2x(1)) - 2 * ∫(x(t) + u(t)) → min + end + x = 1 + u = 2 + x0 = 3 + xf = 4 + @test o.mayer(x0, xf) == x0 + 2xf + @test o.lagrange(x, u) == -2(x + u) + @test o.criterion == :min + + @test_throws ParsingError @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + (x(0) + 2x(1)) + t * ∫(x(t) + u(t)) → min + end + + @test_throws ParsingError @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + (x(0) + 2x(1)) - t * ∫(x(t) + u(t)) → min + end + + # ------------------------------- + # max + # Mayer ± Lagrange + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + (x(0) + 5x(1)) + ∫(x(t) + u(t)) → max + end + x = 1 + u = 2 + x0 = 3 + xf = 4 + @test o.mayer(x0, xf) == x0 + 5xf + @test o.lagrange(x, u) == x + u + @test o.criterion == :max + + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + (x(0) + 2x(1)) + 2 * ∫(x(t) + u(t)) → max + end + x = 1 + u = 2 + x0 = 3 + xf = 4 + @test o.mayer(x0, xf) == x0 + 2xf + @test o.lagrange(x, u) == 2(x + u) + @test o.criterion == :max + + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + (x(0) + 2x(1)) - ∫(x(t) + u(t)) → max + end + x = 1 + u = 2 + x0 = 3 + xf = 4 + @test o.mayer(x0, xf) == x0 + 2xf + @test o.lagrange(x, u) == -(x + u) + @test o.criterion == :max + + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + (x(0) + 2x(1)) - 2 * ∫(x(t) + u(t)) → max + end + x = 1 + u = 2 + x0 = 3 + xf = 4 + @test o.mayer(x0, xf) == x0 + 2xf + @test o.lagrange(x, u) == -2(x + u) + @test o.criterion == :max + + @test_throws ParsingError @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + (x(0) + 2x(1)) + t * ∫(x(t) + u(t)) → max + end + + @test_throws ParsingError @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + (x(0) + 2x(1)) - t * ∫(x(t) + u(t)) → max + end + + # ------------------------------- + # min + # Lagrange ± Mayer + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + ∫(x(t) + u(t)) + (x(0) + 2x(1)) → min + end + x = 1 + u = 2 + x0 = 3 + xf = 4 + @test o.mayer(x0, xf) == x0 + 2xf + @test o.lagrange(x, u) == x + u + @test o.criterion == :min + + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + 2 * ∫(x(t) + u(t)) + (x(0) + 2x(1)) → min + end + x = 1 + u = 2 + x0 = 3 + xf = 4 + @test o.mayer(x0, xf) == x0 + 2xf + @test o.lagrange(x, u) == 2(x + u) + @test o.criterion == :min + + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + ∫(x(t) + u(t)) - (x(0) + 2x(1)) → min + end + x = 1 + u = 2 + x0 = 3 + xf = 4 + @test o.mayer(x0, xf) == -(x0 + 2xf) + @test o.lagrange(x, u) == x + u + @test o.criterion == :min + + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + 2 * ∫(x(t) + u(t)) - (x(0) + 2x(1)) → min + end + x = 1 + u = 2 + x0 = 3 + xf = 4 + @test o.mayer(x0, xf) == -(x0 + 2xf) + @test o.lagrange(x, u) == 2(x + u) + @test o.criterion == :min + + @test_throws ParsingError @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + t * ∫(x(t) + u(t)) + 1 → min + end + + @test_throws ParsingError @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + t * ∫(x(t) + u(t)) - 1 → min + end + + # ------------------------------- + # max + # Lagrange ± Mayer + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + ∫(x(t) + u(t)) + (x(0) + 2x(1)) → max + end + x = 1 + u = 2 + x0 = 3 + xf = 4 + @test o.mayer(x0, xf) == x0 + 2xf + @test o.lagrange(x, u) == x + u + @test o.criterion == :max + + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + 2 * ∫(x(t) + u(t)) + (x(0) + 2x(1)) → max + end + x = 1 + u = 2 + x0 = 3 + xf = 4 + @test o.mayer(x0, xf) == x0 + 2xf + @test o.lagrange(x, u) == 2(x + u) + @test o.criterion == :max + + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + ∫(x(t) + u(t)) - (x(0) + 2x(1)) → max + end + x = 1 + u = 2 + x0 = 3 + xf = 4 + @test o.mayer(x0, xf) == -(x0 + 2xf) + @test o.lagrange(x, u) == x + u + @test o.criterion == :max + + @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + 2 * ∫(x(t) + u(t)) - (x(0) + 2x(1)) → max + end + x = 1 + u = 2 + x0 = 3 + xf = 4 + @test o.mayer(x0, xf) == -(x0 + 2xf) + @test o.lagrange(x, u) == 2(x + u) + @test o.criterion == :max + + @test_throws ParsingError @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + t * ∫(x(t) + u(t)) + 1 → max + end + + @test_throws ParsingError @def o begin + t ∈ [ 0, 1 ], time + x ∈ R, state + u ∈ R, control + t * ∫(x(t) + u(t)) - 1 → max + end + +end + +# --------------------------------------------------------------- +# --------------------------------------------------------------- +@testset "Mayer cost" begin + + @def o begin + s ∈ [ 0, 1 ], time + y ∈ R^4, state + w ∈ R, control + r = y₃ + v = y₄ + r(0) + v(1) → min + end + y0 = [ 1, 2, 3, 4 ] + yf = 2 * [ 1, 2, 3, 4 ] + @test is_min(o) + @test o.mayer(y0, yf) == y0[3] + yf[4] + + @def o begin + s ∈ [ 0, 1 ], time + y ∈ R^4, state + w ∈ R, control + r = y₃ + v = y₄ + r(0) + v(1) → max + end + y0 = [ 1, 2, 3, 4 ] + yf = 2 * [ 1, 2, 3, 4 ] + @test is_max(o) + @test o.mayer(y0, yf) == y0[3] + yf[4] + + @def o begin + z ∈ R^2, variable + s ∈ [ 0, z₁ ], time + y ∈ R^4, state + w ∈ R, control + r = y₃ + v = y₄ + r(0) + v(z₁) + z₂ → min + end + z = [ 5, 6 ] + y0 = [ 1, 2, 3, 4 ] + yf = 2 * [ 1, 2, 3, 4 ] + @test is_min(o) + @test o.mayer(y0, yf, z) == y0[3] + yf[4] + z[2] + + @def o begin + z ∈ R², variable + s ∈ [ 0, z₁ ], time + y ∈ R⁴, state + w ∈ R, control + r = y₃ + v = y₄ + aa = y₁ + w² + v³ + z₂ + ẏ(s) == [ aa(s), r²(s), 0, 0 ] + r(0) + v(z₁) + z₂ → min + end + z = [ 5, 6 ] + y = [ 1, 2, 3, 4 ] + y0 = y + yf = 3y0 + w = 7 + @test o.dynamics(y, w, z) == [ y[1] + w^2 + y[4]^3 + z[2], y[3]^2, 0, 0 ] + @test o.mayer(y0, yf, z) == y0[3] + yf[4] + z[2] + + @def o begin + z ∈ R², variable + s ∈ [ 0, z₁ ], time + y ∈ R⁴, state + w ∈ R, control + r = y₃ + v = y₄ + aa = y₁(s) + v³ + z₂ + ẏ(s) == [ aa(s) + (w^2)(s), r²(s), 0, 0 ] + r(0) + v(z₁) + z₂ → min + end + z = [ 5, 6 ] + y = [ 1, 2, 3, 4 ] + y0 = y + yf = 3y0 + w = 7 + @test o.dynamics(y, w, z) == [ y[1] + w^2 + y[4]^3 + z[2], y[3]^2, 0, 0 ] + @test o.mayer(y0, yf, z) == y0[3] + yf[4] + z[2] + + @def o begin + z ∈ R², variable + s ∈ [ 0, z₁ ], time + y ∈ R⁴, state + w ∈ R, control + r = y₃ + v = y₄ + aa = y₁ + v³ + z₂ + aa(0) + y₂(z₁) → min + end + z = [ 5, 6 ] + y0 = y + yf = 3y0 + @test o.mayer(y0, yf, z) == y0[1] + y0[4]^3 + z[2] + yf[2] + + @def o begin + z ∈ R², variable + __t ∈ [ 0, z₁ ], time + y ∈ R⁴, state + w ∈ R, control + r = y₃ + v = y₄ + aa = y₁(__t) + v³ + z₂ + ẏ(__t) == [ aa(__t) + (w^2)(__t), r²(__t), 0, 0 ] + aa(0) + y₂(z₁) → min + end + z = [ 5, 6 ] + y = [ 1, 2, 3, 4 ] + y0 = y + yf = 3y0 + w = 11 + @test o.dynamics(y, w, z) == [ y[1] + w^2 + y[4]^3 + z[2], y[3]^2, 0, 0 ] + @test_throws UndefVarError o.mayer(y0, yf, z) + +end + +# --------------------------------------------------------------- +# --------------------------------------------------------------- +@testset "closure" begin + + a = 1 + f(b) = begin # closure of a, local c, and @def in function + c = 3 + @def ocp begin + t ∈ [ a, b ], time + x ∈ R, state + u ∈ R, control + ẋ(t) == x(t) + u(t) + b + c + d + end + ocp + end + o = f(2) + d = 4 + x = 10 + u = 20 + @test o.dynamics(x, u) == x + u + 2 + 3 + 4 + +end + +# --------------------------------------------------------------- +# --------------------------------------------------------------- +@testset "error detection" begin + + # error detections (this can be tricky -> need more work) + + # this one is detected by the generated code (and not the parser) + t0 = 9.0; tf = 9.1 + @test_throws CTException @def o begin + t ∈ [ t0, tf ], time + t ∈ [ t0, tf ], time + end + + # illegal constraint name (1bis), detected by the parser + t0 = 9.0; tf = 9.1 + r0 = 1.0; v0 = 2.0; m0 = 3.0 + @test_throws ParsingError @def o begin + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R^2, control + 0 ≤ u(t) ≤ 1 , (1bis) + end + + # t0 is unknown in the x(t0) constraint, detected by the parser + r0 = 1.0; v0 = 2.0; m0 = 3.0 + @test_throws ParsingError @def o begin + t ∈ [ 0, 1 ], time + x ∈ R^2, state + u ∈ R^2, control + x(t0) == [ r0, v0, m0 ], (1) + 0 ≤ u(t) ≤ 1 , (1bis) + end + + # bad syntax for Bolza cost interpreted as a Mayer term with trailing ∫ + @test_throws ParsingError @def o begin + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R, control + x(t0) == [ -1, 0 ], (1) + x(tf) == [ 0, 0 ] + ẋ(t) == A * x(t) + B * u(t) + 1 + 2 + ∫( u(t)^2 ) → min # should be ( 1 + 2 ) + ∫(...) + end + + @test_throws ParsingError @def o begin + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R, control + x(t0) == [ -1, 0 ], (1) + x(tf) == [ 0, 0 ] + ẋ(t) == A * x(t) + B * u(t) + ∫( u(t)^2 ) + 1 + 2 → min # should be ∫(...) + ( 1 + 2 ) + end + + @test_throws ParsingError @def o begin + t ∈ [ t0, tf ], time + x ∈ R^2, state + u ∈ R, control + x(t0) == [ -1, 0 ], (1) + x(tf) == [ 0, 0 ] + ẋ(t) == A * x(t) + B * u(t) + ∫( u(t)^2 ) / 2 → min # forbidden + end + + +end + +end \ No newline at end of file