Skip to content

Commit

Permalink
add val equiv lb=ub
Browse files Browse the repository at this point in the history
  • Loading branch information
ocots committed Aug 5, 2024
1 parent b3f2c37 commit c0e6581
Show file tree
Hide file tree
Showing 2 changed files with 257 additions and 26 deletions.
21 changes: 15 additions & 6 deletions src/model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -553,13 +553,22 @@ function constraint!(
rg::Union{OrdinalRange{<:Integer}, Index, Integer, Nothing}=nothing,
f::Union{Function, Nothing}=nothing,
lb::Union{ctVector, Nothing}=nothing,
ub::Union{ctVector, Nothing}=nothing,
ub::Union{ctVector, Nothing}=nothing,
val::Union{ctVector, Nothing}=nothing,
label::Symbol=__constraint_label()) where {T <: TimeDependence, V <: VariableDependence}

__check_all_set(ocp)
type == :variable && is_fixed(ocp) && throw(UnauthorizedCall("the ocp has no variable" * ", you cannot use constraint! function with type=:variable."))
label constraints_labels(ocp) && throw(UnauthorizedCall("the constraint named " * String(label) * " already exists."))
isnothing(lb) && isnothing(ub) && throw(UnauthorizedCall("Calling the constraint! function without any bounds is not authorized."))
isnothing(val) && isnothing(lb) && isnothing(ub) && throw(UnauthorizedCall("Calling the constraint! function without any bounds is not authorized."))

# value for equality constraint
# if val is not nothing then lb and ub should be nothing
!isnothing(val) && ( !isnothing(lb) || !isnothing(ub) ) && throw(UnauthorizedCall("If val is provided then lb and ub must not be given."))
if !isnothing(val)
lb = val
ub = val
end

# bounds
isnothing(lb) && (lb = -Inf*(size(ub,1) == 1 ? 1 : ones(eltype(ub), size(ub,1))))
Expand All @@ -578,13 +587,13 @@ function constraint!(
(::Nothing, ::Nothing, ::ctVector, ::ctVector) => begin
if type [:initial, :final, :state]
rg = n == 1 ? Index(1) : 1:n
txt = "the lower bound `lb` and the upper bound `ub` must be of dimension $n"
txt = "the lower bound `lb`, the upper bound `ub` and the value `val` must be of dimension $n"
elseif type == :control
rg = m == 1 ? Index(1) : 1:m
txt = "the lower bound `lb` and the upper bound `ub` must be of dimension $m"
txt = "the lower bound `lb`, the upper bound `ub` and the value `val` must be of dimension $m"
elseif type == :variable
rg = q == 1 ? Index(1) : 1:q
txt = "the lower bound `lb` and the upper bound `ub` must be of dimension $q"
txt = "the lower bound `lb`, the upper bound `ub` and the value `val` must be of dimension $q"
else
throw(IncorrectArgument("the following type of constraint is not valid: " * String(type) *
". Please choose in [ :initial, :final, :control, :state, :variable ] or check the arguments of the constraint! method."))
Expand All @@ -594,7 +603,7 @@ function constraint!(
constraint!(ocp, type; rg=rg, lb=lb, ub=ub, label=label) end

(::RangeConstraint, ::Nothing, ::ctVector, ::ctVector) => begin
txt = "the range `rg`, the lower bound `lb` and the upper bound `ub` must have the same dimension"
txt = "the range `rg`, the lower bound `lb`, the upper bound `ub` and the value `val` must have the same dimension"
(length(rg) != length(lb)) && throw(IncorrectArgument(txt))
(length(rg) != length(ub)) && throw(IncorrectArgument(txt))
# check if the range is valid
Expand Down
262 changes: 242 additions & 20 deletions test/test_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -877,16 +877,16 @@ 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
# dimensions (not set yet)
@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)
Expand Down Expand Up @@ -962,16 +962,16 @@ 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
# dimensions (not set yet)
@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)
Expand Down Expand Up @@ -1027,6 +1027,228 @@ end

end


@testset "val vs lb and ub, errors" begin

ocp = Model(variable=true)

time!(ocp; t0=0, tf=1)
variable!(ocp, 4)
state!(ocp, 2)
control!(ocp, 1)

# error val with ub
@test_throws UnauthorizedCall constraint!(ocp, :initial; rg=Index(2), val=10, ub=10, label=:ci)
@test_throws UnauthorizedCall constraint!(ocp, :final; rg=Index(1), val=1, ub=1, label=:cf)
@test_throws UnauthorizedCall constraint!(ocp, :control; val=0, ub=0, label=:cu)
@test_throws UnauthorizedCall constraint!(ocp, :state; val=[0, 1], ub=[0, 1], label=:cs)
@test_throws UnauthorizedCall constraint!(ocp, :boundary; f=(x0, xf, v) -> x0[2]+xf[2]+v[1], val=2, ub=2, label=:cb)
@test_throws UnauthorizedCall constraint!(ocp, :control; f=(u, v) -> u+v[2], val=20, ub=20, label=:cuu)
@test_throws UnauthorizedCall constraint!(ocp, :state; f=(x, v) -> x+v[1:2], val=[100, 101], ub=[100, 101], label=:css)
@test_throws UnauthorizedCall constraint!(ocp, :mixed; f=(x, u, v) -> x[1]+u+v[2], val=-1, ub=-1, label=:cm)
@test_throws UnauthorizedCall constraint!(ocp, :variable; val=[ 5, 5, 5, 5 ], ub=[ 5, 5, 5, 5 ], label=:cv1)
@test_throws UnauthorizedCall constraint!(ocp, :variable; rg=1:2, val=[ 10, 20 ], ub=[ 10, 20 ], label=:cv2)
@test_throws UnauthorizedCall constraint!(ocp, :variable; rg=Index(3), val=1000, ub=1000, label=:cv3)
@test_throws UnauthorizedCall constraint!(ocp, :variable; f=v -> v[3]^2, val=-10, ub=-10, label=:cv4)


ocp = Model(variable=true)

time!(ocp; t0=0, tf=1)
variable!(ocp, 4)
state!(ocp, 2)
control!(ocp, 1)

# error val with lb
@test_throws UnauthorizedCall constraint!(ocp, :initial; rg=Index(2), val=10, lb=10, label=:ci)
@test_throws UnauthorizedCall constraint!(ocp, :final; rg=Index(1), val=1, lb=1, label=:cf)
@test_throws UnauthorizedCall constraint!(ocp, :control; val=0, lb=0, label=:cu)
@test_throws UnauthorizedCall constraint!(ocp, :state; val=[0, 1], lb=[0, 1], label=:cs)
@test_throws UnauthorizedCall constraint!(ocp, :boundary; f=(x0, xf, v) -> x0[2]+xf[2]+v[1], val=2, lb=2, label=:cb)
@test_throws UnauthorizedCall constraint!(ocp, :control; f=(u, v) -> u+v[2], val=20, lb=20, label=:cuu)
@test_throws UnauthorizedCall constraint!(ocp, :state; f=(x, v) -> x+v[1:2], val=[100, 101], lb=[100, 101], label=:css)
@test_throws UnauthorizedCall constraint!(ocp, :mixed; f=(x, u, v) -> x[1]+u+v[2], val=-1, lb=-1, label=:cm)
@test_throws UnauthorizedCall constraint!(ocp, :variable; val=[ 5, 5, 5, 5 ], lb=[ 5, 5, 5, 5 ], label=:cv1)
@test_throws UnauthorizedCall constraint!(ocp, :variable; rg=1:2, val=[ 10, 20 ], lb=[ 10, 20 ], label=:cv2)
@test_throws UnauthorizedCall constraint!(ocp, :variable; rg=Index(3), val=1000, lb=1000, label=:cv3)
@test_throws UnauthorizedCall constraint!(ocp, :variable; f=v -> v[3]^2, val=-10, lb=-10, label=:cv4)

end

@testset "val vs lb and ub, 1/2" begin

ocp = Model(variable=true)

time!(ocp; t0=0, tf=1)
variable!(ocp, 4)
state!(ocp, 2)
control!(ocp, 1)

constraint!(ocp, :initial; rg=Index(2), lb=10, ub=10, label=:ci)
constraint!(ocp, :final; rg=Index(1), lb=1, ub=1, label=:cf)
constraint!(ocp, :control; lb=0, ub=0, label=:cu)
constraint!(ocp, :state; lb=[0, 1], ub=[0, 1], label=:cs)
constraint!(ocp, :boundary; f=(x0, xf, v) -> x0[2]+xf[2]+v[1], lb=2, ub=2, label=:cb)
constraint!(ocp, :control; f=(u, v) -> u+v[2], lb=20, ub=20, label=:cuu)
constraint!(ocp, :state; f=(x, v) -> x+v[1:2], lb=[100, 101], ub=[100, 101], label=:css)
constraint!(ocp, :mixed; f=(x, u, v) -> x[1]+u+v[2], lb=-1, ub=-1, label=:cm)
constraint!(ocp, :variable; lb=[ 5, 5, 5, 5 ], ub=[ 5, 5, 5, 5 ], label=:cv1)
constraint!(ocp, :variable; rg=1:2, lb=[ 10, 20 ], ub=[ 10, 20 ], label=:cv2)
constraint!(ocp, :variable; rg=Index(3), lb=1000, ub=1000, label=:cv3)
constraint!(ocp, :variable; f=v -> v[3]^2, lb=-10, ub=-10, label=:cv4)

# dimensions (not set yet)
@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)

v = [ 1, 2, 3, 4 ]

# control
@test sort(ξl) == sort([20])
@test sort(ξu) == sort([20])
@test sort(ξ(-1, 1, v)) == sort([ 1+v[2] ])

# state
@test sort(ηl) == sort([100, 101])
@test sort(ηu) == sort([100, 101])
@test sort(η(-1, [1, 1], v)) == sort([1, 1]+v[1:2])

# mixed
@test sort(ψl) == sort([-1])
@test sort(ψu) == sort([-1])
@test sort(ψ(-1, [1, 1], 2, v)) == sort([ 3+v[2] ])

# boundary
@test sort(ϕl) == sort([10, 1, 2])
@test sort(ϕu) == sort([10, 1, 2])
@test sort(ϕ([1, 3], [4, 100], v)) == sort([ 3, 4, 103+v[1] ])

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

# box constraint
@test sort(ul) == sort([0])
@test sort(uind) == sort([1])
@test sort(uu) == sort([0])
@test sort(xl) == sort([0, 1])
@test sort(xind) == sort([1, 2])
@test sort(xu) == sort([0, 1])
@test sort(vl) == sort([ 5, 5, 5, 5, 10, 20, 1000 ])
@test sort(vind) == sort([ 1, 2, 3, 4, 1, 2, 3 ])
@test sort(vu) == sort([ 5, 5, 5, 5, 10, 20, 1000 ])

# 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

@testset "val vs lb and ub, 2/2" begin

ocp = Model(variable=true)

time!(ocp; t0=0, tf=1)
variable!(ocp, 4)
state!(ocp, 2)
control!(ocp, 1)

constraint!(ocp, :initial; rg=Index(2), val=10, label=:ci)
constraint!(ocp, :final; rg=Index(1), val=1, label=:cf)
constraint!(ocp, :control; val=0, label=:cu)
constraint!(ocp, :state; val=[0, 1], label=:cs)
constraint!(ocp, :boundary; f=(x0, xf, v) -> x0[2]+xf[2]+v[1], val=2, label=:cb)
constraint!(ocp, :control; f=(u, v) -> u+v[2], val=20, label=:cuu)
constraint!(ocp, :state; f=(x, v) -> x+v[1:2], val=[100, 101], label=:css)
constraint!(ocp, :mixed; f=(x, u, v) -> x[1]+u+v[2], val=-1, label=:cm)
constraint!(ocp, :variable; val=[ 5, 5, 5, 5 ], label=:cv1)
constraint!(ocp, :variable; rg=1:2, val=[ 10, 20 ], label=:cv2)
constraint!(ocp, :variable; rg=Index(3), val=1000, label=:cv3)
constraint!(ocp, :variable; f=v -> v[3]^2, val=-10, label=:cv4)

# dimensions (not set yet)
@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)

v = [ 1, 2, 3, 4 ]

# control
@test sort(ξl) == sort([20])
@test sort(ξu) == sort([20])
@test sort(ξ(-1, 1, v)) == sort([ 1+v[2] ])

# state
@test sort(ηl) == sort([100, 101])
@test sort(ηu) == sort([100, 101])
@test sort(η(-1, [1, 1], v)) == sort([1, 1]+v[1:2])

# mixed
@test sort(ψl) == sort([-1])
@test sort(ψu) == sort([-1])
@test sort(ψ(-1, [1, 1], 2, v)) == sort([ 3+v[2] ])

# boundary
@test sort(ϕl) == sort([10, 1, 2])
@test sort(ϕu) == sort([10, 1, 2])
@test sort(ϕ([1, 3], [4, 100], v)) == sort([ 3, 4, 103+v[1] ])

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

# box constraint
@test sort(ul) == sort([0])
@test sort(uind) == sort([1])
@test sort(uu) == sort([0])
@test sort(xl) == sort([0, 1])
@test sort(xind) == sort([1, 2])
@test sort(xu) == sort([0, 1])
@test sort(vl) == sort([ 5, 5, 5, 5, 10, 20, 1000 ])
@test sort(vind) == sort([ 1, 2, 3, 4, 1, 2, 3 ])
@test sort(vu) == sort([ 5, 5, 5, 5, 10, 20, 1000 ])

# 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

@testset "objective!" begin

ocp = Model()
Expand Down

0 comments on commit c0e6581

Please sign in to comment.