diff --git a/docs/src/lib/types.md b/docs/src/lib/types.md index 8ae0c445..a0c146c6 100644 --- a/docs/src/lib/types.md +++ b/docs/src/lib/types.md @@ -59,6 +59,7 @@ SecondOrderContinuousSystem SecondOrderConstrainedContinuousSystem LinearParametricContinuousSystem LinearControlParametricContinuousSystem +ConstrainedLinearControlParametricContinuousSystem ``` ## Discrete Systems @@ -98,6 +99,7 @@ SecondOrderDiscreteSystem SecondOrderConstrainedDiscreteSystem LinearParametricDiscreteSystem LinearControlParametricDiscreteSystem +ConstrainedLinearControlParametricDiscreteSystem ``` #### Discretization Algorithms diff --git a/docs/src/man/systems.md b/docs/src/man/systems.md index 852e5866..7e980667 100644 --- a/docs/src/man/systems.md +++ b/docs/src/man/systems.md @@ -101,6 +101,7 @@ However in this table we only included continuous system types for brevity. |SOCLCCS|[`SecondOrderConstrainedLinearControlContinuousSystem`](@ref)| |LPCS|[`LinearParametricContinuousSystem`](@ref)| |LCPCS|[`LinearControlParametricContinuousSystem`](@ref)| +|CLCPCS|[`ConstrainedLinearControlParametricContinuousSystem`](@ref)| The following table summarizes the equation represented by each system type (the names are given in abbreviated form). Again, discrete systems are not included. The column *Input constraints* is `yes` if the structure can model input or noise constraints (or both). @@ -141,3 +142,4 @@ The following table summarizes the equation represented by each system type |``Mx'' + Cx' + f_i(x) = f_e``, x ∈ X, u ∈ U``|yes|yes|SOCCS| |``x' = A(θ)x, θ ∈ Θ``| no|no|LPCS| |``x' = A(θ)x + B(θ)u, θ ∈ Θ``| no|no|LPCS| +|``x' = A(θ)x + B(θ)u, θ ∈ Θ, x ∈ X, u ∈ U``| no|yes|CLCPCS| diff --git a/src/MathematicalSystems.jl b/src/MathematicalSystems.jl index 7050e261..c424bd8d 100644 --- a/src/MathematicalSystems.jl +++ b/src/MathematicalSystems.jl @@ -99,7 +99,9 @@ export ContinuousIdentitySystem, LinearParametricContinuousSystem, LinearParametricDiscreteSystem, LinearControlParametricContinuousSystem, - LinearControlParametricDiscreteSystem + LinearControlParametricDiscreteSystem, + ConstrainedLinearControlParametricContinuousSystem, + ConstrainedLinearControlParametricDiscreteSystem #================================== Concrete Types for Discrete Systems diff --git a/src/macros.jl b/src/macros.jl index f2c25624..dded0fab 100644 --- a/src/macros.jl +++ b/src/macros.jl @@ -343,7 +343,7 @@ function _parse_system(exprs::NTuple{N,Expr}) where {N} # error handling for the given set constraints nsets = length(constraints) - nsets > 3 && throw(ArgumentError("cannot parse $nsets set constraints")) + nsets > 4 && throw(ArgumentError("cannot parse $nsets set constraints")) # error handling for variable names isnothing(state_var) && throw(ArgumentError("the state variable was not found")) @@ -694,18 +694,18 @@ function constructor_input(lhs, rhs, set, parametric) var_names = (rhs_var_names..., lhs_var_names..., set_var_names...) if parametric # strip off normal matrices from expressions (they are just variables) - if length(field_names) == 2 - @assert field_names == (:A, :AS) - field_names = (field_names[2],) - @assert length(var_names) == 2 + if field_names == (:A, :AS) + field_names = (:AS,) @assert var_names == (:A, :AS) var_names = (var_names[2],) - elseif length(field_names) == 4 - @assert field_names == (:A, :B, :AS, :BS) - field_names = (field_names[3], field_names[4]) - @assert length(var_names) == 4 + elseif field_names == (:A, :B, :AS, :BS) @assert (var_names[3], var_names[4]) == (:AS, :BS) + field_names = (:AS, :BS) var_names = (var_names[3], var_names[4]) + elseif field_names == (:A, :B, :X, :U, :AS, :BS) + @assert (var_names[3], var_names[4], var_names[5], var_names[6]) == (:X, :U, :AS, :BS) + field_names = (:AS, :BS, :X, :U) + var_names = (var_names[5], var_names[6], var_names[3], var_names[4]) else throw(ArgumentError("the entry $(field_names) does not match a " * "parametric `MathematicalSystems.jl` structure")) @@ -724,6 +724,10 @@ function extract_set_parameter(expr, state, input, noise, parametric) # input => return Set, :AS elseif @capture(expr, B ∈ Set_) return Set, :BS + elseif @capture(expr, x_ ∈ Set_) && x == state + return Set, :X + elseif @capture(expr, u_ ∈ Set_) && u == input + return Set, :U end elseif @capture(expr, x_ ∈ Set_) if x == state diff --git a/src/systems.jl b/src/systems.jl index bd5456fc..5158ee88 100644 --- a/src/systems.jl +++ b/src/systems.jl @@ -3482,3 +3482,107 @@ for (Z, AZ) in end end end + +@doc """ + ConstrainedLinearControlParametricContinuousSystem + +Continuous-time linear parametric control system with state and input constraints of the form: + +```math + x(t)' = A(θ) x(t) + B(θ) u(t), ; x(t) ∈ X, u(t) \\; ∈ U, θ ∈ \\Theta \\; ∀t +``` + +### Fields + +- `AS` -- parametric state matrix +- `BS` -- parametric input matrix +- `X` -- state constraints +- `U` -- input constraints +""" +ConstrainedLinearControlParametricContinuousSystem + +@doc """ + ConstrainedLinearControlParametricDiscreteSystem + +Discrete-time linear parametric control system with state and input constraints of the form: + +```math + x_{k+1} = A(θ) x_k + B(θ) u_k, \\; x_k ∈ X \\; u_k ∈ U, θ ∈ \\Theta \\; ∀k +``` + +### Fields + +- `AS` -- parametric state matrix +- `BS` -- parametric input matrix +- `X` -- states constraints +- `U` -- input constraints +""" +ConstrainedLinearControlParametricDiscreteSystem + +for (Z, AZ) in + ((:ConstrainedLinearControlParametricContinuousSystem, :AbstractContinuousSystem), + (:ConstrainedLinearControlParametricDiscreteSystem, :AbstractDiscreteSystem)) + @eval begin + struct $(Z){MTA,MTB,XT, UT} <: $(AZ) + AS::MTA + BS::MTB + X::XT + U::UT + + function $(Z)(AS::MTA, BS::MTB, X::XT, U::UT) where {MTA,MTB,XT,UT} + if checksquare(AS) != size(BS, 1) + throw(DimensionMismatch("incompatible dimensions")) + end + return new{MTA,MTB,XT,UT}(AS, BS, X, U) + end + end + + function statedim(s::$Z) + return size(s.AS, 1) + end + function inputdim(s::$Z) + return size(s.BS, 2) + end + function noisedim(::$Z) + return 0 + end + function state_matrix(s::$Z) + return s.AS + end + function input_matrix(s::$Z) + return s.BS + end + function stateset(s::$Z) + return s.X + end + function inputset(s::$Z) + return s.U + end + end + + for T in [Z, Type{<:eval(Z)}] + @eval begin + function islinear(::$T) + return true + end + function isaffine(::$T) + return true + end + function ispolynomial(::$T) + return false + end + function isnoisy(::$T) + return false + end + function iscontrolled(::$T) + return true + end + function isconstrained(::$T) + return true + end + function isparametric(::$T) + return true + end + end + end +end diff --git a/test/@ivp.jl b/test/@ivp.jl index c9fa2e15..30de6a8f 100644 --- a/test/@ivp.jl +++ b/test/@ivp.jl @@ -11,3 +11,19 @@ P3 = @ivp(@system(x' = -x), x(0) ∈ Interval(-1.0, 1.0)) @test P3 == testSystem end + +@testset "@ivp for constrained parametric control systems" begin + @static if isdefined(@__MODULE__, :LazySets) + AS = rand(MatrixZonotope) + BS = rand(MatrixZonotope) + U = BallInf(zeros(1), 1.0) + X = BallInf(zeros(1), 1.0) + X0 = Interval(-1.0, 1.0) + + P = @ivp(x' = A * x + B * u, x(0) ∈ X0, x ∈ X, u ∈ U, A ∈ AS, B ∈ BS) + @test P == InitialValueProblem(ConstrainedLinearControlParametricContinuousSystem(AS, BS, + X, U), X0) + @test inputset(system(P)) == U + end +end + diff --git a/test/@system.jl b/test/@system.jl index a719fb3b..757c96da 100644 --- a/test/@system.jl +++ b/test/@system.jl @@ -203,6 +203,9 @@ end BS = AS sys = @system(x' = A * x + B * u, A ∈ AS, B ∈ BS) @test sys == LinearControlParametricContinuousSystem(AS, BS) + + sys = @system(x' = A * x + B * u, x ∈ X, u ∈ U, A ∈ AS, B ∈ BS) + @test sys == ConstrainedLinearControlParametricContinuousSystem(AS, BS, X, U) end end @@ -320,6 +323,9 @@ end BS = AS sys = @system(x⁺ = A * x + B * u, A ∈ AS, B ∈ BS) @test sys == LinearControlParametricDiscreteSystem(AS, BS) + + sys = @system(x⁺ = A * x + B * u, x ∈ X, u ∈ U, A ∈ AS, B ∈ BS) + @test sys == ConstrainedLinearControlParametricDiscreteSystem(AS, BS, X, U) end end diff --git a/test/continuous.jl b/test/continuous.jl index 1b889f92..eee2dfca 100644 --- a/test/continuous.jl +++ b/test/continuous.jl @@ -793,7 +793,7 @@ end @testset "Linear parametric continuous systems" begin @static if isdefined(@__MODULE__, :LazySets) - using LazySets: MatrixZonotope + using LazySets: MatrixZonotope, Zonotope Ac = [1.0 0.0; 0.0 1.0] A1 = [0.1 0.0; 0.0 0.0] @@ -840,5 +840,25 @@ end @test iscontrolled(sc) @test !isnoisy(sc) @test !isconstrained(sc) + + X = Zonotope([0.0, 0.0], Matrix{Float64}(I, 2, 2)) + U = Zonotope([0.0], Matrix{Float64}(I, 1, 1)) + + scc = ConstrainedLinearControlParametricContinuousSystem(A, B, X, U) + @test scc isa ConstrainedLinearControlParametricContinuousSystem + + @test statedim(scc) == 2 + @test inputdim(scc) == 1 + @test noisedim(scc) == 0 + @test state_matrix(scc) === A + @test input_matrix(scc) === B + @test stateset(scc) === X + @test inputset(scc) === U + @test islinear(scc) + @test isparametric(scc) + @test isaffine(scc) + @test iscontrolled(scc) + @test isconstrained(scc) + @test !isnoisy(scc) end end diff --git a/test/discrete.jl b/test/discrete.jl index c14a94d0..aac327c2 100644 --- a/test/discrete.jl +++ b/test/discrete.jl @@ -561,5 +561,25 @@ end @test iscontrolled(sc) @test !isnoisy(sc) @test !isconstrained(sc) + + X = Zonotope([0.0, 0.0], Matrix{Float64}(I, 2, 2)) + U = Zonotope([0.0], Matrix{Float64}(I, 1, 1)) + + scd = ConstrainedLinearControlParametricDiscreteSystem(A, B, X, U) + @test scd isa ConstrainedLinearControlParametricDiscreteSystem + + @test statedim(scd) == 2 + @test inputdim(scd) == 1 + @test noisedim(scd) == 0 + @test state_matrix(scd) === A + @test input_matrix(scd) === B + @test stateset(scd) === X + @test inputset(scd) === U + @test islinear(scd) + @test isparametric(scd) + @test isaffine(scd) + @test iscontrolled(scd) + @test isconstrained(scd) + @test !isnoisy(scd) end end diff --git a/test/discretize.jl b/test/discretize.jl index 6d0f44d2..9a5d6222 100644 --- a/test/discretize.jl +++ b/test/discretize.jl @@ -38,7 +38,7 @@ end !occursin("Descriptor", string(x)), subtypes(AbstractContinuousSystem)) # this test doesn't apply for second order systems and parametric systems - filter!(x -> x ∉ SECOND_ORDER_CTYPES && x ∉ PARAMETRIC_CTYPES, CTYPES) + filter!(x -> x ∉ SECOND_ORDER_CTYPES && !isparametric(x), CTYPES) DTYPES = MathematicalSystems._complementary_type.(CTYPES) n_types = length(CTYPES) @@ -79,7 +79,7 @@ end !occursin("Descriptor", string(x)), subtypes(AbstractContinuousSystem)) # this test doesn't apply for second order systems and parametric systems - filter!(x -> x ∉ SECOND_ORDER_CTYPES && x ∉ PARAMETRIC_CTYPES, CTYPES) + filter!(x -> x ∉ SECOND_ORDER_CTYPES && !isparametric(x), CTYPES) DTYPES = MathematicalSystems._complementary_type.(CTYPES) n_types = length(CTYPES)