Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

time! is revised #150

Merged
merged 3 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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, [-1, 0])
constraint!(ocp, :final , [ 0, 0])
Expand Down
218 changes: 71 additions & 147 deletions src/model.jl
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ocots at some point, we should switch to Variable / NonVariable instead of Fixed / NonFixed (maybe keeping the old syntax). I plead guilty on this 🙁

Copy link
Member

@jbcaillau jbcaillau Jun 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ocots now Index is only used to store internally (= a user will not see it, even with the functional API) the index of the variable associated with free initial / final time 👍🏽

And nice consistency checks!

Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,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 @@ -287,166 +289,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)
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".

Fix final time, initial time is free and given by the variable at the provided index.

# 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

"""
$(TYPEDSIGNATURES)

Initial and final times are free and given by the variable at the provided indices.
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

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

# 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)
# 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)."))

Fix initial and final times to `times[1]` and `times[2]`, respectively.

# 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"

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.

# 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"
#
name = name isa String ? name : string(name)

# 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ocots 👍🏽

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
3 changes: 3 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ const has = CTBase.has
const replace_call = CTBase.replace_call
const constraint_type = CTBase.constraint_type

#
include("utils.jl")

#
@testset verbose = true showtiming = true "Base" begin
for name ∈ (
Expand Down
Loading
Loading