Skip to content

Commit

Permalink
Merge pull request #150 from control-toolbox/time_revise
Browse files Browse the repository at this point in the history
time! is revised
  • Loading branch information
jbcaillau authored Jun 14, 2024
2 parents 14449f4 + f6e69d4 commit eda4107
Show file tree
Hide file tree
Showing 7 changed files with 253 additions and 319 deletions.
2 changes: 1 addition & 1 deletion docs/src/api-print.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ ocp = Model()
state!(ocp, 2, "x", ["r", "v"]) # dimension of the state with the names of the components
control!(ocp, 1) # dimension of the control
time!(ocp, [0, 1], "s") # initial and final time, with the name of the variable time
time!(ocp, t0=0, tf=1, name="s") # initial and final time, with the name of the variable time
constraint!(ocp, :initial, lb=[-1, 0], ub=[-1, 0])
constraint!(ocp, :final , lb=[ 0, 0], ub=[ 0, 0])
Expand Down
212 changes: 68 additions & 144 deletions src/model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,9 @@ end
"""
$(TYPEDSIGNATURES)
Fix initial time, final time is free and given by the variable at the provided index.
Set the initial and final times. We denote by t0 the initial time and tf the final time.
The optimal control problem is denoted ocp.
When a time is free, then one must provide the corresponding index of the ocp variable.
!!! note
Expand All @@ -349,166 +351,88 @@ Fix initial time, final time is free and given by the variable at the provided i
# Examples
```jldoctest
julia> time!(ocp, 0, Index(2), "t")
julia> time!(ocp, t0=0, tf=1 ) # Fixed t0 and fixed tf
julia> time!(ocp, t0=0, indf=2) # Fixed t0 and free tf
julia> time!(ocp, ind0=2, tf=1 ) # Free t0 and fixed tf
julia> time!(ocp, ind0=2, indf=3) # Free t0 and free tf
```
"""
function time!(ocp::OptimalControlModel{<: TimeDependence, NonFixed}, t0::Time, indf::Index, name::String=__time_name())
__check_variable_set(ocp)
__is_time_set(ocp) && throw(UnauthorizedCall("the time has already been set. Use time! once."))
(indf.val > ocp.variable_dimension) && throw(IncorrectArgument("out of range index of variable"))
#
ocp.initial_time = t0
ocp.final_time = indf
ocp.time_name = name
ocp.initial_time_name = t0 isa Integer ? string(t0) : string(round(t0, digits=2))
ocp.final_time_name = ocp.variable_components_names[indf]
nothing # to force to return nothing
end

function time!(ocp::OptimalControlModel, t0::Time, indf::Index, name::Symbol)
time!(ocp, t0, indf, string(name))
end

"""
$(TYPEDSIGNATURES)
Fix final time, initial time is free and given by the variable at the provided index.
When you plot a solution of an optimal control problem, the name of the time variable appears.
By default, the name is "t".
Consider you want to set the name of the time variable to "s".
# Examples
```jldoctest
julia> time!(ocp, Index(2), 1, "t")
julia> time!(ocp, t0=0, tf=1, name="s") # name is a String
# or
julia> time!(ocp, t0=0, tf=1, name=:s ) # name is a Symbol
```
"""
function time!(ocp::OptimalControlModel{<: TimeDependence, NonFixed}, ind0::Index, tf::Time, name::String=__time_name())
__check_variable_set(ocp)
__is_time_set(ocp) && throw(UnauthorizedCall("the time has already been set. Use time! once."))
(ind0.val > ocp.variable_dimension) && throw(IncorrectArgument("out of range index of variable"))
ocp.initial_time = ind0
ocp.final_time = tf
ocp.time_name = name
ocp.initial_time_name = ocp.variable_components_names[ind0]
ocp.final_time_name = tf isa Integer ? string(tf) : string(round(tf, digits=2))
nothing # to force to return nothing
end

function time!(ocp::OptimalControlModel, ind0::Index, tf::Time, name::Symbol)
time!(ocp, ind0, tf, string(name))
end
function time!(
ocp::OptimalControlModel{<: TimeDependence, VT};
t0::Union{Time, Nothing}=nothing,
tf::Union{Time, Nothing}=nothing,
ind0::Union{Integer, Nothing}=nothing,
indf::Union{Integer, Nothing}=nothing,
name::Union{String, Symbol}=__time_name()) where VT

"""
$(TYPEDSIGNATURES)
# check if the problem has been set to Variable or NonVariable
VT == NonFixed && (!isnothing(ind0) || !isnothing(indf)) && __check_variable_set(ocp)

Initial and final times are free and given by the variable at the provided indices.
# check if indices are in 1:q
q = ocp.variable_dimension
!isnothing(ind0) && !(1 ind0 q) && throw(IncorrectArgument("the index of t0 variable must be contained in 1:$q"))
!isnothing(indf) && !(1 indf q) && throw(IncorrectArgument("the index of tf variable must be contained in 1:$q"))

# Examples
```jldoctest
julia> time!(ocp, Index(2), Index(3), "t")
```
"""
function time!(ocp::OptimalControlModel{<: TimeDependence, NonFixed}, ind0::Index, indf::Index, name::String=__time_name())
__check_variable_set(ocp)
# check if the function has been already called
__is_time_set(ocp) && throw(UnauthorizedCall("the time has already been set. Use time! once."))
(ind0.val > ocp.variable_dimension) && throw(IncorrectArgument("out of range index of variable"))
(indf.val > ocp.variable_dimension) && throw(IncorrectArgument("out of range index of variable"))
ocp.initial_time = ind0
ocp.final_time = indf
ocp.time_name = name
ocp.initial_time_name = ocp.variable_components_names[ind0]
ocp.final_time_name = ocp.variable_components_names[indf]
nothing # to force to return nothing
end

function time!(ocp::OptimalControlModel, ind0::Index, indf::Index, name::Symbol)
time!(ocp, ind0, indf, string(name))
end

"""
$(TYPEDSIGNATURES)
Fix initial and final times to `times[1]` and `times[2]`, respectively.
# Examples
```jldoctest
julia> time!(ocp, [ 0, 1 ])
julia> ocp.initial_time
0
julia> ocp.final_time
1
julia> ocp.time_name
"t"
julia> time!(ocp, [ 0, 1 ], "s")
julia> ocp.initial_time
0
julia> ocp.final_time
1
julia> ocp.time_name
"s"
julia> time!(ocp, [ 0, 1 ], :s)
julia> ocp.initial_time
0
julia> ocp.final_time
1
julia> ocp.time_name
"s"
```
"""
function time!(ocp::OptimalControlModel, times::Times, name::String=__time_name())
(length(times) != 2) && throw(IncorrectArgument("times must be of dimension 2"))
time!(ocp, times[1], times[2], name)
end

function time!(ocp::OptimalControlModel, times::Times, name::Symbol)
time!(ocp, times, string(name))
end

"""
$(TYPEDSIGNATURES)

Fix initial and final times to `times[1]` and `times[2]`, respectively.
# check consistency
!isnothing(t0) && !isnothing(ind0) && throw(IncorrectArgument("Providing t0 and ind0 has no sense. The initial time cannot be fixed and free."))
isnothing(t0) && isnothing(ind0) && throw(IncorrectArgument("Please either provide the value of the initial time t0 (if fixed) or its index in the variable of ocp (if free)."))
!isnothing(tf) && !isnothing(indf) && throw(IncorrectArgument("Providing tf and indf has no sense. The final time cannot be fixed and free."))
isnothing(tf) && isnothing(indf) && throw(IncorrectArgument("Please either provide the value of the final time tf (if fixed) or its index in the variable of ocp (if free)."))

# Examples
VT == Fixed && !isnothing(ind0) && throw(IncorrectArgument("You cannot have the initial time free (ind0 is provided) and the ocp non variable."))
VT == Fixed && !isnothing(indf) && throw(IncorrectArgument("You cannot have the final time free (indf is provided) and the ocp non variable."))

```jldoctest
julia> time!(ocp, 0, 1)
julia> ocp.initial_time
0
julia> ocp.final_time
1
julia> ocp.time_name
"t"
#
name = name isa String ? name : string(name)

julia> time!(ocp, 0, 1, "s")
julia> ocp.initial_time
0
julia> ocp.final_time
1
julia> ocp.time_name
"s"
# core
@match (t0, ind0, tf, indf) begin
(::Time, ::Nothing, ::Time, ::Nothing) => begin # (t0, tf)
ocp.initial_time = t0
ocp.final_time = tf
ocp.time_name = name
ocp.initial_time_name = t0 isa Integer ? string(t0) : string(round(t0, digits=2))
ocp.final_time_name = tf isa Integer ? string(tf) : string(round(tf, digits=2))
end
(::Nothing, ::Integer, ::Time, ::Nothing) => begin # (ind0, tf)
ocp.initial_time = Index(ind0)
ocp.final_time = tf
ocp.time_name = name
ocp.initial_time_name = ocp.variable_components_names[ind0]
ocp.final_time_name = tf isa Integer ? string(tf) : string(round(tf, digits=2))
end
(::Time, ::Nothing, ::Nothing, ::Integer) => begin # (t0, indf)
ocp.initial_time = t0
ocp.final_time = Index(indf)
ocp.time_name = name
ocp.initial_time_name = t0 isa Integer ? string(t0) : string(round(t0, digits=2))
ocp.final_time_name = ocp.variable_components_names[indf]
end
(::Nothing, ::Integer, ::Nothing, ::Integer) => begin # (ind0, indf)
ocp.initial_time = Index(ind0)
ocp.final_time = Index(indf)
ocp.time_name = name
ocp.initial_time_name = ocp.variable_components_names[ind0]
ocp.final_time_name = ocp.variable_components_names[indf]
end
_ => throw(IncorrectArgument("Provided arguments are inconsistent."))
end

julia> time!(ocp, 0, 1, :s)
julia> ocp.initial_time
0
julia> ocp.final_time
1
julia> ocp.time_name
"s"
```
"""
function time!(ocp::OptimalControlModel, t0::Time, tf::Time, name::String=__time_name())
__is_time_set(ocp) && throw(UnauthorizedCall("the time has already been set. Use time! once."))
ocp.initial_time=t0
ocp.final_time=tf
ocp.time_name = name
ocp.initial_time_name = t0 isa Integer ? string(t0) : string(round(t0, digits=2))
ocp.final_time_name = tf isa Integer ? string(tf) : string(round(tf, digits=2))
nothing # to force to return nothing
end

function time!(ocp::OptimalControlModel, t0::Time, tf::Time, name::Symbol)
time!(ocp, t0, tf, string(name))
end

"""
Expand Down
12 changes: 6 additions & 6 deletions src/onepass.jl
Original file line number Diff line number Diff line change
Expand Up @@ -198,26 +198,26 @@ p_time!(p, ocp, t, t0, tf; log=false) = begin
p.tf = tf
tt = QuoteNode(t)
code = @match (has(t0, p.v), has(tf, p.v)) begin
(false, false) => :( time!($ocp, $t0, $tf, $tt) )
(false, false) => :( time!($ocp; t0=$t0, tf=$tf, name=$tt) )
(true , false) => @match t0 begin
:( $v1[$i] ) && if (v1 == p.v) end => :( time!($ocp, Index($i), $tf, $tt) )
:( $v1[$i] ) && if (v1 == p.v) end => :( time!($ocp; ind0=$i, tf=$tf, name=$tt) )
:( $v1 ) && if (v1 == p.v) end => quote
($ocp.variable_dimension 1) &&
throw(IncorrectArgument("variable must be of dimension one for a time"))
time!($ocp, Index(1), $tf, $tt) end
time!($ocp; ind0=1, tf=$tf, name=$tt) end
_ =>
return __throw("bad time declaration", p.lnum, p.line) end
(false, true ) => @match tf begin
:( $v1[$i] ) && if (v1 == p.v) end => :( time!($ocp, $t0, Index($i), $tt) )
:( $v1[$i] ) && if (v1 == p.v) end => :( time!($ocp; t0=$t0, indf=$i, name=$tt) )
:( $v1 ) && if (v1 == p.v) end => quote
($ocp.variable_dimension 1) &&
throw(IncorrectArgument("variable must be of dimension one for a time"))
time!($ocp, $t0, Index(1), $tt) end
time!($ocp; t0=$t0, indf=1, name=$tt) end
_ =>
return __throw("bad time declaration", p.lnum, p.line) end
_ => @match (t0, tf) begin
(:( $v1[$i] ), :( $v2[$j] )) && if (v1 == v2 == p.v) end =>
:( time!($ocp, Index($i), Index($j), $tt) )
:( time!($ocp; ind0=$i, indf=$j, name=$tt) )
_ =>
return __throw("bad time declaration", p.lnum, p.line) end
end
Expand Down
51 changes: 2 additions & 49 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,55 +9,8 @@ const has = CTBase.has
const replace_call = CTBase.replace_call
const constraint_type = CTBase.constraint_type

function __constraint!(
ocp::OptimalControlModel{<: TimeDependence, V},
type::Symbol,
rg::Index,
lb::Union{ctVector,Nothing},
ub::Union{ctVector,Nothing},
label::Symbol=CTBase.__constraint_label()) where {V <: VariableDependence}

constraint!(ocp, type, rg=rg.val, f=nothing, lb=lb, ub=ub, label=label)
nothing # to force to return nothing

end

function __constraint!(
ocp::OptimalControlModel{<: TimeDependence, V},
type::Symbol,
rg::OrdinalRange{<:Integer},
lb::Union{ctVector,Nothing},
ub::Union{ctVector,Nothing},
label::Symbol=CTBase.__constraint_label()) where {V <: VariableDependence}

constraint!(ocp, type, rg=rg, f=nothing, lb=lb, ub=ub, label=label)
nothing # to force to return nothing

end

function __constraint!(
ocp::OptimalControlModel,
type::Symbol,
lb::Union{ctVector,Nothing},
ub::Union{ctVector,Nothing},
label::Symbol=CTBase.__constraint_label())

constraint!(ocp, type, rg=nothing, f=nothing, lb=lb, ub=ub, label=label)
nothing # to force to return nothing

end

function __constraint!(
ocp::OptimalControlModel{T, V},
type::Symbol,
f::Function,
lb::Union{ctVector,Nothing},
ub::Union{ctVector,Nothing},
label::Symbol=CTBase.__constraint_label()) where {T, V}

constraint!(ocp, type, rg=nothing, f=f, lb=lb, ub=ub, label=label)
nothing # to force to return nothing
end
#
include("utils.jl")

#
@testset verbose = true showtiming = true "Base" begin
Expand Down
Loading

0 comments on commit eda4107

Please sign in to comment.