diff --git a/lib/ControlSystemsBase/src/types/SisoTfTypes/SisoZpk.jl b/lib/ControlSystemsBase/src/types/SisoTfTypes/SisoZpk.jl index 91ec7d191..9e3f87f54 100644 --- a/lib/ControlSystemsBase/src/types/SisoTfTypes/SisoZpk.jl +++ b/lib/ControlSystemsBase/src/types/SisoTfTypes/SisoZpk.jl @@ -79,11 +79,12 @@ function minreal(sys::SisoZpk{T,TR}, eps::Real) where {T, TR} distance, zidx = findmin(abs.(p .- newZ)) if distance < eps - if imag(p) == 0 && imag(newZ[zidx]) != 0 + if imag(p) == 0 && imag(newZ[zidx]) != 0 && zidx < length(newZ) newZ[zidx+1] = real(newZ[zidx+1]) end if imag(newZ[zidx]) == 0 && imag(p) != 0 pidx += 1 + pidx > length(sys.p) && (deleteat!(newZ, zidx); break) p = real(sys.p[pidx]) deleteat!(newZ, zidx) continue diff --git a/lib/ControlSystemsBase/test/test_zpk.jl b/lib/ControlSystemsBase/test/test_zpk.jl index 955eb5cbc..94fb10ea7 100644 --- a/lib/ControlSystemsBase/test/test_zpk.jl +++ b/lib/ControlSystemsBase/test/test_zpk.jl @@ -144,6 +144,14 @@ k = 0.3 @test minreal(zpk([-1.0, -2.0], Float64[], 2.5)) == zpk([-1.0, -2.0], Float64[], 2.5) @test minreal(zpk(Float64[], [-1.0, -2.0], 2.5)) == zpk(Float64[], [-1.0, -2.0], 2.5) +# Regression: bounds-checks in SisoZpk minreal. With complex-valued zpk the +# constructor does not enforce conjugate-pair ordering, so the cancellation +# loop can hit `newZ[zidx+1]` (line 83) with `zidx == length(newZ)`, or +# advance `pidx` past `length(sys.p)` before reading `sys.p[pidx]` (line 87). +# Both previously threw BoundsError; they should now return cleanly. +@test minreal(zpk([1.0+0.001im, 1.0-0.001im], [1.0, 1.0+0.001im], 1.0+0im), 0.01) == zpk(ComplexF64[], ComplexF64[], 1.0+0im) +@test minreal(zpk([1.0+0.001im, 1.0-0.001im], [1.0+0.001im, 1.0], 1.0+0im), 0.01) == zpk(ComplexF64[], ComplexF64[], 1.0+0im) + # Test type inference @test eltype(fill(zpk("s"),2)) <: TransferFunction @test eltype(fill(zpk([1],[1,1],1),2)) <: TransferFunction