From bb244e9376f2523295101fa72a1f8bf7e19d829a Mon Sep 17 00:00:00 2001 From: Aocci Date: Thu, 1 Apr 2021 19:08:31 -0300 Subject: [PATCH 1/7] feat: add `prescale(sys::StateSpace)` and corresponding test. --- src/matrix_comps.jl | 22 ++++++++++++++++++++++ test/test_matrix_comps.jl | 6 ++++++ 2 files changed, 28 insertions(+) diff --git a/src/matrix_comps.jl b/src/matrix_comps.jl index ee3c74405..50a11a454 100644 --- a/src/matrix_comps.jl +++ b/src/matrix_comps.jl @@ -583,6 +583,28 @@ function similarity_transform(sys::ST, T) where ST <: AbstractStateSpace ST(A,B,C,D,sys.timeevol) end +""" + syst, S = prescale(sys) +Perform a eigendecomposition on system state-transition matrix `sys.A`. +``` +Ã = S⁻¹AS +B̃ = S⁻¹ B +C̃ = CS +D̃ = D +``` +Such that `Ã` is diagonal. +Returns a new scaled state-space object and the associated transformation +matrix. +""" +function prescale(sys::StateSpace) + d, S = eigen(sys.A) + A = Diagonal(d) + B = S\sys.B + C = sys.C*S + normalized_sys = iscontinuous(sys) ? ss(A, B, C, sys.D) : ss(A, B, C, sys.D, sys.Ts) + return normalized_sys, S +end + """ sysi = innovation_form(sys, R1, R2) sysi = innovation_form(sys; sysw=I, syse=I, R1=I, R2=I) diff --git a/test/test_matrix_comps.jl b/test/test_matrix_comps.jl index 527811e2f..92f4cca55 100644 --- a/test/test_matrix_comps.jl +++ b/test/test_matrix_comps.jl @@ -70,6 +70,12 @@ syst = similarity_transform(sys, Tr) @test sys.B ≈ Tr*syst.B @test sys.C*Tr ≈ syst.C +nsys, T = prescale(sys) +@test isdiagonal(nsys.A) +@test T*nsys.A ≈ sys.A*T +@test T*nsys.B ≈ sys.B +@test nsys.C = sys.C*T + sys = ss([1 0.1; 0 1], ones(2), [1. 0], 0) sysi = ControlSystems.innovation_form(sys, I, I) @test sysi.A ≈ sysi.A From 880fdcb4247709cf0e91434b8b126db2145531f3 Mon Sep 17 00:00:00 2001 From: Aocci Date: Thu, 1 Apr 2021 20:23:04 -0300 Subject: [PATCH 2/7] fix: `isdiagonal` -> `isdiag`. --- test/test_matrix_comps.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_matrix_comps.jl b/test/test_matrix_comps.jl index 92f4cca55..93ada018a 100644 --- a/test/test_matrix_comps.jl +++ b/test/test_matrix_comps.jl @@ -71,10 +71,10 @@ syst = similarity_transform(sys, Tr) @test sys.C*Tr ≈ syst.C nsys, T = prescale(sys) -@test isdiagonal(nsys.A) +@test isdiag(nsys.A) @test T*nsys.A ≈ sys.A*T @test T*nsys.B ≈ sys.B -@test nsys.C = sys.C*T +@test nsys.C ≈ sys.C*T sys = ss([1 0.1; 0 1], ones(2), [1. 0], 0) sysi = ControlSystems.innovation_form(sys, I, I) From 4139f2da6956532b0191d02168da435bc0649acc Mon Sep 17 00:00:00 2001 From: Aocci Date: Thu, 1 Apr 2021 23:52:38 -0300 Subject: [PATCH 3/7] feat: export `prescale` and `luenberger`. --- src/ControlSystems.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ControlSystems.jl b/src/ControlSystems.jl index 8506aed5b..40df3ad20 100644 --- a/src/ControlSystems.jl +++ b/src/ControlSystems.jl @@ -32,6 +32,7 @@ export LTISystem, ctrb, obsv, place, + luenberger, # Model Simplification reduce_sys, sminreal, @@ -39,6 +40,7 @@ export LTISystem, balreal, baltrunc, similarity_transform, + prescale, innovation_form, # Stability Analysis isstable, From a590809d4da24fb966cbcf94632deddefe4f2688 Mon Sep 17 00:00:00 2001 From: Aocci Date: Sat, 3 Apr 2021 20:50:03 -0300 Subject: [PATCH 4/7] refactor: function --- src/matrix_comps.jl | 59 +++++++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/src/matrix_comps.jl b/src/matrix_comps.jl index 50a11a454..4952a8ec4 100644 --- a/src/matrix_comps.jl +++ b/src/matrix_comps.jl @@ -585,29 +585,58 @@ end """ syst, S = prescale(sys) -Perform a eigendecomposition on system state-transition matrix `sys.A`. + +Balances the metasystem matrix ``` -Ã = S⁻¹AS -B̃ = S⁻¹ B -C̃ = CS -D̃ = D + M = [A B; + C 0] ``` -Such that `Ã` is diagonal. +such that their column and row norms are closer. +This results in a system with overall better numerical conditioning. + Returns a new scaled state-space object and the associated transformation matrix. """ -function prescale(sys::StateSpace) - d, S = eigen(sys.A) - A = Diagonal(d) - B = S\sys.B - C = sys.C*S - normalized_sys = iscontinuous(sys) ? ss(A, B, C, sys.D) : ss(A, B, C, sys.D, sys.Ts) - return normalized_sys, S +function prescale(sys::StateSpace) where ST <: AbstractStateSpace + n, m = size(sys.B) + p = size(sys.C, 1) + + # create an augmented system to balance the whole system at once + metasys = [sys.A sys.B zeros(n, n-m); sys.C zeros(p, n)] + S, P, B = balance(metasys) + T = S*P # permutation matrix to be returned later + + # reconstruct A, B and C by taking slices of the balanced metasystem + bal_metasys = P\B*P # undo permutation + A = bal_metasys[1:n, 1:n] + B = bal_metasys[1:n, n+1:n+m] + C = bal_metasys[n+1:end, 1:n] + return ST(A, B, C, sys.D, sys.timeevol), T +end + +""" + canon_sys = canon(sys::StateSpace, type::String) +Receives a state space and a string ('diagonal', 'controlable' or 'observable') +describing the canonical form. + +""" +function canon(sys::AbstractStateSpace, opt::Symbol) + if opt === :d # diagonal form + elseif opt == :c # controlable form + # determine transformation matrix `T` + elseif opt == :o # observable form + # determine transformation matrix `T` + elseif opt === :j # jordan form + # determine transformation matrix `T` + else + print("Unrecongized decomposition type") + end + # apply transformation to system end """ -sysi = innovation_form(sys, R1, R2) -sysi = innovation_form(sys; sysw=I, syse=I, R1=I, R2=I) + sysi = innovation_form(sys, R1, R2) + sysi = innovation_form(sys; sysw=I, syse=I, R1=I, R2=I) Takes a system ``` From be249d0a707cab9b7428080acc61443aa87e27f2 Mon Sep 17 00:00:00 2001 From: Aocci Date: Sat, 3 Apr 2021 20:55:28 -0300 Subject: [PATCH 5/7] refactor: remove `canon` function; work still in progress. --- src/matrix_comps.jl | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/matrix_comps.jl b/src/matrix_comps.jl index 4952a8ec4..51f758de4 100644 --- a/src/matrix_comps.jl +++ b/src/matrix_comps.jl @@ -614,26 +614,6 @@ function prescale(sys::StateSpace) where ST <: AbstractStateSpace return ST(A, B, C, sys.D, sys.timeevol), T end -""" - canon_sys = canon(sys::StateSpace, type::String) -Receives a state space and a string ('diagonal', 'controlable' or 'observable') -describing the canonical form. - -""" -function canon(sys::AbstractStateSpace, opt::Symbol) - if opt === :d # diagonal form - elseif opt == :c # controlable form - # determine transformation matrix `T` - elseif opt == :o # observable form - # determine transformation matrix `T` - elseif opt === :j # jordan form - # determine transformation matrix `T` - else - print("Unrecongized decomposition type") - end - # apply transformation to system -end - """ sysi = innovation_form(sys, R1, R2) sysi = innovation_form(sys; sysw=I, syse=I, R1=I, R2=I) From c0a1088018f1930fa6378e0c051d2b024aab8fa6 Mon Sep 17 00:00:00 2001 From: Aocci Date: Sat, 3 Apr 2021 22:14:12 -0300 Subject: [PATCH 6/7] fix: prescale tests --- src/matrix_comps.jl | 10 ++++------ test/test_matrix_comps.jl | 12 +++++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/matrix_comps.jl b/src/matrix_comps.jl index 4952a8ec4..1318aeef8 100644 --- a/src/matrix_comps.jl +++ b/src/matrix_comps.jl @@ -594,24 +594,22 @@ Balances the metasystem matrix such that their column and row norms are closer. This results in a system with overall better numerical conditioning. -Returns a new scaled state-space object and the associated transformation -matrix. +Returns a new scaled state-space object. """ -function prescale(sys::StateSpace) where ST <: AbstractStateSpace +function prescale(sys::ST) where ST <: AbstractStateSpace n, m = size(sys.B) p = size(sys.C, 1) # create an augmented system to balance the whole system at once - metasys = [sys.A sys.B zeros(n, n-m); sys.C zeros(p, n)] + metasys = [sys.A sys.B; sys.C zeros(p, m)] S, P, B = balance(metasys) - T = S*P # permutation matrix to be returned later # reconstruct A, B and C by taking slices of the balanced metasystem bal_metasys = P\B*P # undo permutation A = bal_metasys[1:n, 1:n] B = bal_metasys[1:n, n+1:n+m] C = bal_metasys[n+1:end, 1:n] - return ST(A, B, C, sys.D, sys.timeevol), T + return ST(A, B, C, sys.D, sys.timeevol) end """ diff --git a/test/test_matrix_comps.jl b/test/test_matrix_comps.jl index 93ada018a..258c9e0db 100644 --- a/test/test_matrix_comps.jl +++ b/test/test_matrix_comps.jl @@ -70,11 +70,13 @@ syst = similarity_transform(sys, Tr) @test sys.B ≈ Tr*syst.B @test sys.C*Tr ≈ syst.C -nsys, T = prescale(sys) -@test isdiag(nsys.A) -@test T*nsys.A ≈ sys.A*T -@test T*nsys.B ≈ sys.B -@test nsys.C ≈ sys.C*T +nsys = prescale(sys) +@test pole(nsys) ≈ pole(sys) +n, m = size(sys.B) +p = size(sys.C, 1) +M = [nsys.A nsys.B; nsys.C zeros(p, m)] +M2 = [sys.A sys.B; sys.C zeros(p, m)] +@test cond(M) <= cond(M2) sys = ss([1 0.1; 0 1], ones(2), [1. 0], 0) sysi = ControlSystems.innovation_form(sys, I, I) From cac2f60d2a66a352ead9f4b7d7e4dcf7656eb4fa Mon Sep 17 00:00:00 2001 From: Aocci Date: Sat, 3 Apr 2021 22:22:30 -0300 Subject: [PATCH 7/7] refactor: slicing indices in prescale --- src/matrix_comps.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/matrix_comps.jl b/src/matrix_comps.jl index 1318aeef8..6c4f06e14 100644 --- a/src/matrix_comps.jl +++ b/src/matrix_comps.jl @@ -607,7 +607,7 @@ function prescale(sys::ST) where ST <: AbstractStateSpace # reconstruct A, B and C by taking slices of the balanced metasystem bal_metasys = P\B*P # undo permutation A = bal_metasys[1:n, 1:n] - B = bal_metasys[1:n, n+1:n+m] + B = bal_metasys[1:n, n+1:end] C = bal_metasys[n+1:end, 1:n] return ST(A, B, C, sys.D, sys.timeevol) end