We now discuss an algorithm for computing the SVD based on the derivation in the previous lecture. We use the QR algorithm to compute the singular values. We first note that the QR factorization can be extended to work on singular $n \times n$ matrices $A$, but $r_{ii} = 0$ for some $i$.
A = rand(5); A(:,2) = A(:,1); A(:,4) = A(:,5);
[Q,R] = QRsing(A,1e-12)
norm(Q'*Q - eye(5))
norm(A-Q*R)
This allows the QR eigenvalue algorithm to find zero eigenvalues.
A = rand(5); A(:,2) = A(:,1); A(:,4) = A(:,5); A = A'*A;
QRalg(A,100,1e-12)'
We can also have the QR algorithm return eigenvectors.
m = 7; n = 5;
A = randn(7,5); A(:,2) = A(:,1); A(:,4) = A(:,5); AtA = A'*A;
[Q,D] = QRsys(AtA,1000,1e-13)
norm(AtA - Q*diag(D)*Q')
We can now build up $V$ and $\Sigma$:
V = Q;
sv = real(sqrt(D));
S = [diag(sv); zeros(m-n,n) ] % this is Sigma
Then, we compute the (numerical) rank.
rank = 0;
for i = 1:n
if S(i,i) < 1e-10
break
end
rank = rank + 1;
end
rank
Then we fill out $U$, adding appropriate orthogonal columns.
U = zeros(m);
for i = 1:rank
U(:,i) = A*V(:,i)/S(i,i);
end
U(:,rank+1:end) = randn(m,m-rank);
for j = rank+1:m
for i=1:j-1
U(:,j) = U(:,j) - (U(:,i)'*U(:,j))*U(:,i);
end
U(:,j) = U(:,j)/norm(U(:,j));
end
Now, we test our factorization.
norm(A-U*S*V')
norm(U'*U - eye(length(U)))
norm(V'*V - eye(length(V)))
A = imread('demo.png');
A = rgb2gray(A);
imshow(A)
For this image, the first singular value is dominant.
[U,S,V] = svd(double(A));
S(1:5,1:5)
If we reconstruct the image using only the first singular value, we get a surprisingly good idea what the picture is.
k = 1; % A = U S V^T
W = V';
[m,n] = size(A);
B = U(:,1:k)*S(1:k,1:k)*W(1:k,:);
B = uint8(B);
imshow(B)
fprintf('Compression ratio = %.5f', (m*k+k+n*k)/(m*n))
This required less then 1% of the storage required to store the full matrix.
Next, we look at the size of the singular values, relative to the top singular value:
diag(S(10:30,10:30))'/S(1,1) % Look at singular values 10 through 30, in relative size
The 20th singular value is less than 1% of the magnitude of the top singular value. So, we will chop there and see what image we reconstruct.
k = 20; % A = U S V^T
W = V';
[m,n] = size(A);
B = U(:,1:k)*S(1:k,1:k)*W(1:k,:);
B = uint8(B);
imshow(B)
fprintf('Compression ratio = %.5f', (m*k+k+n*k)/(m*n))
With just over 10% of the storage, we can compress the image so that it is at least recognizable. There are some issues that affect the true amount of storage. These are related to the fact that the original image was an integer matrix and $U,V,S$ are now double precision numbers. Whether justified, or not, we ignore that here for simplicity.