Skip to content

Commit 4159e1d

Browse files
committed
Implementing coupling with the new descriptor system types.
1 parent 923e414 commit 4159e1d

File tree

4 files changed

+99
-43
lines changed

4 files changed

+99
-43
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
## Compatibility
1212

13-
Julia 1.8 and higher.
13+
Julia 1.10 and higher.
1414

1515
## How to install
1616

ReleaseNotes.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ Minor release which provides extended functionality covering sparse matrix model
77
This release relies on the newly implemented generic functions available in the version `v1.8.9` of the
88
[`MatrixPencils.jl`](https://github.com/andreasvarga/MatrixPencils.jl) package.
99

10-
TODO: Implement basic connections for sparse matrix models.
11-
1210
## Version 1.4.4
1311

1412
Some fixes to handle structured and sparse matrices in the `dss` function.

src/connections.jl

Lines changed: 92 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ or with UniformScalings is also supported.
99
Series coupling with a constant is equivalent to elementwise multiplication of
1010
the transfer function matrix with the constant.
1111
"""
12-
series(sys1::DescriptorStateSpace, sys2::DescriptorStateSpace) = sys2*sys1
13-
series(sys1::DescriptorStateSpace, sys2::Union{AbstractNumOrArray,UniformScaling}) = sys2*sys1
14-
series(sys1::Union{AbstractNumOrArray,UniformScaling}, sys2::DescriptorStateSpace) = sys2*sys1
12+
series(sys1::DSTYPE, sys2::DSTYPE) = sys2*sys1
13+
series(sys1::DSTYPE, sys2::Union{AbstractNumOrArray,UniformScaling}) = sys2*sys1
14+
series(sys1::Union{AbstractNumOrArray,UniformScaling}, sys2::DSTYPE) = sys2*sys1
1515

1616
"""
1717
sys = parallel(sys1, sys2)
@@ -24,9 +24,9 @@ or with UniformScalings is also supported.
2424
Parallel coupling with a constant is equivalent to elementwise parallel coupling of
2525
the transfer function matrix with the constant.
2626
"""
27-
parallel(sys1::DescriptorStateSpace, sys2::DescriptorStateSpace) = sys1 + sys2
28-
parallel(sys1::DescriptorStateSpace, sys2::Union{AbstractNumOrArray,UniformScaling}) = sys1 + sys2
29-
parallel(sys1::Union{AbstractNumOrArray,UniformScaling},sys2::DescriptorStateSpace) = sys1 + sys2
27+
parallel(sys1::DSTYPE, sys2::DSTYPE) = sys1 + sys2
28+
parallel(sys1::DSTYPE, sys2::Union{AbstractNumOrArray,UniformScaling}) = sys1 + sys2
29+
parallel(sys1::Union{AbstractNumOrArray,UniformScaling},sys2::DSTYPE) = sys1 + sys2
3030
"""
3131
sys = append(systems...)
3232
@@ -35,7 +35,7 @@ of individual systems. This corresponds to the block diagonal concatenation of
3535
their transfer function matrices.
3636
Appending systems with constant matrices, vectors or scalars or with UniformScalings is also supported.
3737
"""
38-
function append(systems::DescriptorStateSpace...)
38+
function append(systems::DSTYPE...)
3939
T = promote_type(eltype.(systems)...)
4040
Ts = systems[1].Ts
4141
if !all(s.Ts == Ts for s in systems)
@@ -54,11 +54,14 @@ function append(systems::DescriptorStateSpace...)
5454
B = blockdiag([s.B for s in systems]...)
5555
C = blockdiag([s.C for s in systems]...)
5656
D = blockdiag([s.D for s in systems]...)
57-
return DescriptorStateSpace{T}(A, E, B, C, D, Ts)
57+
if issparse(A)
58+
return SparseDescriptorStateSpace{T}(A, E, B, C, D, Ts)
59+
else
60+
return DescriptorStateSpace{T}(A, E, B, C, D, Ts)
61+
end
5862
end
5963

60-
61-
function append(A::Union{DescriptorStateSpace,AbstractNumOrArray,UniformScaling}...)
64+
function append(A::Union{DSTYPE,AbstractNumOrArray,UniformScaling}...)
6265
for a in A
6366
isa(a, UniformScaling) && @warn "All UniformScaling objects in append are set to scalars"
6467
require_one_based_indexing(a)
@@ -95,7 +98,36 @@ end
9598
# return DescriptorStateSpace{T}(A, E, B, C, D, Ts)
9699
# end
97100

98-
function hcat(SYS1::DescriptorStateSpace{T1}, SYS2::DescriptorStateSpace{T2}) where {T1<:Number, T2<:Number}
101+
# function hcat(SYS1::DescriptorStateSpace{T1}, SYS2::DescriptorStateSpace{T2}) where {T1<:Number, T2<:Number}
102+
# #function hcat(SYS1 :: DescriptorStateSpace, SYS2 :: DescriptorStateSpace)
103+
# ny = SYS1.ny
104+
# ny == size(SYS2, 1) || error("The systems must have the same output dimension")
105+
# #T = promote_type(eltype(SYS1), eltype(SYS2))
106+
# T = promote_type(T1, T2)
107+
# #TE = promote_type(TE1, TE2)
108+
# TE1 = typeof(SYS1.E)
109+
# TE2 = typeof(SYS2.E)
110+
# TE = promote_Etype(T, TE1, TE2)
111+
# Ts = promote_Ts(SYS1.Ts, SYS2.Ts)
112+
113+
# A = blockdiag(T.(SYS1.A), T.(SYS2.A))
114+
115+
# local E::TE
116+
# if SYS1.E === I && SYS2.E === I
117+
# #if SYS1.E == I && SYS2.E == I
118+
# E = I
119+
# elseif SYS1.E == I || SYS2.E == I
120+
# blockdims = [size(SYS1.A,1), size(SYS2.A,1)]
121+
# E = sblockdiag(blockdims, SYS1.E == I ? SYS1.E : T.(SYS1.E), SYS2.E == I ? SYS2.E : T.(SYS2.E))
122+
# else
123+
# E = blockdiag(T.(SYS1.E), T.(SYS2.E))
124+
# end
125+
# B = blockdiag(T.(SYS1.B), T.(SYS2.B))
126+
# C = [ T.(SYS1.C) T.(SYS2.C)]
127+
# D = [ T.(SYS1.D) T.(SYS2.D)]
128+
# return DescriptorStateSpace{T}(A, E, B, C, D, Ts)
129+
# end
130+
function hcat(SYS1::DSTYPE{T1}, SYS2::DSTYPE{T2}) where {T1<:Number, T2<:Number}
99131
#function hcat(SYS1 :: DescriptorStateSpace, SYS2 :: DescriptorStateSpace)
100132
ny = SYS1.ny
101133
ny == size(SYS2, 1) || error("The systems must have the same output dimension")
@@ -122,17 +154,24 @@ function hcat(SYS1::DescriptorStateSpace{T1}, SYS2::DescriptorStateSpace{T2}) wh
122154
B = blockdiag(T.(SYS1.B), T.(SYS2.B))
123155
C = [ T.(SYS1.C) T.(SYS2.C)]
124156
D = [ T.(SYS1.D) T.(SYS2.D)]
125-
return DescriptorStateSpace{T}(A, E, B, C, D, Ts)
157+
#return DescriptorStateSpace{T}(A, E, B, C, D, Ts)
158+
if issparse(A)
159+
return SparseDescriptorStateSpace{T}(A, E, B, C, D, Ts)
160+
else
161+
return DescriptorStateSpace{T}(A, E, B, C, D, Ts)
162+
end
163+
126164
end
127-
hcat(SYS :: DescriptorStateSpace{T}, MAT :: Union{Number, AbstractVecOrMat{<:Number}}) where {T} = hcat(SYS,dss(promote_type(T,eltype(MAT)).(MAT),Ts=SYS.Ts))
128-
hcat(MAT :: Union{Number, AbstractVecOrMat{<:Number}}, SYS :: DescriptorStateSpace{T}) where {T} = hcat(dss(promote_type(T,eltype(MAT)).(MAT),Ts=SYS.Ts),SYS)
129-
hcat(SYS :: DescriptorStateSpace, MAT :: UniformScaling) = hcat(SYS,dss(Matrix{promote_type(eltype(SYS),eltype(MAT))}(MAT,SYS.ny,SYS.ny),Ts=SYS.Ts))
130-
hcat(MAT :: UniformScaling, SYS :: DescriptorStateSpace) = hcat(dss(Matrix{promote_type(eltype(SYS),eltype(MAT))}(MAT,SYS.ny,SYS.ny),Ts=SYS.Ts),SYS)
131165

132-
function isadss(DST :: Union{DescriptorStateSpace,AbstractNumOrArray,UniformScaling}...)
166+
hcat(SYS :: DSTYPE{T}, MAT :: Union{Number, AbstractVecOrMat{<:Number}}) where {T} = hcat(SYS,dss(promote_type(T,eltype(MAT)).(MAT),Ts=SYS.Ts))
167+
hcat(MAT :: Union{Number, AbstractVecOrMat{<:Number}}, SYS :: DSTYPE{T}) where {T} = hcat(dss(promote_type(T,eltype(MAT)).(MAT),Ts=SYS.Ts),SYS)
168+
hcat(SYS :: DSTYPE, MAT :: UniformScaling) = hcat(SYS,dss(Matrix{promote_type(eltype(SYS),eltype(MAT))}(MAT,SYS.ny,SYS.ny),Ts=SYS.Ts))
169+
hcat(MAT :: UniformScaling, SYS :: DSTYPE) = hcat(dss(Matrix{promote_type(eltype(SYS),eltype(MAT))}(MAT,SYS.ny,SYS.ny),Ts=SYS.Ts),SYS)
170+
171+
function isadss(DST :: Union{DSTYPE,AbstractNumOrArray,UniformScaling}...)
133172
# pick the index i of first system
134173
for i = 1:length(DST)
135-
DST[i] isa DescriptorStateSpace && (return i)
174+
DST[i] isa DSTYPE && (return i)
136175
end
137176
# set index to 0 if no system is present
138177
return 0
@@ -149,8 +188,8 @@ concatenation of their transfer function matrices.
149188
Concatenation of systems with constant matrices, vectors, or scalars having the same row dimensions
150189
or with UniformScalings is also supported.
151190
"""
152-
horzcat(systems::Union{DescriptorStateSpace,AbstractNumOrArray,UniformScaling}...) = hcat(systems...)
153-
function Base.hcat(systems::DescriptorStateSpace...)
191+
horzcat(systems::Union{DSTYPE,AbstractNumOrArray,UniformScaling}...) = hcat(systems...)
192+
function Base.hcat(systems::DSTYPE...)
154193
# Perform checks
155194
T = promote_type(eltype.(systems)...)
156195
Ts = systems[1].Ts
@@ -170,7 +209,11 @@ function Base.hcat(systems::DescriptorStateSpace...)
170209
C = hcat([s.C for s in systems]...)
171210
D = hcat([s.D for s in systems]...)
172211

173-
return DescriptorStateSpace{T}(A, E, B, C, D, Ts)
212+
if issparse(A)
213+
return SparseDescriptorStateSpace{T}(A, E, B, C, D, Ts)
214+
else
215+
return DescriptorStateSpace{T}(A, E, B, C, D, Ts)
216+
end
174217
end
175218

176219
"""
@@ -184,9 +227,9 @@ concatenation of their transfer function matrices.
184227
Concatenation of systems with constant matrices, vectors, or scalars having the same column dimensions
185228
or with UniformScalings is also supported.
186229
"""
187-
vertcat(systems::Union{DescriptorStateSpace,AbstractNumOrArray,UniformScaling}...) = vcat(systems...)
230+
vertcat(systems::Union{DSTYPE,AbstractNumOrArray,UniformScaling}...) = vcat(systems...)
188231

189-
function Base.vcat(systems::DescriptorStateSpace...)
232+
function Base.vcat(systems::DSTYPE...)
190233
# Perform checks
191234
T = promote_type(eltype.(systems)...)
192235
Ts = systems[1].Ts
@@ -205,7 +248,11 @@ function Base.vcat(systems::DescriptorStateSpace...)
205248
B = vcat([s.B for s in systems]...)
206249
C = blockdiag([s.C for s in systems]...)
207250
D = vcat([s.D for s in systems]...)
208-
return DescriptorStateSpace{T}(A, E, B, C, D, Ts)
251+
if issparse(A)
252+
return SparseDescriptorStateSpace{T}(A, E, B, C, D, Ts)
253+
else
254+
return DescriptorStateSpace{T}(A, E, B, C, D, Ts)
255+
end
209256
end
210257

211258
# function vcat(SYS1::DescriptorStateSpace{T1, TE1}, SYS2::DescriptorStateSpace{T2, TE2}) where {T1<:Number, TE1<:ETYPE, T2<:Number, TE2<:ETYPE}
@@ -236,7 +283,7 @@ end
236283
# return DescriptorStateSpace{T}(A, E, B, C, D, Ts)
237284
# end
238285

239-
function vcat(SYS1::DescriptorStateSpace{T1}, SYS2::DescriptorStateSpace{T2}) where {T1<:Number, T2<:Number}
286+
function vcat(SYS1::DSTYPE{T1}, SYS2::DSTYPE{T2}) where {T1<:Number, T2<:Number}
240287
#function vcat(SYS1 :: DescriptorStateSpace, SYS2 :: DescriptorStateSpace)
241288
nu = SYS1.nu
242289
nu == size(SYS2, 2) || error("The systems must have the same input dimension")
@@ -263,18 +310,22 @@ function vcat(SYS1::DescriptorStateSpace{T1}, SYS2::DescriptorStateSpace{T2}) wh
263310
C = blockdiag(T.(SYS1.C), T.(SYS2.C))
264311
B = [ T.(SYS1.B); T.(SYS2.B)]
265312
D = [ T.(SYS1.D); T.(SYS2.D)]
266-
return DescriptorStateSpace{T}(A, E, B, C, D, Ts)
313+
if issparse(A)
314+
return SparseDescriptorStateSpace{T}(A, E, B, C, D, Ts)
315+
else
316+
return DescriptorStateSpace{T}(A, E, B, C, D, Ts)
317+
end
267318
end
268319

269-
vcat(SYS :: DescriptorStateSpace{T}, MAT :: Union{Number, AbstractVecOrMat{<:Number}}) where {T} = vcat(SYS,dss(promote_type(T,eltype(MAT)).(MAT),Ts=SYS.Ts))
270-
vcat(MAT :: Union{Number, AbstractVecOrMat{<:Number}}, SYS :: DescriptorStateSpace{T}) where {T} = vcat(dss(promote_type(T,eltype(MAT)).(MAT),Ts=SYS.Ts),SYS)
271-
vcat(SYS :: DescriptorStateSpace, MAT :: UniformScaling) = vcat(SYS,dss(Matrix{promote_type(eltype(SYS),eltype(MAT))}(MAT,SYS.nu,SYS.nu),Ts=SYS.Ts))
272-
vcat(MAT :: UniformScaling, SYS :: DescriptorStateSpace) = vcat(dss(Matrix{promote_type(eltype(SYS),eltype(MAT))}(MAT,SYS.nu,SYS.nu),Ts=SYS.Ts),SYS)
320+
vcat(SYS :: DSTYPE{T}, MAT :: Union{Number, AbstractVecOrMat{<:Number}}) where {T} = vcat(SYS,dss(promote_type(T,eltype(MAT)).(MAT),Ts=SYS.Ts))
321+
vcat(MAT :: Union{Number, AbstractVecOrMat{<:Number}}, SYS :: DSTYPE{T}) where {T} = vcat(dss(promote_type(T,eltype(MAT)).(MAT),Ts=SYS.Ts),SYS)
322+
vcat(SYS :: DSTYPE, MAT :: UniformScaling) = vcat(SYS,dss(Matrix{promote_type(eltype(SYS),eltype(MAT))}(MAT,SYS.nu,SYS.nu),Ts=SYS.Ts))
323+
vcat(MAT :: UniformScaling, SYS :: DSTYPE) = vcat(dss(Matrix{promote_type(eltype(SYS),eltype(MAT))}(MAT,SYS.nu,SYS.nu),Ts=SYS.Ts),SYS)
273324

274325
for (f, _f, dim, name) in ((:hcat, :_hcat, 1, "rows"), (:vcat, :_vcat, 2, "cols"))
275326
@eval begin
276-
@inline $f(A::Union{DescriptorStateSpace, AbstractVecOrMat,UniformScaling,Number}...) = $_f(A...)
277-
function $_f(A::Union{DescriptorStateSpace, AbstractVecOrMat,UniformScaling,Number}...)
327+
@inline $f(A::Union{DSTYPE, AbstractVecOrMat,UniformScaling,Number}...) = $_f(A...)
328+
function $_f(A::Union{DSTYPE, AbstractVecOrMat,UniformScaling,Number}...)
278329
n = -1
279330
for a in A
280331
if !isa(a, UniformScaling)
@@ -287,7 +338,7 @@ for (f, _f, dim, name) in ((:hcat, :_hcat, 1, "rows"), (:vcat, :_vcat, 2, "cols"
287338
end
288339
end
289340
n == -1 && throw(ArgumentError($("$f of only UniformScaling objects cannot determine the matrix size")))
290-
if isnothing(findfirst(a -> isa(a, DescriptorStateSpace), A))
341+
if isnothing(findfirst(a -> isa(a, DSTYPE), A))
291342
# @warn "Type piracy in $(string($f)): to be fixed in Julia 1.8 (make an issue otherwise)"
292343
# alleviate type piracy problematic
293344
n == 1 && (A = promote_to_arrays(A...)) # convert all scalars to vectors
@@ -300,7 +351,7 @@ for (f, _f, dim, name) in ((:hcat, :_hcat, 1, "rows"), (:vcat, :_vcat, 2, "cols"
300351
end
301352
end
302353

303-
function Base.hvcat(rows :: Tuple{Vararg{Int}}, DST :: DescriptorStateSpace...)
354+
function Base.hvcat(rows :: Tuple{Vararg{Int}}, DST :: DSTYPE...)
304355
j2 = rows[1]
305356
sys = hcat(DST[1:j2]...)
306357
for i = 2:length(rows)
@@ -313,8 +364,8 @@ end
313364

314365

315366
#function Base.hvcat(rows::Tuple{Vararg{Int}}, A::Union{DescriptorStateSpace, AbstractVecOrMat{<:Number}, Number, UniformScaling}...)
316-
Base.hvcat(rows::Tuple{Vararg{Int}}, A::Union{DescriptorStateSpace, AbstractVecOrMat, UniformScaling, Number}...) = _hvcat(rows, A...)
317-
function _hvcat(rows::Tuple{Vararg{Int}}, A::Union{DescriptorStateSpace, AbstractVecOrMat, UniformScaling, Number}...)
367+
Base.hvcat(rows::Tuple{Vararg{Int}}, A::Union{DSTYPE, AbstractVecOrMat, UniformScaling, Number}...) = _hvcat(rows, A...)
368+
function _hvcat(rows::Tuple{Vararg{Int}}, A::Union{DSTYPE, AbstractVecOrMat, UniformScaling, Number}...)
318369
require_one_based_indexing(A...)
319370
nr = length(rows)
320371
sum(rows) == length(A) || throw(ArgumentError("mismatch between row sizes and number of arguments"))
@@ -368,7 +419,7 @@ function _hvcat(rows::Tuple{Vararg{Int}}, A::Union{DescriptorStateSpace, Abstrac
368419
j += rows[i]
369420
end
370421
end
371-
if isnothing(findfirst(a -> isa(a, DescriptorStateSpace), A))
422+
if isnothing(findfirst(a -> isa(a, DSTYPE), A))
372423
# alleviate type piracy problematic
373424
# convert all scalars to vectors: not needed in Julia 1.8
374425
# @warn "Type piracy in hvcat: to be fixed in Julia 1.8 (make an issue otherwise)"
@@ -384,7 +435,8 @@ end
384435
# promotion to systems of constant matrices, vectors, scalars and UniformScalings
385436
promote_to_system_(n::Int, ::Type{T}, J::UniformScaling, Ts::Real) where {T} = dss(copyto!(Matrix{T}(undef, n,n), J), Ts = Ts)
386437
promote_to_system_(n::Int, ::Type{T}, A::AbstractNumOrArray, Ts::Real) where {T} = dss(to_matrix(T,A), Ts = Ts)
387-
promote_to_system_(n::Int, ::Type{T}, A::DescriptorStateSpace, Ts::Real) where {T} = T == eltype(A) ? A : dss(dssdata(T,A)..., Ts = Ts)
438+
#promote_to_system_(n::Int, ::Type{T}, A::DescriptorStateSpace, Ts::Real) where {T} = T == eltype(A) ? A : dss(dssdata(T,A)..., Ts = Ts)
439+
promote_to_system_(n::Int, ::Type{T}, A::DSTYPE, Ts::Real) where {T} = T == eltype(A) ? A : dss(dssdata(T,A)..., Ts = Ts)
388440
promote_to_systems(Ts::Real, n, k, ::Type) = ()
389441
promote_to_systems(Ts::Real, n, k, ::Type{T}, A) where {T} = (promote_to_system_(n[k], T, A, Ts),)
390442
promote_to_systems(Ts::Real, n, k, ::Type{T}, A, B) where {T} =
@@ -407,11 +459,11 @@ function promote_to_arrays(A...)
407459
end
408460

409461

410-
function promote_system_SamplingTime(A::Union{DescriptorStateSpace,AbstractNumOrArray,UniformScaling}...)
462+
function promote_system_SamplingTime(A::Union{DSTYPE,AbstractNumOrArray,UniformScaling}...)
411463
# pick and check the common sampling time
412464
Ts = nothing
413465
for a in A
414-
typeof(a) <: DescriptorStateSpace && (isnothing(Ts) ? Ts = a.Ts : Ts = promote_Ts(Ts,a.Ts))
466+
typeof(a) <: DSTYPE && (isnothing(Ts) ? Ts = a.Ts : Ts = promote_Ts(Ts,a.Ts))
415467
end
416468
return isnothing(Ts) ? (return 0) : (return Ts) # for systems use Ts = 0 as defualt
417469
end

src/types/DescriptorStateSpace.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,9 @@ end
211211
-(sys1::SparseDescriptorStateSpace, sys2::SparseDescriptorStateSpace) = +(sys1,-sys2)
212212
+(sys1::SparseDescriptorStateSpace, sys2::DescriptorStateSpace) = +(sys1,DescriptorSystems.sparse(sys2))
213213
+(sys1::DescriptorStateSpace, sys2::SparseDescriptorStateSpace) = +(DescriptorSystems.sparse(sys1),sys2)
214+
+(sys1::SparseDescriptorStateSpace, sys2::DSTYPEX) = +(sys1,DescriptorSystems.sparse(sys2))
215+
+(sys1::DSTYPEX, sys2::SparseDescriptorStateSpace) = +(DescriptorSystems.sparse(sys1),sys2)
216+
214217
# difference sys1-sys2
215218
function -(sys1::DSTYPE{T1}, sys2::DSTYPE{T2}) where {T1,T2}
216219
sys1.nu == 1 && sys1.ny == 1 && (sys2.ny > 1 || sys2.nu > 1) && (return ones(T1,sys2.ny,1)*sys1*ones(T1,1,sys2.nu) - sys2)
@@ -353,6 +356,9 @@ function *(sys1::SparseDescriptorStateSpace{T1}, sys2::SparseDescriptorStateSpac
353356
end
354357
return SparseDescriptorStateSpace{T}(A, E, B, C, D, Ts)
355358
end
359+
*(sys1::SparseDescriptorStateSpace, sys2::DSTYPEX) = *(sys1,DescriptorSystems.sparse(sys2))
360+
*(sys1::DSTYPEX, sys2::SparseDescriptorStateSpace) = *(DescriptorSystems.sparse(sys1),sys2)
361+
356362

357363
# sys*mat
358364
function *(sys::DSTYPE{T1}, mat::AbstractVecOrMat{T2}) where {T1,T2}

0 commit comments

Comments
 (0)