Skip to content

Commit b51adf7

Browse files
committed
handle proper quotient of proper statespace systems
1 parent 27c1b00 commit b51adf7

File tree

2 files changed

+58
-3
lines changed

2 files changed

+58
-3
lines changed

lib/ControlSystemsBase/src/types/StateSpace.jl

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,55 @@ end
378378

379379

380380
## DIVISION ##
381-
/(sys1::AbstractStateSpace, sys2::AbstractStateSpace) = sys1*inv(sys2)
381+
382+
383+
"""
384+
/(sys1::AbstractStateSpace{TE}, sys2::AbstractStateSpace{TE}; atol::Real = 0, atol1::Real = atol, atol2::Real = atol, rtol::Real = max(size(sys1.A, 1), size(sys2.A, 1)) * eps(real(float(one(numeric_type(sys1))))) * iszero(min(atol1, atol2)))
385+
386+
Compute `sys1 / sys2 = sys1 * inv(sys2)` in a way that tries to handle situations in which the inverse `sys2` is non-proper, but the resulting system `sys1 / sys2` is proper.
387+
388+
See `ControlSystemsBase.MatrixPencils.isregular` for keyword arguments `atol`, `atol1`, `atol2`, and `rtol`.
389+
"""
390+
function Base.:(/)(sys1::AbstractStateSpace{TE}, sys2::AbstractStateSpace{TE};
391+
atol::Real = 0, atol1::Real = atol, atol2::Real = atol,
392+
rtol::Real = max(size(sys1.A,1),size(sys2.A,1))*eps(real(float(one(numeric_type(sys1)))))*iszero(min(atol1,atol2))) where {TE<:ControlSystemsBase.TimeEvolution}
393+
T1 = float(numeric_type(sys1))
394+
T2 = float(numeric_type(sys2))
395+
T = promote_type(T1,T2)
396+
timeevol = common_timeevol(sys1, sys2)
397+
ny2, nu2 = sys2.ny, sys2.nu
398+
nu2 == ny2 || error("The system sys2 must be square")
399+
ny1, nu1 = sys1.ny, sys1.nu
400+
nu1 == nu2 || error("The systems sys1 and sys2 must have the same number of inputs")
401+
nx1 = sys1.nx
402+
nx2 = sys2.nx
403+
if nx2 > 0
404+
A, B, C, D = ssdata([sys2; sys1])
405+
Ai = [A B; C[1:ny2,:] D[1:ny2,:]]
406+
Ei = [I zeros(T,nx1+nx2,ny2); zeros(T,ny2,nx1+nx2+ny2)] |> Matrix # TODO: rm call to Matrix when type piracy in https://github.com/JuliaLinearAlgebra/LinearMaps.jl/issues/219 is fixed
407+
MatrixPencils.isregular(Ai, Ei; atol1, atol2, rtol) ||
408+
error("The system sys2 is not invertible")
409+
Ci = [C[ny2+1:ny1+ny2,:] D[ny2+1:ny1+ny2,:]]
410+
Bi = [zeros(T,nx1+nx2,nu1); -I] |> Matrix # TODO: rm call to Matrix when type piracy in https://github.com/JuliaLinearAlgebra/LinearMaps.jl/issues/219 is fixed
411+
Di = zeros(T,ny1,nu1)
412+
Ai, Ei, Bi, Ci, Di = MatrixPencils.lsminreal(Ai, Ei, Bi, Ci, Di; fast = true, atol1 = 0, atol2, rtol, contr = true, obs = true, noseig = true)
413+
if Ei != I
414+
luE = lu!(Ei, check=false)
415+
issuccess(luE) || throw(ArgumentError("The system sys2 is not invertible"))
416+
Ai = luE\Ai
417+
Bi = luE\Bi
418+
end
419+
else
420+
D2 = T.(sys2.D)
421+
LUD = lu(D2)
422+
(norm(D2,Inf) <= atol1 || rcond(LUD.U) <= 10*nu1*eps(real(float(one(T))))) &&
423+
error("The system sys2 is not invertible")
424+
Ai, Bi, Ci, Di = ssdata(sys1)
425+
rdiv!(Bi,LUD); rdiv!(Di,LUD)
426+
end
427+
428+
return StateSpace{TE, T}(Ai, Bi, Ci, Di, timeevol)
429+
end
382430

383431
function /(n::Number, sys::ST) where ST <: AbstractStateSpace
384432
# Ensure s.D is invertible

lib/ControlSystemsBase/test/test_statespace.jl

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,18 @@
159159

160160
# Division
161161
@test 1/C_222_d == SS([-6 -3; 2 -11],[1 0; 0 2],[-1 0; -0 -1],[1 -0; 0 1])
162-
@test C_221/C_222_d == SS([-5 -3 -1 0; 2 -9 -0 -2; 0 0 -6 -3;
163-
0 0 2 -11],[1 0; 0 2; 1 0; 0 2],[1 0 0 0],[0 0])
162+
@test hinfnorm((C_221/C_222_d) - SS([-5 -3 -1 0; 2 -9 -0 -2; 0 0 -6 -3;
163+
0 0 2 -11],[1 0; 0 2; 1 0; 0 2],[1 0 0 0],[0 0]))[1] < 1e-10
164164
@test 1/D_222_d == SS([-0.8 -0.8; -0.8 -1.93],[1 0; 0 2],[-1 0; -0 -1],
165165
[1 -0; 0 1],0.005)
166166

167+
# Division when denominator inverse is non-proper but quotient is proper
168+
G1 = tf([1], [1, 1])
169+
G2 = tf([1], [1, 1, 1])
170+
G1s = ss(G1)
171+
G2s = ss(G2)
172+
@test tf(G2s / G1s) G2 / G1
173+
167174
fsys = ss(1,1,1,0)/3 # Int becomes FLoat after division
168175
@test fsys.B[]*fsys.C[] == 1/3
169176

0 commit comments

Comments
 (0)