Singular Value Decomposition and Pseudo-Inverse
Implementing Singular Value Decomposition and Pseudo-Inverse algorithms and comparing them to R’s built in algorithms to test their accuracy.
.rmd knitted with execution results available for download here
Q1
Compute the SVD of the following matrices
A = matrix(c(c(1,0), c(1,0)), nc=2, nr=2)A## [,1] [,2]## [1,] 1 1## [2,] 0 0B = matrix(c(c(-2,0), c(0, 0)), nc=2, nr=2)B## [,1] [,2]## [1,] -2 0## [2,] 0 0C = matrix(c(c(2,0), c(0,2), c(1,0)), nc=3, nr=2)C## [,1] [,2] [,3]## [1,] 2 0 1## [2,] 0 2 0The SVD formula
The SVD of any matrix ‘M’, size ‘m’ by ‘n’ is:
M = U * E * V^T,
where U is the orthonormal eigenvectors of M * M^T,
and where V is the orthonormal eigenvectors of M^T * M,
and where E is a diagonal matrix of the singular values (square root of eigenvalues) of size ‘m’ by ‘n’
SVD of A
# calculating UAAt = A %*% t(A)eu = eigen(AAt)# for some reason the eigenvectors are negative, I assume this has something to do with the eigen() implementation# it should be noted that below, when using svd(), U has no negative values.# So, since U needs to be the orthonormal eigenvectors of 'AAt', we can just multiply both eigenvectors# by -1, and they will remain orthonormal eigenvectors of 'AAt' associated with the same eigenvalues
Ua = matrix(c(-1 * eu$vectors), nc=2, nr=2)Ua## [,1] [,2]## [1,] 1 0## [2,] 0 1# calculating VAtA = t(A) %*% Aev = eigen(AtA)Va = matrix(c(ev$vectors), nc=2, nr=2)Va## [,1] [,2]## [1,] 0.7071068 -0.7071068## [2,] 0.7071068 0.7071068# calculate E# looking at the eigenvalueseu$values## [1] 2 0ev$values## [1] 2 0# the only non-zero eigenvalue is 2,# so the only singular value put in the E matrix is sqrt(2)# so E is just sqrt(2) on the main diagonal, and the rest is zeros# E needs to be size 'm' by 'n', so 2 by 2Ea = matrix(c(c(sqrt(eu$values)), c(0, 0)), nc=2, nr=2)Ea## [,1] [,2]## [1,] 1.414214 0## [2,] 0.000000 0# now the result of U %*% E %*% t(V) should be equal to A againA == Ua %*% Ea %*% t(Va)## [,1] [,2]## [1,] TRUE TRUE## [2,] TRUE TRUE# using the SVD function built in to calculate svd of AsvdA = svd(A)# U issvdA$u## [,1] [,2]## [1,] 1 0## [2,] 0 1# V issvdA$v## [,1] [,2]## [1,] 0.7071068 -0.7071068## [2,] 0.7071068 0.7071068# the singular values of A aresvdA$d## [1] 1.414214 0.000000# set up the diagonal E matrix using the non-zero singular values svdA$d# E is 'm' by 'n', so 2 by 2, with the singular values on the diagonalE = diag(svdA$d, nc=2, nr=2)E## [,1] [,2]## [1,] 1.414214 0## [2,] 0.000000 0res = svdA$u %*% E %*% t(svdA$v)res## [,1] [,2]## [1,] 1 1## [2,] 0 0A## [,1] [,2]## [1,] 1 1## [2,] 0 0# on my machine, this returns FALSE when comparing the 1's in 'res' to the 1's in 'A'# I assume this is some floating point arithmetic error, but the value are both 1's of 'num' type# I legitimately don't know why it does this, but taking the ceiling of both matrices works,# so it must be some floating point error?res == A## [,1] [,2]## [1,] FALSE FALSE## [2,] TRUE TRUE# what????ceiling(res) == ceiling(A)## [,1] [,2]## [1,] TRUE TRUE## [2,] TRUE TRUESVD of B
# calculate UBBt = B %*% t(B)eu = eigen(BBt)# once again, the eigenvectors are negative for some reason, in this case we want a negative to get the '-2' in BUb = matrix(c(eu$vectors), nc=2, nr=2)Ub## [,1] [,2]## [1,] -1 0## [2,] 0 -1# calculate VBtB = t(B) %*% Bev = eigen(BtB)# these eigenvectors are all negative too,# but since they're already negative in U, this will cause the 'SVD = -B'# so multiply these eigenvectors by -1 (they're still othonormal eigenvectors of BtB)Vb = matrix(c(-1 * ev$vectors), nc=2, nr=2)Vb## [,1] [,2]## [1,] 1 0## [2,] 0 1# calculate Eeu$values## [1] 4 0ev$values## [1] 4 0Eb = diag(c(c(sqrt(eu$values))), nc=2, nr=2)Eb## [,1] [,2]## [1,] 2 0## [2,] 0 0res = Ub %*% Eb %*% t(Vb)res## [,1] [,2]## [1,] -2 0## [2,] 0 0res == B## [,1] [,2]## [1,] TRUE TRUE## [2,] TRUE TRUE# using built in SVD to calculate svd of BsvdB = svd(B)# U issvdB$u## [,1] [,2]## [1,] 1 0## [2,] 0 1# V issvdB$v## [,1] [,2]## [1,] -1 0## [2,] 0 1# singular values of B aresvdB$d## [1] 2 0# now create the diagonal E matrix using the singular values on the main diagonal# E has the same dimensions as B, so 2 by 2E = diag(svdB$d, nc=2, nr=2)E## [,1] [,2]## [1,] 2 0## [2,] 0 0resB = svdB$u %*% E %*% t(svdB$v)resB## [,1] [,2]## [1,] -2 0## [2,] 0 0B## [,1] [,2]## [1,] -2 0## [2,] 0 0resB == B## [,1] [,2]## [1,] TRUE TRUE## [2,] TRUE TRUESVD of C
# calculate UCCt = C %*% t(C)eu = eigen(CCt)# negative again, for some reason, you know the drill by nowUc = matrix(c(-1 * eu$vectors), nc=2, nr=2)Uc## [,1] [,2]## [1,] 1 0## [2,] 0 1# calculate VCtC = t(C) %*% Cev = eigen(CtC)# the second eigenvector is negative for some reasonev$vectors[,2]## [1] 0 -1 0# make it non-negative, to make the SVD work (remains orthonormal eigenvector)ev$vectors[,2] = -1 * ev$vectors[,2]Vc = matrix(c(ev$vectors), nc=3, nr=3)Vc## [,1] [,2] [,3]## [1,] 0.8944272 0 0.4472136## [2,] 0.0000000 1 0.0000000## [3,] 0.4472136 0 -0.8944272# calculate Eeu$values## [1] 5 4# floating point error, round eigenvaluesround(ev$values, 14)## [1] 5 4 0# non-zero eigenvalues are 5 and 4# size of E is the same as C, so a 2 x 3 matrixEc = diag(sqrt(eu$values), nc=3, nr=2)Ec## [,1] [,2] [,3]## [1,] 2.236068 0 0## [2,] 0.000000 2 0resC = Uc %*% Ec %*% t(Vc)resC## [,1] [,2] [,3]## [1,] 2 0 1## [2,] 0 2 0C## [,1] [,2] [,3]## [1,] 2 0 1## [2,] 0 2 0# weird floating point stuff??round(resC) == round(C)## [,1] [,2] [,3]## [1,] TRUE TRUE TRUE## [2,] TRUE TRUE TRUE# using built in SVD to calculate svd of CsvdC = svd(C)# U issvdC$u## [,1] [,2]## [1,] 1 0## [2,] 0 1# V issvdC$v## [,1] [,2]## [1,] 0.8944272 0## [2,] 0.0000000 1## [3,] 0.4472136 0# this only gives two eigenvectors for V# this is because the eigenvalue associated with the third eigenvector is 0# so the third eigenvector is technically arbitrary, but I included it for matrix size consistency
# the singular values of C aresvdC$d## [1] 2.236068 2.000000# now create E, diagonal matrix w/ same dimensions as C, so a 2 x 3 matrix# however, since 'svdC$v' only contains 2 vectors, E will have to be a 2 x 2 matrixE = diag(svdC$d, nc=2, nr=2)E## [,1] [,2]## [1,] 2.236068 0## [2,] 0.000000 2resC = svdC$u %*% E %*% t(svdC$v)resC## [,1] [,2] [,3]## [1,] 2 0 1## [2,] 0 2 0C## [,1] [,2] [,3]## [1,] 2 0 1## [2,] 0 2 0# float stuff againround(resC) == round(C)## [,1] [,2] [,3]## [1,] TRUE TRUE TRUE## [2,] TRUE TRUE TRUEQ2
Compute the Pseudo-Inverses of the matrices given in Q1
The pseudo-inverse of a matrix M uses the SVD of that matrix.
Given the SVD of M: M = U * E * V^T,
The pseudo-inverse of M, ‘M^t’ is:
M^t = V * E^t * U^T
Where E^t is the pseudo inverse of E, specifically, a diagonal matrix of the reciprocal of the non-zero singular values of ‘M’
Pseudo-Inverse of A
# have U and V, need to make E^t
# get the eigenvaluessing = eigen(A %*% t(A))$values# invert the non-zero entries, take the square rootsing = sqrt(c(sing[sing != 0]^-1, sing[sing == 0]))sing## [1] 0.7071068 0.0000000# make diagonal matrix of non-zero singular reciprocalsEt = diag(sing, nc=2, nr=2)Et## [,1] [,2]## [1,] 0.7071068 0## [2,] 0.0000000 0At = Va %*% Et %*% t(Ua)At## [,1] [,2]## [1,] 0.5 0## [2,] 0.5 0Pseudo-Inverse of B
# have U and V, need to make E^t
# get the eigenvaluessing = eigen(B %*% t(B))$values# invert the non-zero entries, take the square rootsing = sqrt(c(sing[sing != 0]^-1, sing[sing == 0]))sing## [1] 0.5 0.0# make diagonal matrix of non-zero singular reciprocalsEt = diag(sing, nc=2, nr=2)Et## [,1] [,2]## [1,] 0.5 0## [2,] 0.0 0Bt = Vb %*% Et %*% t(Ub)Bt## [,1] [,2]## [1,] -0.5 0## [2,] 0.0 0Pseudo-Inverse of C
# have U and V, need to make E^t
# get the eigenvaluessing = eigen(C %*% t(C))$values# invert the non-zero entries, take the square rootsing = sqrt(c(sing[sing != 0]^-1, sing[sing == 0]))sing## [1] 0.4472136 0.5000000# make diagonal matrix of non-zero singular reciprocals# needs to be 3 x 2 to matrix multiply w/ VEt = diag(sing, nc=2, nr=3)Et## [,1] [,2]## [1,] 0.4472136 0.0## [2,] 0.0000000 0.5## [3,] 0.0000000 0.0Ct = Vc %*% Et %*% t(Uc)Ct## [,1] [,2]## [1,] 0.4 0.0## [2,] 0.0 0.5## [3,] 0.2 0.0