Skip to content

Commit c79201d

Browse files
authored
do not promote system type when creating a vector (#958)
* do not promote system type when creating a vector * handle identical types * handle equality between different system types * rm old advice
1 parent 5b2b96d commit c79201d

File tree

8 files changed

+33
-20
lines changed

8 files changed

+33
-20
lines changed

docs/src/man/creating_systems.md

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -268,19 +268,6 @@ P = ssrand(2,3,1) # A random 2×3 MIMO system
268268
sys_array = getindex.(Ref(P), 1:P.ny, (1:P.nu)')
269269
```
270270

271-
### Creating arrays with different types of systems
272-
When calling `hcat/vcat`, Julia automatically tries to promote the types to the smallest common supertype, this means that creating an array with one continuous and one discrete-time system fails
273-
```@example MIMO
274-
P_cont = ssrand(2,3,1)
275-
P_disc = ssrand(2,3,1, Ts=1)
276-
@test_throws ErrorException [P_cont, P_disc] # ERROR: Sampling time mismatch
277-
```
278-
You can explicitly tell Julia that you want a particular supertype, e.g,
279-
```@example MIMO
280-
StateSpace[P_cont, P_disc]
281-
```
282-
The type `StateSpace` is abstract, since the type parameters are not specified.
283-
284271
## Demo systems
285272
The module `ControlSystemsBase.DemoSystems` contains a number of demo systems demonstrating different kinds of dynamics.
286273

lib/ControlSystemsBase/src/ControlSystemsBase.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ function __init__()
237237
print(io, " for automatic discretization (applicable to systems without delays or nonlinearities only).")
238238
end
239239
plots_id = Base.PkgId(UUID("91a5bcdd-55d7-5caf-9e0b-520d859cae80"), "Plots")
240-
if nameof(exc.f) === :plot && parentmodule(argtypes[1]) == @__MODULE__() && !haskey(Base.loaded_modules, plots_id)
240+
if exc.f !== nothing && nameof(exc.f) === :plot && parentmodule(argtypes[1]) == @__MODULE__() && !haskey(Base.loaded_modules, plots_id)
241241
printstyled(io, "\nPlotting is not available unless Plots.jl is loaded manually. Call `using Plots` before plotting.", color=:green, bold=true)
242242
elseif (exc.f == /) && argtypes[2] <: DelayLtiSystem
243243
print(io, "A delayed system can not be inverted. Consider use of the function `feedback`.")

lib/ControlSystemsBase/src/connections.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,16 @@ end
138138
Base.typed_hcat(::Type{S}, X::Number...) where {S<:LTISystem} = hcat(convert.(S, X)...)
139139
Base.typed_hcat(::Type{S}, X::Union{AbstractArray{<:Number,1}, AbstractArray{<:Number,2}}...) where {S<:LTISystem} = hcat(convert.(S, X)...)
140140

141+
## Mixed-type array creation
142+
# When creating an array of systems, an error may be thrown if the default Base.vect is called that tries to promote all systems to a common type. E.g., when using non-proper transfer functions and statespace systems. We thus opt out of the conversion with the method below
143+
function Base.vect(X::LTISystem...)
144+
LTISystem[X...]
145+
end
146+
147+
function Base.vect(X::T...) where T <: LTISystem
148+
T[X...]
149+
end
150+
141151
"""
142152
add_input(sys::AbstractStateSpace, B2::AbstractArray, D2 = 0)
143153

lib/ControlSystemsBase/src/types/StateSpace.jl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,13 @@ end
248248
## Approximate ##
249249
function isapprox(sys1::ST1, sys2::ST2; kwargs...) where {ST1<:AbstractStateSpace,ST2<:AbstractStateSpace}
250250
fieldnames(ST1) == fieldnames(ST2) || (return false)
251-
return all(isapprox(getfield(sys1, f), getfield(sys2, f); kwargs...) for f in fieldnames(ST1))
251+
return all(fieldnames(ST1)) do f
252+
if fieldtype(ST1, f) <: Union{Number, AbstractArray{<:Number}, LTISystem}
253+
isapprox(getfield(sys1, f), getfield(sys2, f); kwargs...)
254+
else
255+
getfield(sys1, f) == getfield(sys2, f)
256+
end
257+
end
252258
end
253259

254260
## ADDITION ##

lib/ControlSystemsBase/src/types/TransferFunction.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ isrational(::TransferFunction) = true
105105
#####################################################################
106106

107107
## EQUALITY ##
108-
function ==(G1::TransferFunction, G2::TransferFunction)
108+
function ==(G1::T, G2::T) where T<:TransferFunction
109109
fields = (:timeevol, :ny, :nu, :matrix)
110110
for field in fields
111111
if getproperty(G1, field) != getproperty(G2, field)
@@ -115,6 +115,8 @@ function ==(G1::TransferFunction, G2::TransferFunction)
115115
return true
116116
end
117117

118+
==(G1::TransferFunction, G2::TransferFunction) = ==(promote(G1,G2)...)
119+
118120
## Approximate ##
119121
function isapprox(G1::TransferFunction, G2::TransferFunction; kwargs...)
120122
G1, G2 = promote(G1, G2)

lib/ControlSystemsBase/src/types/conversion.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ function siso_tf_to_ss(T::Type, f::SisoRational)
136136
num0, den0 = numvec(f), denvec(f)
137137
# Normalize the numerator and denominator to allow realization of transfer functions
138138
# that are proper, but not strictly proper
139-
num = num0 / den0[1]
140-
den = den0 / den0[1]
139+
num = num0 ./ den0[1]
140+
den = den0 ./ den0[1]
141141

142142
N = length(den) - 1 # The order of the rational function f
143143

lib/ControlSystemsBase/test/test_connections.jl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,15 @@ P = ss(-1.0, 2.0, 3.0, 4.0)
148148
@test [2.5 P 3.5] == ss(-1.0, [0.0 2.0 0.0], 3.0, [2.5 4.0 3.5])
149149
@test [2.5; P; 3.5] == ss(-1.0, 2.0, [0.0; 3.0; 0.0], [2.5; 4.0; 3.5])
150150

151-
151+
# Test vector creation
152+
v = [ssrand(1,1,1), tf(1)]
153+
@test v isa Vector{LTISystem}
154+
@test v[1] isa StateSpace{Continuous, Float64}
155+
@test v[2] isa TransferFunction{Continuous, ControlSystemsBase.SisoRational{Int64}}
156+
157+
# Test vector creation
158+
v = [tf(1), tf(1)]
159+
@test v isa Vector{TransferFunction{Continuous, ControlSystemsBase.SisoRational{Int64}}}
152160

153161
# Combination tfRational and sisoZpk
154162
Czpk_111 = zpk([-2],[-5],1)

lib/ControlSystemsBase/test/test_zpk.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ k = 0.3
153153
@test eltype(fill(zpk(1,0.005)/zpk(2, 0.005),2)) <: TransferFunction
154154
@test eltype(fill(zpk(1)+1,2)) <: TransferFunction
155155

156-
@test eltype([tf(1,1), zpk(1,1)]) <: TransferFunction
156+
@test eltype([tf(1,1), zpk(1,1)]) <: LTISystem
157157

158158
zpk(tf([1 2; 3 4])) == zpk([1 2; 3 4])
159159

0 commit comments

Comments
 (0)