Skip to content

Commit 4d92b20

Browse files
committed
add times
1 parent dbb6b5d commit 4d92b20

File tree

15 files changed

+658
-97
lines changed

15 files changed

+658
-97
lines changed

Project.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@ version = "0.1.0"
66
[deps]
77
CTBase = "54762871-cc72-4466-b8e8-f6c8b58076cd"
88
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
9+
MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078"
910
Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a"
11+
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
1012

1113
[compat]
1214
CTBase = "0.14.0"
1315
DocStringExtensions = "0.9.3"
16+
MLStyle = "0.4.17"
1417
Parameters = "0.12.3"
18+
StaticArrays = "1.9.8"
1519
julia = "1.10"

src/CTModels.jl

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,24 @@ module CTModels
33
# imports
44
import CTBase
55
using DocStringExtensions
6+
using MLStyle
67
using Parameters # @with_kw: to have default values in struct
7-
8+
using StaticArrays
89

910
# aliases
1011
const Dimension = Int
12+
const ctNumber = Real
13+
const Time = ctNumber
14+
const Variable = Union{ctNumber, AbstractVector{<:ctNumber}}
1115

1216
#
1317
include("types.jl")
14-
include("control.jl")
18+
19+
#
1520
include("state.jl")
21+
include("control.jl")
22+
include("variable.jl")
23+
include("times.jl")
1624

1725
#
1826

src/common.jl

Lines changed: 0 additions & 3 deletions
This file was deleted.

src/control.jl

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,22 @@ $(TYPEDSIGNATURES)
44
Used to set the default value of the names of the control.
55
The default value is `"u"`.
66
"""
7-
__control_name() = "u"
7+
__control_name()::String = "u"
88

99
"""
1010
$(TYPEDSIGNATURES)
1111
1212
Used to set the default value of the names of the controls.
1313
The default value is `["u"]` for a one dimensional control, and `["u₁", "u₂", ...]` for a multi dimensional control.
1414
"""
15-
__control_components(m::Dimension, name::String) =
15+
__control_components(m::Dimension, name::String)::Vector{String} =
1616
m > 1 ? [name * CTBase.ctindices(i) for i range(1, m)] : [name]
1717

1818
"""
1919
$(TYPEDSIGNATURES)
2020
2121
"""
22-
__is_control_set(ocp::OptimalControlModelMutable) = !isnothing(ocp.control)
22+
__is_control_set(ocp::OptimalControlModelMutable)::Bool = !ismissing(ocp.control)
2323

2424
"""
2525
$(TYPEDSIGNATURES)
@@ -81,7 +81,7 @@ function control!(
8181
m::Dimension,
8282
name::T1 = __control_name(),
8383
components_names::Vector{T2} = __control_components(m, string(name)),
84-
) where {T1<:Union{String, Symbol}, T2<:Union{String, Symbol}}
84+
)::Nothing where {T1<:Union{String, Symbol}, T2<:Union{String, Symbol}}
8585

8686
# checkings
8787
__is_control_set(ocp) && throw(CTBase.UnauthorizedCall("the control has already been set."))
@@ -92,7 +92,8 @@ function control!(
9292
)
9393

9494
# set the control
95-
ocp.control = ControlModel(m, string(name), string.(components_names))
95+
ocp.control = ControlModel{m}(string(name),
96+
SVector{m}(string.(components_names)))
9697
return nothing
9798
end
9899

@@ -101,18 +102,16 @@ end
101102
# ------------------------------------------------------------------------------ #
102103

103104
# from ControlModel
104-
dimension(model::ControlModel) = model.dimension
105-
name(model::ControlModel) = model.name
106-
components(model::ControlModel) = model.components
105+
(dimension(::ControlModel{M})::Dimension) where M = M
106+
name(model::ControlModel)::String = model.name
107+
components(model::ControlModel)::Vector{String} = Vector(model.components)
107108

108109
# from OptimalControlModel
109-
control(model::OptimalControlModel) = model.control
110-
control_dimension(model::OptimalControlModel) = dimension(control(model))
111-
control_name(model::OptimalControlModel) = name(control(model))
112-
control_components(model::OptimalControlModel) = components(control(model))
113-
114-
# from OptimalControlModelMutable
115-
control(model::OptimalControlModelMutable) = model.control
116-
control_dimension(model::OptimalControlModelMutable) = dimension(control(model))
117-
control_name(model::OptimalControlModelMutable) = name(control(model))
118-
control_components(model::OptimalControlModelMutable) = components(control(model))
110+
(control(model::OptimalControlModel{T, S, C, V})::C) where {
111+
T<:AbstractTimesModel,
112+
S<:AbstractStateModel,
113+
C<:AbstractControlModel,
114+
V<:AbstractVariableModel} = model.control
115+
control_dimension(model::OptimalControlModel)::Dimension = dimension(control(model))
116+
control_name(model::OptimalControlModel)::String = name(control(model))
117+
control_components(model::OptimalControlModel)::Vector{String} = components(control(model))

src/state.jl

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,22 @@ $(TYPEDSIGNATURES)
44
Used to set the default value of the name of the state.
55
The default value is `"x"`.
66
"""
7-
__state_name() = "x"
7+
__state_name()::String = "x"
88

99
"""
1010
$(TYPEDSIGNATURES)
1111
1212
Used to set the default value of the names of the states.
1313
The default value is `["x"]` for a one dimensional state, and `["x₁", "x₂", ...]` for a multi dimensional state.
1414
"""
15-
__state_components(n::Dimension, name::String) =
15+
__state_components(n::Dimension, name::String)::Vector{String} =
1616
n > 1 ? [name * CTBase.ctindices(i) for i range(1, n)] : [name]
1717

1818
"""
1919
$(TYPEDSIGNATURES)
2020
2121
"""
22-
__is_state_set(ocp::OptimalControlModelMutable) = !isnothing(ocp.state)
22+
__is_state_set(ocp::OptimalControlModelMutable)::Bool = !ismissing(ocp.state)
2323

2424
"""
2525
$(TYPEDSIGNATURES)
@@ -81,7 +81,7 @@ function state!(
8181
n::Dimension,
8282
name::T1 = __state_name(),
8383
components_names::Vector{T2} = __state_components(n, string(name)),
84-
) where {T1<:Union{String, Symbol}, T2<:Union{String, Symbol}}
84+
)::Nothing where {T1<:Union{String, Symbol}, T2<:Union{String, Symbol}}
8585

8686
# checkings
8787
__is_state_set(ocp) && throw(CTBase.UnauthorizedCall("the state has already been set."))
@@ -90,7 +90,8 @@ function state!(
9090
throw(CTBase.IncorrectArgument("the number of state names must be equal to the state dimension"))
9191

9292
# set the state
93-
ocp.state = StateModel(n, string(name), string.(components_names))
93+
ocp.state = StateModel{n}(string(name),
94+
SVector{n}(string.(components_names)))
9495
return nothing
9596
end
9697

@@ -99,18 +100,16 @@ end
99100
# ------------------------------------------------------------------------------ #
100101

101102
# from StateModel
102-
dimension(model::StateModel) = model.dimension
103-
name(model::StateModel) = model.name
104-
components(model::StateModel) = model.components
103+
(dimension(::StateModel{N})::Dimension) where N = N
104+
name(model::StateModel)::String = model.name
105+
components(model::StateModel)::Vector{String} = Vector(model.components)
105106

106107
# from OptimalControlModel
107-
state(model::OptimalControlModel) = model.state
108-
state_dimension(model::OptimalControlModel) = dimension(state(model))
109-
state_name(model::OptimalControlModel) = name(state(model))
110-
state_components(model::OptimalControlModel) = components(state(model))
111-
112-
# from OptimalControlModelMutable
113-
state(model::OptimalControlModelMutable) = model.state
114-
state_dimension(model::OptimalControlModelMutable) = dimension(state(model))
115-
state_name(model::OptimalControlModelMutable) = name(state(model))
116-
state_components(model::OptimalControlModelMutable) = components(state(model))
108+
(state(model::OptimalControlModel{T, S, C, V})::S) where {
109+
T<:AbstractTimesModel,
110+
S<:AbstractStateModel,
111+
C<:AbstractControlModel,
112+
V<:AbstractVariableModel} = model.state
113+
state_dimension(model::OptimalControlModel)::Dimension = dimension(state(model))
114+
state_name(model::OptimalControlModel)::String = name(state(model))
115+
state_components(model::OptimalControlModel)::Vector{String} = components(state(model))

src/times.jl

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
"""
2+
$(TYPEDSIGNATURES)
3+
4+
Used to set the default value of the name of the time.
5+
The default value is `t`.
6+
"""
7+
__time_name()::String = "t"
8+
9+
"""
10+
$(TYPEDSIGNATURES)
11+
12+
"""
13+
__is_times_set(ocp::OptimalControlModelMutable)::Bool = !ismissing(ocp.times)
14+
15+
"""
16+
$(TYPEDSIGNATURES)
17+
18+
Set the initial and final times. We denote by t0 the initial time and tf the final time.
19+
The optimal control problem is denoted ocp.
20+
When a time is free, then, one must provide the corresponding index of the ocp variable.
21+
22+
!!! note
23+
24+
You must use time! only once to set either the initial or the final time, or both.
25+
26+
# Examples
27+
28+
```@example
29+
julia> time!(ocp, t0=0, tf=1 ) # Fixed t0 and fixed tf
30+
julia> time!(ocp, t0=0, indf=2) # Fixed t0 and free tf
31+
julia> time!(ocp, ind0=2, tf=1 ) # Free t0 and fixed tf
32+
julia> time!(ocp, ind0=2, indf=3) # Free t0 and free tf
33+
```
34+
35+
When you plot a solution of an optimal control problem, the name of the time variable appears.
36+
By default, the name is "t".
37+
Consider you want to set the name of the time variable to "s".
38+
39+
```@example
40+
julia> time!(ocp, t0=0, tf=1, name="s") # name is a String
41+
# or
42+
julia> time!(ocp, t0=0, tf=1, name=:s ) # name is a Symbol
43+
```
44+
"""
45+
function time!(
46+
ocp::OptimalControlModelMutable;
47+
t0::Union{Time, Nothing} = nothing,
48+
tf::Union{Time, Nothing} = nothing,
49+
ind0::Union{Int, Nothing} = nothing,
50+
indf::Union{Int, Nothing} = nothing,
51+
time_name::Union{String, Symbol} = __time_name(),
52+
)::Nothing
53+
54+
# check if the function has been already called
55+
__is_times_set(ocp) && throw(CTBase.UnauthorizedCall("the time has already been set."))
56+
57+
# If t0 or tf is free, check if the problem has a variable set
58+
# and in this case check consistency, meaning that ind0 and indf must belong
59+
# to 1 <= ind0, indf <= q, where q is the variable dimension.
60+
# Otherwise, throw an error.
61+
(!isnothing(ind0) || !isnothing(indf)) && !__is_variable_set(ocp) &&
62+
throw(CTBase.UnauthorizedCall("the variable must be set before calling time! if t0 or tf is free."))
63+
64+
# check consistency with the variable
65+
if __is_variable_set(ocp)
66+
q = dimension(ocp.variable)
67+
68+
!isnothing(ind0) && !(1 ind0 q) && # t0 is free
69+
throw(CTBase.IncorrectArgument("the index of the t0 variable must be contained in 1:$q"))
70+
71+
!isnothing(indf) && !(1 indf q) && # tf is free
72+
throw(CTBase.IncorrectArgument("the index of the tf variable must be contained in 1:$q"))
73+
end
74+
75+
# check consistency
76+
!isnothing(t0) &&
77+
!isnothing(ind0) &&
78+
throw(
79+
CTBase.IncorrectArgument(
80+
"Providing t0 and ind0 has no sense. The initial time cannot be fixed and free.",
81+
),
82+
)
83+
isnothing(t0) &&
84+
isnothing(ind0) &&
85+
throw(
86+
CTBase.IncorrectArgument(
87+
"Please either provide the value of the initial time t0 (if fixed) or its index in the variable of ocp (if free).",
88+
),
89+
)
90+
!isnothing(tf) &&
91+
!isnothing(indf) &&
92+
throw(
93+
CTBase.IncorrectArgument(
94+
"Providing tf and indf has no sense. The final time cannot be fixed and free.",
95+
),
96+
)
97+
isnothing(tf) &&
98+
isnothing(indf) &&
99+
throw(
100+
CTBase.IncorrectArgument(
101+
"Please either provide the value of the final time tf (if fixed) or its index in the variable of ocp (if free).",
102+
),
103+
)
104+
105+
#
106+
time_name = time_name isa String ? time_name : string(time_name)
107+
108+
# core
109+
(initial_time, final_time) = @match (t0, ind0, tf, indf) begin
110+
(::Time, ::Nothing, ::Time, ::Nothing) => begin # (t0, tf)
111+
(FixedTimeModel(t0, t0 isa Int ? string(t0) : string(round(t0, digits = 2))),
112+
FixedTimeModel(tf, tf isa Int ? string(tf) : string(round(tf, digits = 2))))
113+
end
114+
(::Nothing, ::Int, ::Time, ::Nothing) => begin # (ind0, tf)
115+
(FreeTimeModel(ind0, components(ocp.variable)[ind0]),
116+
FixedTimeModel(tf, tf isa Int ? string(tf) : string(round(tf, digits = 2))))
117+
end
118+
(::Time, ::Nothing, ::Nothing, ::Int) => begin # (t0, indf)
119+
(FixedTimeModel(t0, t0 isa Int ? string(t0) : string(round(t0, digits = 2))),
120+
FreeTimeModel(indf, components(ocp.variable)[indf]))
121+
end
122+
(::Nothing, ::Int, ::Nothing, ::Int) => begin # (ind0, indf)
123+
(FreeTimeModel(ind0, components(ocp.variable)[ind0]),
124+
FreeTimeModel(indf, components(ocp.variable)[indf]))
125+
end
126+
_ => throw(CTBase.IncorrectArgument("Provided arguments are inconsistent."))
127+
end
128+
129+
ocp.times = TimesModel(initial_time, final_time, time_name)
130+
return nothing
131+
end
132+
133+
# ------------------------------------------------------------------------------ #
134+
# GETTERS
135+
# ------------------------------------------------------------------------------ #
136+
137+
# From FixedTimeModel
138+
time(model::FixedTimeModel)::Time = model.time
139+
name(model::FixedTimeModel)::String = model.name
140+
141+
# From FreeTimeModel
142+
index(model::FreeTimeModel)::Int = model.index
143+
name(model::FreeTimeModel)::String = model.name
144+
function time(model::FreeTimeModel, variable::ctNumber)::Time
145+
# check if model.index = 1
146+
!(model.index == 1) && throw(CTBase.IncorrectArgument("the index of the time variable must be 1."))
147+
return variable
148+
end
149+
function time(model::FreeTimeModel, variable::AbstractVector{<:ctNumber})::Time
150+
# check if model.index in [1, length(variable)]
151+
!(1 model.index length(variable)) && throw(CTBase.IncorrectArgument("the index of the time variable must be contained in 1:$(length(variable))"))
152+
return variable[model.index]
153+
end
154+
155+
# From TimesModel
156+
(initial(model::TimesModel{TI, TF})::TI) where {TI <: AbstractTimeModel, TF <: AbstractTimeModel} = model.initial
157+
(final(model::TimesModel{TI, TF})::TF) where {TI <: AbstractTimeModel, TF <: AbstractTimeModel} = model.final
158+
time_name(model::TimesModel)::String = model.time_name
159+
initial_time(model::TimesModel{FixedTimeModel, <:AbstractTimeModel})::Time = time(initial(model))
160+
final_time(model::TimesModel{<:AbstractTimeModel, FixedTimeModel})::Time = time(final(model))
161+
initial_time(model::TimesModel{FreeTimeModel, <:AbstractTimeModel}, variable::Variable)::Time = time(initial(model), variable)
162+
final_time(model::TimesModel{<:AbstractTimeModel, FreeTimeModel}, variable::Variable)::Time = time(final(model), variable)
163+
164+
# From OptimalControlModel
165+
(times(model::OptimalControlModel{T, S, C, V})::T) where {
166+
T<:AbstractTimesModel,
167+
S<:AbstractStateModel,
168+
C<:AbstractControlModel,
169+
V<:AbstractVariableModel} = model.times
170+
time_name(model::OptimalControlModel)::String = time_name(times(model))
171+
(initial_time(model::OptimalControlModel{T, S, C, V})::Time) where {
172+
T<:TimesModel{FixedTimeModel, <:AbstractTimeModel},
173+
S<:AbstractStateModel,
174+
C<:AbstractControlModel,
175+
V<:AbstractVariableModel} = initial_time(times(model))
176+
(final_time(model::OptimalControlModel{T, S, C, V})::Time) where {
177+
T<:TimesModel{<:AbstractTimeModel, FixedTimeModel},
178+
S<:AbstractStateModel,
179+
C<:AbstractControlModel,
180+
V<:AbstractVariableModel} = final_time(times(model))
181+
(initial_time(model::OptimalControlModel{T, S, C, V}, variable::Variable)::Time) where {
182+
T<:TimesModel{FreeTimeModel, <:AbstractTimeModel},
183+
S<:AbstractStateModel,
184+
C<:AbstractControlModel,
185+
V<:AbstractVariableModel} = initial_time(times(model), variable)
186+
(final_time(model::OptimalControlModel{T, S, C, V}, variable::Variable)::Time) where {
187+
T<:TimesModel{<:AbstractTimeModel, FreeTimeModel},
188+
S<:AbstractStateModel,
189+
C<:AbstractControlModel,
190+
V<:AbstractVariableModel} = final_time(times(model), variable)
191+
initial_time_name(model::OptimalControlModel)::String = name(initial(times(model)))
192+
final_time_name(model::OptimalControlModel)::String = name(final(times(model)))

0 commit comments

Comments
 (0)