Skip to content

Commit c522a79

Browse files
authored
handle B matrix with non-full column rank in place (#1009)
* handle B matrix with non-full column rank in place * add additional test case
1 parent 7c91ecc commit c522a79

File tree

2 files changed

+39
-5
lines changed

2 files changed

+39
-5
lines changed

lib/ControlSystemsBase/src/synthesis.jl

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,11 @@ function place_knvd(A::AbstractMatrix, B, λ; verbose=false, init=:s, method = 0
242242
λ = sort(vec(λ), by=LinearAlgebra.eigsortby)
243243
length(λ) == size(A, 1) == n || error("Must specify as many poles as the state dimension")
244244
Λ = diagm(λ)
245-
QRB = qr(B)
246-
U0, U1 = QRB.Q[:, 1:m], QRB.Q[:, m+1:end] # TODO: check dimension
247-
Z = QRB.R
248245
R = svdvals(B)
249246
m = count(>(100*eps()*R[1]), R) # Rank of B
247+
QRB = qr(B, ColumnNorm())
248+
U0, U1 = QRB.Q[:, 1:m], QRB.Q[:, m+1:end] # TODO: check dimension
249+
Z = (QRB.R*QRB.P')[:, 1:m]
250250
if m == n # Easy case, B is full rank
251251
r = count(e->imag(e) == 0, λ)
252252
ABF = diagm(real(λ))
@@ -259,9 +259,21 @@ function place_knvd(A::AbstractMatrix, B, λ; verbose=false, init=:s, method = 0
259259
return B\(A - ABF) # Solve for F in (A - BF) = Λ
260260
end
261261

262+
mB = size(B, 2)
263+
if mB > m
264+
# several inputs but not full column rank, this case must be handled separately
265+
# when B does not have full column rank but that rank is not 1. In that case, find B2 and T from rank-revealing QR (qr(B, ColumnNorm())
266+
verbose && @info "Projecting down to rank of B"
267+
B2 = QRB.Q[:, 1:m]
268+
T = QRB.P * QRB.R[1:m, :]'
269+
F = place(A, B2, λ; verbose, init, method)
270+
return pinv(T)'*F
271+
end
272+
262273
S = Matrix{CT}[]
263274
for j = 1:n
264-
qj = qr((U1'*(A- λ[j]*I))')
275+
H = (U1'*(A- λ[j]*I))
276+
qj = qr(H')
265277
# Ŝj = qj.Q[:, 1:n-m] # Needed for method 2
266278
Sj = qj.Q[:, n-m+1:n]
267279
push!(S, Sj)

lib/ControlSystemsBase/test/test_synthesis.jl

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ end
114114
# @show cond(gram(sys, :c))
115115
(; A, B) = sys
116116
p = [-1.0, -2, -3]
117-
L = place(A, B, p)
117+
L = place(A, B, p, verbose=false)
118118
@test eltype(L) <: Real
119119
@test allin(eigvals(A - B*L), p)
120120

@@ -177,6 +177,28 @@ end
177177
@test allin(eigvals(A - B*L), p; tol=0.025) # Tolerance from paper
178178
# norm(L)
179179

180+
## Rank deficient multi-input B
181+
P = let
182+
tempA = [0.0 1.0; -4.0 -1.2]
183+
tempB = [0.0 0.0; 4.0 4.0]
184+
tempC = [1.0 0.0]
185+
tempD = [0.0 0.0]
186+
ss(tempA, tempB, tempC, tempD)
187+
end
188+
F = place(P, [-2, -2], verbose=true)
189+
@test allin(eigvals(P.A - P.B*F), [-2, -2])
190+
191+
192+
P = let
193+
tempA = [0.0 1.0; -4.0 -1.2]
194+
tempB = randn(2, 1) * randn(1, 10) # Rank deficient
195+
tempC = [1.0 0.0]
196+
tempD = zeros(1, 10)
197+
ss(tempA, tempB, tempC, tempD)
198+
end
199+
F = place(P, [-2, -2], verbose=true)
200+
@test allin(eigvals(P.A - P.B*F), [-2, -2])
201+
180202
end
181203

182204
@testset "LQR" begin

0 commit comments

Comments
 (0)