4.9 Schur and Generalized Schur Factorization

gees(A[, w=None[, V=None[, select=None]]])

Computes the Schur factorization

        T                      H
A = V SV   (A real),    A = VSV    (A complex)

of a real or complex n by n matrix A. If A is real, the matrix of Schur vectors V is orthogonal, and S is a real upper quasi-triangular matrix with 1 by 1 or 2 by 2 diagonal blocks. The 2 by 2 blocks correspond to complex conjugate pairs of eigenvalues of A. If A is complex, the matrix of Schur vectors V is unitary, and S is a complex upper triangular matrix with the eigenvalues of A on the diagonal.

The optional argument w is a complex matrix of length at least n. If it is provided, the eigenvalues of A are returned in w. The optional argument V is an n by n matrix of the same type as A. If it is provided, then the Schur vectors are returned in V.

The argument select is an optional ordering routine. It must be a Python function that can be called as ”f(s)” with a complex argument s, and returns True or False. The eigenvalues for which select returns True will be selected to appear first along the diagonal. (In the real Schur factorization, if either one of a complex conjugate pair of eigenvalues is selected, then both are selected.)

On exit, A is replaced with the matrix S. The function gees() returns an integer equal to the number of eigenvalues that were selected by the ordering routine. If select is None, then gees() returns 0.

As an example we compute the complex Schur form of the matrix

    ⌊                        ⌋
      - 7 - 11   - 6  - 4 11
    ||   5  - 3    3  - 12   0 ||
A = ||  11   11   - 5 - 14   9 || .
    ⌈ - 4    8    0    8   6 ⌉
       13 - 19  - 12  - 8 10

>>> A = matrix([[-7., 5., 11., -4., 13.], [-11., -3., 11., 8., -19.], [-6., 3., -5., 0., -12.], [-4., -12., -14., 8., -8.], [11., 0., 9., 6., 10.]])  
>>> S = matrix(A, tc=’z’)  
>>> w = matrix(0.0, (5,1), ’z’)  
>>> gees(S, w)  
0  
>>> print S  
[ 5.67e+00+j1.69e+01 -2.13e+01+j2.85e+00  1.40e+00+j5.88e+00 -4.19e+00+j2.05e-01  3.19e+00-j1.01e+01]  
[ 0.00e+00-j0.00e+00  5.67e+00-j1.69e+01  1.09e+01+j5.93e-01 -3.29e+00-j1.26e+00 -1.26e+01+j7.80e+00]  
[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  1.27e+01+j3.43e-17 -6.83e+00+j2.18e+00  5.31e+00-j1.69e+00]  
[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00 -1.31e+01-j0.00e+00 -2.60e-01-j0.00e+00]  
[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00 -7.86e+00-j0.00e+00]  
>>> print w  
[ 5.67e+00+j1.69e+01]  
[ 5.67e+00-j1.69e+01]  
[ 1.27e+01+j3.43e-17]  
[-1.31e+01-j0.00e+00]  
[-7.86e+00-j0.00e+00]

An ordered Schur factorization with the eigenvalues in the left half of the complex plane ordered first, can be computed as follows.

>>> S = matrix(A, tc=’z’)  
>>> def F(x): return (x.real < 0.0)  
...  
>>> gees(S, w, select = F)  
2  
>>> print S  
[-1.31e+01-j0.00e+00 -1.72e-01+j7.93e-02 -2.81e+00+j1.46e+00  3.79e+00-j2.67e-01  5.14e+00-j4.84e+00]  
[ 0.00e+00-j0.00e+00 -7.86e+00-j0.00e+00 -1.43e+01+j8.31e+00  5.17e+00+j8.79e+00  2.35e+00-j7.86e-01]  
[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  5.67e+00+j1.69e+01 -1.71e+01-j1.41e+01  1.83e+00-j4.63e+00]  
[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  5.67e+00-j1.69e+01 -8.75e+00+j2.88e+00]  
[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  1.27e+01+j3.43e-17]  
>>> print w  
[-1.31e+01-j0.00e+00]  
[-7.86e+00-j0.00e+00]  
[ 5.67e+00+j1.69e+01]  
[ 5.67e+00-j1.69e+01]  
[ 1.27e+01+j3.43e-17]

gges(A, B[, a=None, b=None[, Vl=None[, Vr=None[, select=None]]]])

Computes the generalized Schur factorization

A = VSV T,  B = V TV T  (A and B real),   A = V SVH ,  B = VT VH , (A and B complex)
     l  r        l  r                         l  r         l  r

of a pair of real or complex n by n matrices A, B. If A and B are real, then the matrices of left and right Schur vectors Vl and Vr are orthogonal, S is a real upper quasi-triangular matrix with 1 by 1 or 2 by 2 diagonal blocks, and T is a real triangular matrix with nonnegative diagonal. The 2 by 2 blocks along the diagonal of S correspond to complex conjugate pairs of generalized eigenvalues of A, B. If A and B are complex, the matrices of left and right Schur vectors Vl and Vr are unitary, S is complex upper triangular, and T is complex upper triangular with nonnegative real diagonal.

The optional arguments a and b are ’z’ and ’d’ matrices of length at least n. If these are provided, the generalized eigenvalues of A, B are returned in a and b. (The generalized eigenvalues are the ratios a[k] / b[k].) The optional arguments Vl and Vr are n by n matrices of the same type as A and B. If they are provided, then the left Schur vectors are returned in Vl and the right Schur vectors are returned in Vr.

The argument select is an optional ordering routine. It must be a Python function that can be called as ”f(x,y)” with a complex argument x and a real argument y, and returns True or False. The eigenvalues for which select returns True will be selected to appear first on the diagonal. (In the real Schur factorization, if either one of a complex conjugate pair of eigenvalues is selected, then both are selected.)

On exit, A is replaced with the matrix S and B is replaced with the matrix T. The function gges() returns an integer equal to the number of eigenvalues that were selected by the ordering routine. If select is None, then gges() returns 0.

As an example, we compute the generalized complex Schur form of the matrix A of the previous example, and

    ⌊ 1  0  0  0 0 ⌋
    | 0  1  0  0 0 |
B = || 0  0  1  0 0 || .
    |⌈ 0  0  0  1 0 |⌉
      0  0  0  0 0

>>> A = matrix([[-7., 5., 11., -4., 13.], [-11., -3., 11., 8., -19.], [-6., 3., -5., 0., -12.], [-4., -12., -14., 8., -8.], [11., 0., 9., 6., 10.]])  
>>> B = matrix(0.0, (5,5))  
>>> B[:19:6] = 1.0  
>>> S = matrix(A, tc=’z’)  
>>> T = matrix(B, tc=’z’)  
>>> a = matrix(0.0, (5,1), ’z’)  
>>> b = matrix(0.0, (5,1))  
>>> gges(S, T, a, b)  
0  
>>> print S  
[ 6.64e+00-j8.87e+00 -7.81e+00-j7.53e+00  6.16e+00-j8.51e-01  1.18e+00+j9.17e+00  5.88e+00-j4.51e+00]  
[ 0.00e+00-j0.00e+00  8.48e+00+j1.13e+01 -2.12e-01+j1.00e+01  5.68e+00+j2.40e+00 -2.47e+00+j9.38e+00]  
[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00 -1.39e+01-j0.00e+00  6.78e+00-j0.00e+00  1.09e+01-j0.00e+00]  
[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00 -6.62e+00-j0.00e+00 -2.28e-01-j0.00e+00]  
[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00 -2.89e+01-j0.00e+00]  
>>> print T  
[ 6.46e-01-j0.00e+00  4.29e-01-j4.79e-02  2.02e-01-j3.71e-01  1.08e-01-j1.98e-01 -1.95e-01+j3.58e-01]  
[ 0.00e+00-j0.00e+00  8.25e-01-j0.00e+00 -2.17e-01+j3.11e-01 -1.16e-01+j1.67e-01  2.10e-01-j3.01e-01]  
[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  7.41e-01-j0.00e+00 -3.25e-01-j0.00e+00  5.87e-01-j0.00e+00]  
[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  8.75e-01-j0.00e+00  4.84e-01-j0.00e+00]  
[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00]  
>>> print a  
[ 6.64e+00-j8.87e+00]  
[ 8.48e+00+j1.13e+01]  
[-1.39e+01-j0.00e+00]  
[-6.62e+00-j0.00e+00]  
[-2.89e+01-j0.00e+00]  
>>> print b  
[ 6.46e-01]  
[ 8.25e-01]  
[ 7.41e-01]  
[ 8.75e-01]  
[ 0.00e+00]