Skip to content

Commit dda8d38

Browse files
albheimolof3
andauthored
Add function to order conjugate pairs in zpk constructor (#386)
* Add order_conjugates and call from zpk constructor * add simple test for conjugate ordering * Rename to pairup, merge checkreal functionality * Copy zero/pole vecs in zpk constructor * Update src/types/SisoTfTypes/SisoZpk.jl Co-authored-by: olof3 <olof3@users.noreply.github.com> * Update src/types/SisoTfTypes/SisoZpk.jl Co-authored-by: olof3 <olof3@users.noreply.github.com> * Update src/types/SisoTfTypes/SisoZpk.jl Co-authored-by: olof3 <olof3@users.noreply.github.com> * Update src/types/SisoTfTypes/SisoZpk.jl Co-authored-by: olof3 <olof3@users.noreply.github.com> * Update * Add more tests Co-authored-by: olof3 <olof3@users.noreply.github.com>
1 parent 892dcc1 commit dda8d38

File tree

2 files changed

+32
-31
lines changed

2 files changed

+32
-31
lines changed

src/types/SisoTfTypes/SisoZpk.jl

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,20 @@ struct SisoZpk{T,TR<:Number} <: SisoTf{T}
66
z::Vector{TR}
77
p::Vector{TR}
88
k::T
9-
function SisoZpk{T,TR}(z::Vector{TR}, p::Vector{TR}, k::T) where {T<:Number, TR<:Number}
9+
function SisoZpk{T,TR}(z::Vector{TR}, p::Vector{TR}, k::T) where {T<:Number, TR<:Number}
1010
if k == zero(T)
1111
p = TR[]
1212
z = TR[]
1313
end
1414
if TR <: Complex && T <: Real
15-
@assert check_real(z) "zpk model should be real-valued, but zeros do not come in conjugate pairs."
16-
@assert check_real(p) "zpk model should be real-valued, but poles do not come in conjugate pairs."
15+
z, p = copy(z), copy(p)
16+
@assert pairup_conjugates!(z) "zpk model should be real-valued, but zeros do not come in conjugate pairs."
17+
@assert pairup_conjugates!(p) "zpk model should be real-valued, but poles do not come in conjugate pairs."
1718
end
1819
new{T,TR}(z, p, k)
1920
end
2021
end
21-
function SisoZpk{T,TR}(z::Vector, p::Vector, k::Number) where {T<:Number, TR<:Number}
22+
function SisoZpk{T,TR}(z::Vector, p::Vector, k::Number) where {T<:Number, TR<:Number}
2223
SisoZpk{T,TR}(Vector{TR}(z), Vector{TR}(p), T(k))
2324
end
2425
function SisoZpk{T}(z::Vector, p::Vector, k::Number) where T
@@ -27,13 +28,6 @@ function SisoZpk{T}(z::Vector, p::Vector, k::Number) where T
2728
end
2829
function SisoZpk(z::AbstractVector{TZ}, p::AbstractVector{TP}, k::T) where {T<:Number, TZ<:Number, TP<:Number} # NOTE: is this constructor really needed?
2930
TR = promote_type(TZ,TP)
30-
# Could check if complex roots come with their conjugates,
31-
# i.e., if the SisoZpk corresponds to a real-valued system
32-
33-
if TR <: Complex && T <: Real
34-
@assert check_real(z) "zpk model should be real-valued, but zeros do not come in conjugate pairs."
35-
@assert check_real(p) "zpk model should be real-valued, but poles do not come in conjugate pairs."
36-
end
3731
SisoZpk{T,TR}(Vector{TR}(z), Vector{TR}(p), k)
3832
end
3933

@@ -109,32 +103,33 @@ function minreal(sys::SisoZpk{T,TR}, eps::Real) where {T, TR}
109103
SisoZpk{T, TR}(newZ, newP, sys.k)
110104
end
111105

112-
# FIXME: Perhaps move to some other file with auxilliary functions,
113-
# the name could also be imporoved. Perhaps this functionality can be found in some other package.
114-
""" If TR is Complex and T is Real, check that every pole is matched to its conjugate
115-
this assumes that the compelx poles are ordered as they are output by the LAPACK
116-
routines that return complex-conjugated values, i.e., (x+iy) is followed by (x-iy)"""
117-
function check_real(r_vec::AbstractVector{<:Complex})
118-
k = 1
119-
while k <= length(r_vec)
120-
if isreal(r_vec[k])
121-
# Do nothing
106+
""" Reorder the vector x of complex numbers so that complex conjugates come after each other,
107+
with the one with positive imaginary part first. Returns true if the conjugates can be
108+
paired and otherwise false."""
109+
function pairup_conjugates!(x::AbstractVector)
110+
i = 0
111+
while i < length(x)
112+
i += 1
113+
imag(x[i]) == 0 && continue
114+
115+
# Attempt to find a matching conjugate to x[i]
116+
j = findnext(==(conj(x[i])), x, i+1)
117+
j === nothing && return false
118+
119+
tmp = x[j]
120+
x[j] = x[i+1]
121+
# Make sure that the complex number with positive imaginary part comes first
122+
if imag(x[i]) > 0
123+
x[i+1] = tmp
122124
else
123-
if k == length(r_vec) # Element is complex and there is no more elements to match up with
124-
return false
125-
elseif conj(r_vec[k]) == r_vec[k+1] # Element is complex and succeeding element is its conjugate
126-
k=k+1
127-
else
128-
return false
129-
end
125+
x[i+1] = x[i]
126+
x[i] = tmp
130127
end
131-
k += 1
128+
i += 1 # Since it is a pair and the conjugate was found
132129
end
133130
return true
134131
end
135132

136-
137-
138133
function evalfr(f::SisoZpk{T1,TR}, s::T2) where {T1<:Number, TR<:Number, T2<:Number}
139134
T0 = promote_type(T2, TR)
140135
T = promote_type(T1, Base.promote_op(/, T0, T0))

test/test_zpk.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ z = zpk("z", 0.005)
3838
@test zpk([], [], 1.0) == zpk(Float64[], Float64[], 1.0)
3939
@test zpk([], [1.0+im,1.0-im], 1.0) == zpk(ComplexF64[], [1.0+im,1.0-im], 1.0)
4040

41+
# Test constructors with different conjugate ordering
42+
@test zpk([], [1.0-im,1.0+im], 1.0) == zpk(ComplexF64[], [1.0+im,1.0-im], 1.0)
43+
@test zpk([], [1.0+im,1.0,1.0-im], 1.0) == zpk(ComplexF64[], [1.0+im,1.0-im,1.0], 1.0)
44+
@test zpk([1.0-im,1.0,1.0+im], [], 1.0) == zpk([1.0+im,1.0-im,1.0], ComplexF64[], 1.0)
45+
@test zpk([], [1.0+im,1.0,1.0+im,1.0-im,1.0-im], 1.0) == zpk(ComplexF64[], [1.0+im,1.0-im,1.0+im,1.0-im,1.0], 1.0)
46+
@test_throws AssertionError zpk([], [1.01+im,1.0-im], 1.0)
4147

4248

4349
#TODO improve polynomial accuracy se these are equal

0 commit comments

Comments
 (0)