Skip to content

Commit

Permalink
Merge pull request #169 from control-toolbox/167-update-nlp_constraints
Browse files Browse the repository at this point in the history
167 update nlp constraints
  • Loading branch information
ocots authored Jun 23, 2024
2 parents 6f49f3f + f2dccec commit 2798385
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 41 deletions.
5 changes: 3 additions & 2 deletions src/CTBase.jl
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,9 @@ export Model
export __OCPModel # redirection to Model to avoid confusion with other Model functions from other packages. Due to @def macro
export variable!, time!, constraint!, dynamics!, objective!, state!, control!, remove_constraint!, constraint
export is_autonomous, is_fixed, is_time_independent, is_time_dependent, is_min, is_max, is_variable_dependent, is_variable_independent
export nlp_constraints, constraints_labels
export nlp_constraints!, constraints_labels
export has_free_final_time, has_free_initial_time, has_lagrange_cost, has_mayer_cost
export dim_control_constraints, dim_state_constraints, dim_mixed_constraints, dim_path_constraints, dim_boundary_constraints, dim_variable_constraints, dim_control_range, dim_state_range, dim_variable_range, dim_control_box, dim_state_box, dim_variable_box

# solution
export OptimalControlSolution
Expand All @@ -273,4 +274,4 @@ export @def
export ct_repl, ct_repl_update_model
isdefined(Base, :active_repl) && ct_repl()

end
end
128 changes: 99 additions & 29 deletions src/model.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# todo: use design pattern to generate functions in nlp_constraints!
"""
$(TYPEDSIGNATURES)
Expand Down Expand Up @@ -865,18 +866,21 @@ Return a 6-tuple of tuples:
- `(xl, xind, xu)` are state linear constraints of a subset of indices
- `(vl, vind, vu)` are variable linear constraints of a subset of indices
and update information about constraints dimensions of `ocp`.
!!! note
- The dimensions of the state and control must be set before calling `nlp_constraints`.
- The dimensions of the state and control must be set before calling `nlp_constraints!`.
- For a `Fixed` problem, dimensions associated with constraints on the variable are set to zero.
# Example
```jldoctest
julia> (ξl, ξ, ξu), (ηl, η, ηu), (ψl, ψ, ψu), (ϕl, ϕ, ϕu), (θl, θ, θu),
(ul, uind, uu), (xl, xind, xu), (vl, vind, vu) = nlp_constraints(ocp)
(ul, uind, uu), (xl, xind, xu), (vl, vind, vu) = nlp_constraints!(ocp)
```
"""
function nlp_constraints(ocp::OptimalControlModel)
function nlp_constraints!(ocp::OptimalControlModel)

# we check if the dimensions and times have been set
__check_all_set(ocp)
Expand Down Expand Up @@ -965,12 +969,6 @@ function nlp_constraints(ocp::OptimalControlModel)
return val
end

function (t, x, v) # nonlinear state constraints
val = Vector{ctNumber}()
for i 1:length(ηf) append!(val, ηf[i](t, x, v)) end
return val
end

function ψ(t, x, u, v) # nonlinear mixed constraints
dim = length(ψl)
val = zeros(ctNumber, dim)
Expand All @@ -984,13 +982,7 @@ function nlp_constraints(ocp::OptimalControlModel)
return val
end

function (t, x, u, v) # nonlinear mixed constraints
val = Vector{ctNumber}()
for i 1:length(ψf) append!(val, ψf[i](t, x, u, v)) end
return val
end

function ϕ(x0, xf, v) # nonlinear mixed constraints
function ϕ(x0, xf, v) # nonlinear boundary constraints
dim = length(ϕl)
val = zeros(ctNumber, dim)
j = 1
Expand All @@ -1003,13 +995,7 @@ function nlp_constraints(ocp::OptimalControlModel)
return val
end

function (x0, xf, v) # nonlinear boundary constraints
val = Vector{ctNumber}()
for i 1:length(ϕf) append!(val, ϕf[i](x0, xf, v)) end
return val
end

function θ(v) # nonlinear mixed constraints
function θ(v) # nonlinear variable constraints
dim = length(θl)
val = zeros(ctNumber, dim)
j = 1
Expand All @@ -1022,12 +1008,96 @@ function nlp_constraints(ocp::OptimalControlModel)
return val
end

function (v) # nonlinear variable constraints
val = Vector{ctNumber}()
for i 1:length(θf) append!(val, θf[i](v)) end
return val
end
ocp.dim_control_constraints = length(ξl)
ocp.dim_state_constraints = length(ηl)
ocp.dim_mixed_constraints = length(ψl)
ocp.dim_boundary_constraints = length(ϕl)
ocp.dim_variable_constraints = length(θl)
ocp.dim_control_range = length(ul)
ocp.dim_state_range = length(xl)
ocp.dim_variable_range = length(vl)

return (ξl, ξ, ξu), (ηl, η, ηu), (ψl, ψ, ψu), (ϕl, ϕ, ϕu), (θl, θ, θu), (ul, uind, uu), (xl, xind, xu), (vl, vind, vu)

end
end


"""
$(TYPEDSIGNATURES)
Return the dimension of nonlinear state constraints (`nothing` if not knonw).
Information is updated after `nlp_constraints!` is called.
"""
dim_state_constraints(ocp::OptimalControlModel) = ocp.dim_state_constraints

"""
$(TYPEDSIGNATURES)
Return the dimension of nonlinear control constraints (`nothing` if not knonw).
Information is updated after `nlp_constraints!` is called.
"""
dim_control_constraints(ocp::OptimalControlModel) = ocp.dim_control_constraints

"""
$(TYPEDSIGNATURES)
Return the dimension of nonlinear mixed constraints (`nothing` if not knonw).
Information is updated after `nlp_constraints!` is called.
"""
dim_mixed_constraints(ocp::OptimalControlModel) = ocp.dim_mixed_constraints

"""
$(TYPEDSIGNATURES)
Return the dimension of nonlinear path (state + control + mixed) constraints (`nothing` if one of them is not knonw).
Information is updated after `nlp_constraints!` is called.
"""
function dim_path_constraints(ocp::OptimalControlModel)
isnothing(ocp.dim_control_constraints) && return nothing
isnothing(ocp.dim_state_constraints) && return nothing
isnothing(ocp.dim_mixed_constraints) && return nothing
return ocp.dim_state_constraints + ocp.dim_control_constraints + ocp.dim_mixed_constraints
end

"""
$(TYPEDSIGNATURES)
Return the dimension of the boundary constraints (`nothing` if not knonw).
Information is updated after `nlp_constraints!` is called.
"""
dim_boundary_constraints(ocp::OptimalControlModel) = ocp.dim_boundary_constraints

"""
$(TYPEDSIGNATURES)
Return the dimension of nonlinear variable constraints (`nothing` if not knonw).
Information is updated after `nlp_constraints!` is called.
"""
dim_variable_constraints(ocp::OptimalControlModel) = ocp.dim_variable_constraints

"""
$(TYPEDSIGNATURES)
Return the dimension of range constraints on state (`nothing` if not knonw).
Information is updated after `nlp_constraints!` is called.
"""
dim_state_range(ocp::OptimalControlModel) = ocp.dim_state_range
dim_state_box = dim_state_range # alias, CTDirect.jl compatibility

"""
$(TYPEDSIGNATURES)
Return the dimension of range constraints on control (`nothing` if not knonw).
Information is updated after `nlp_constraints!` is called.
"""
dim_control_range(ocp::OptimalControlModel) = ocp.dim_control_range
dim_control_box = dim_control_range # alias, CTDirect.jl compatibility

"""
$(TYPEDSIGNATURES)
Return the dimension of range constraints on variable (`nothing` if not knonw).
Information is updated after `nlp_constraints!` is called.
"""
dim_variable_range(ocp::OptimalControlModel) = ocp.dim_variable_range
dim_variable_box = dim_variable_range # alias, CTDirect.jl compatibility
2 changes: 1 addition & 1 deletion src/print.jl
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ function Base.show(io::IO, ::MIME"text/plain", ocp::OptimalControlModel{<: TimeD
println(io, "")

# other constraints: control, state, mixed, boundary, bounds on u, bounds on x
(ξl, ξ, ξu), (ηl, η, ηu), (ψl, ψ, ψu), (ϕl, ϕ, ϕu), (ulb, uind, uub), (xlb, xind, xub) = nlp_constraints(ocp)
(ξl, ξ, ξu), (ηl, η, ηu), (ψl, ψ, ψu), (ϕl, ϕ, ϕu), (ulb, uind, uub), (xlb, xind, xub) = nlp_constraints!(ocp)
has_constraints = false
if !isempty(ξl) || !isempty(ulb)
has_constraints = true
Expand Down
8 changes: 8 additions & 0 deletions src/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,14 @@ $(TYPEDFIELDS)
criterion::Union{Symbol,Nothing}=nothing
dynamics::Union{Dynamics,Nothing}=nothing
constraints::Dict{Symbol, Tuple{Vararg{Any}}}=Dict{Symbol, Tuple{Vararg{Any}}}()
dim_control_constraints::Union{Dimension,Nothing}=nothing
dim_state_constraints::Union{Dimension,Nothing}=nothing
dim_mixed_constraints::Union{Dimension,Nothing}=nothing
dim_boundary_constraints::Union{Dimension,Nothing}=nothing
dim_variable_constraints::Union{Dimension,Nothing}=nothing
dim_control_range::Union{Dimension,Nothing}=nothing
dim_state_range::Union{Dimension,Nothing}=nothing
dim_variable_range::Union{Dimension,Nothing}=nothing
end

# used for checkings
Expand Down
62 changes: 53 additions & 9 deletions test/test_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -859,7 +859,7 @@ end

end

@testset "nlp_constraints without variable" begin
@testset "nlp_constraints! without variable" begin

ocp = Model(); __time!(ocp, 0, 1); state!(ocp, 2); control!(ocp, 1)
__constraint!(ocp, :initial, Index(2), 10, 10, :ci)
Expand All @@ -871,8 +871,19 @@ end
__constraint!(ocp, :state, x->x, [0, 1], [1, 2], :css)
__constraint!(ocp, :mixed, (x,u)->x[1]+u, 1, 1, :cm)

# dimensions (not yet set)
@test dim_control_constraints(ocp) == nothing
@test dim_state_constraints(ocp) == nothing
@test dim_mixed_constraints(ocp) == nothing
@test dim_path_constraints(ocp) == nothing
@test dim_boundary_constraints(ocp) == nothing
@test dim_variable_constraints(ocp) == nothing
@test dim_control_range(ocp) == nothing
@test dim_state_range(ocp) == nothing
@test dim_variable_range(ocp) == nothing

(ξl, ξ, ξu), (ηl, η, ηu), (ψl, ψ, ψu), (ϕl, ϕ, ϕu), (θl, θ, θu),
(ul, uind, uu), (xl, xind, xu), (vl, vind, vu) = nlp_constraints(ocp)
(ul, uind, uu), (xl, xind, xu), (vl, vind, vu) = nlp_constraints!(ocp)

v = Real[]

Expand Down Expand Up @@ -912,9 +923,20 @@ end
@test sort(θu) == sort([ ])
@test sort(θ(v)) == sort([ ])

# dimensions (set)
@test dim_control_constraints(ocp) == 1
@test dim_state_constraints(ocp) == 2
@test dim_mixed_constraints(ocp) == 1
@test dim_path_constraints(ocp) == 4
@test dim_boundary_constraints(ocp) == 3
@test dim_variable_constraints(ocp) == 0
@test dim_control_range(ocp) == 1
@test dim_state_range(ocp) == 2
@test dim_variable_range(ocp) == 0

end

@testset "nlp_constraints with variable" begin
@testset "nlp_constraints! with variable" begin

ocp = Model(variable=true)
__time!(ocp, 0, 1)
Expand All @@ -934,8 +956,19 @@ end
__constraint!(ocp, :variable, Index(3), 2, 3, :cv3)
__constraint!(ocp, :variable, v -> v[3]^2, 0, 1, :cv4)

# dimensions (not yet set)
@test dim_control_constraints(ocp) == nothing
@test dim_state_constraints(ocp) == nothing
@test dim_mixed_constraints(ocp) == nothing
@test dim_path_constraints(ocp) == nothing
@test dim_boundary_constraints(ocp) == nothing
@test dim_variable_constraints(ocp) == nothing
@test dim_control_range(ocp) == nothing
@test dim_state_range(ocp) == nothing
@test dim_variable_range(ocp) == nothing

(ξl, ξ, ξu), (ηl, η, ηu), (ψl, ψ, ψu), (ϕl, ϕ, ϕu), (θl, θ, θu),
(ul, uind, uu), (xl, xind, xu), (vl, vind, vu) = nlp_constraints(ocp)
(ul, uind, uu), (xl, xind, xu), (vl, vind, vu) = nlp_constraints!(ocp)

v = [ 1, 2, 3, 4 ]

Expand All @@ -959,21 +992,32 @@ end
@test sort(ϕu) == sort([10, 1, 1])
@test sort(ϕ([1, 3], [4, 100], v)) == sort([ 3, 4, 103+v[1] ])

# variable
@test sort(θl) == sort([ 0 ])
@test sort(θu) == sort([ 1 ])
@test sort(θ(v)) == sort([ v[3]^2 ])

# box constraint
@test sort(ul) == sort([0])
@test sort(uind) == sort([1])
@test sort(uu) == sort([1])
@test sort(xl) == sort([0, 1])
@test sort(xind) == sort([1, 2])
@test sort(xu) == sort([1, 2])

# variable
@test sort(vl) == sort([ 0, 0, 0, 0, 1, 2, 2 ])
@test sort(vind) == sort([ 1, 2, 3, 4, 1, 2, 3 ])
@test sort(vu) == sort([ 5, 5, 5, 5, 3, 4, 3 ])
@test sort(θl) == sort([ 0 ])
@test sort(θu) == sort([ 1 ])
@test sort(θ(v)) == sort([ v[3]^2 ])

# dimensions
@test dim_control_constraints(ocp) == 1
@test dim_state_constraints(ocp) == 2
@test dim_mixed_constraints(ocp) == 1
@test dim_path_constraints(ocp) == 4
@test dim_boundary_constraints(ocp) == 3
@test dim_variable_constraints(ocp) == 1
@test dim_control_range(ocp) == 1
@test dim_state_range(ocp) == 2
@test dim_variable_range(ocp) == 7

end

Expand Down

0 comments on commit 2798385

Please sign in to comment.