In
Section 4.6 we saw that the
singular_value_decomposition
algorithm in SymPy does things a little bit differently than in
Section 4.6. If we start with a square matrix
the results are the same, but if
is not square, the decomposition
looks a little different. In particular, if
is
the matrix
defined in
Section 4.6 will also be
but it will contain some rows or columns of zeros that are added to get the desired size.
The matrix is an orthogonal matrix whose columns are an orthonormal basis of eigenvectors for The matrix is an orthogonal matrix whose columns are an orthonormal basis of (The first columns of are given by where is the eigenvector of corresponding to the positive singular value )
The algorithm provided by SymPy replaces by the diagonal matrix of nonzero singular values. (This is common in most algorithms, since we don’t want to bother storing data we don’t need.) The matrix is replaced by the matrix whose columns are the first eigenvectors of and the matrix is replaced by the matrix whose columns are the orthonormal basis for the column space of (Note that the rank of is equal to the rank of which is equal to the number of nonzero eigenvectors of (counted with multiplicity).)
The product will be the same in both cases, and the matrix is the same as well.
This time, rather than using the SymPy algorithm, we will work through the process outlined in
Section 4.6 step-by-step. Let’s revisit
Example 4.6.4. Let
First, we get the singular values:
Next, we get the eigenvalues and eigenvectors of
Now we need to normalize the eigenvectors, in the correct order. Note that the eigenvectors were listed in increasing order of eigenvalue, so we need to reverse the order. Note that L1
is a list of lists. The eigenvector is the third entry (index 2) in the list (eigenvalue, multiplicity, eigenvector). We also need to turn list elements into matrices. So, for example the second eigenvector is Matrix(L1[1][2])
.
Next, we can assemble these vectors into a matrix, and confirm that it’s orthogonal.
We’ve made the matrix Next, we construct This we will do by hand. (Can you think of a way to do it automatically?)
Alternatively, you could do SigA = diag(L0[0],L0[1]).row_join(Matrix([0,0]))
. Finally, we need to make the matrix First, we find the vectors and normalize. (Note that so this vector is unneeded, as expected.)
Note that the matrix is already the correct size, because In general, for an matrix if we would have to extend the set to a basis for Finally, we check that our matrices work as advertised.
For convenience, here is all of the above code, with all print commands (except the last one) removed.
from sympy import Matrix,BlockMatrix,init_printing
init_printing()
A = Matrix([[1,1,1],[1,0,-1]])
B=(A.T)*A
L0=A.singular_values()
L1=B.eigenvects()
R1=Matrix(L1[2][2])
R2=Matrix(L1[1][2])
R3=Matrix(L1[0][2])
Q1 = (1/R1.norm())*R1
Q2 = (1/R2.norm())*R2
Q3 = (1/R3.norm())*R3
Q = Matrix(BlockMatrix([Q1,Q2,Q3]))
SigA = diag(L0[0],L0[1]).row_join(Matrix([0,0]))
S1 = A*Q1
S2 = A*Q2
P1 = (1/S1.norm())*S1
P2 = (1/S2.norm())*S2
P = Matrix(BlockMatrix([P1,P2]))
P,SigA,Q,P*SigA*Q.T
1.
Compute the SVD for the matrices
Note that for these matrices, you may need to do some additional work to extend the vectors to an orthonormal basis. You can adapt the code above, but you will have to think about how to implement additional code to construct any extra vectors you need.
2.
By making some very minor changes in the matrices in
Worksheet Exercise 4.7.1, convince yourself that (a) those matrices were chosen very carefully, and (b) there’s a reason why most people do
SVD numerically.