Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial svds support based on eigs #9425

Merged
merged 9 commits into from
Jan 11, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,7 @@ export
svd,
svdfact!,
svdfact,
svds,
svdvals!,
svdvals,
sylvester,
Expand Down
1 change: 1 addition & 0 deletions base/linalg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export
svd,
svdfact!,
svdfact,
svds,
svdvals!,
svdvals,
sylvester,
Expand Down
33 changes: 33 additions & 0 deletions base/linalg/arnoldi.jl
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,36 @@ function eigs(A, B;
resid, ncv, v, ldv, sigma, iparam, ipntr, workd, workl, lworkl, rwork)

end


## svds

type SVDOperator{T,S} <: AbstractArray{T, 2}
X::S
m::Int
n::Int
SVDOperator(X::S) = new(X, size(X,1), size(X,2))
end

## v = [ left_singular_vector; right_singular_vector ]
*{T,S}(s::SVDOperator{T,S}, v::Vector{T}) = [s.X * v[s.m+1:end]; s.X' * v[1:s.m]]
size(s::SVDOperator) = s.m + s.n, s.m + s.n
issym(s::SVDOperator) = true

function svds{S}(X::S; nsv::Int = 6, ritzvec::Bool = true, tol::Float64 = 0.0, maxiter::Int = 1000)
if nsv > minimum(size(X)); error("nsv($nsv) should be at most $(minimum(size(X)))"); end

otype = eltype(X)
ex = eigs(SVDOperator{otype,S}(X), I; ritzvec = ritzvec, nev = 2*nsv, tol = tol, maxiter = maxiter)
ind = [1:2:nsv*2]
sval = abs(ex[1][ind])

if ! ritzvec
return sval, ex[2], ex[3], ex[4], ex[5]
end

## calculating singular vectors
left_sv = sqrt(2) * ex[2][ 1:size(X,1), ind ] .* sign(ex[1][ind]')
right_sv = sqrt(2) * ex[2][ size(X,1)+1:end, ind ]
return left_sv, sval, right_sv, ex[3], ex[4], ex[5], ex[6]
end
19 changes: 18 additions & 1 deletion doc/stdlib/linalg.rst
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ Linear algebra functions in Julia are largely implemented by calling functions f

Conjugate transpose array ``src`` and store the result in the preallocated array ``dest``, which should have a size corresponding to ``(size(src,2),size(src,1))``. No in-place transposition is supported and unexpected results will happen if `src` and `dest` have overlapping memory regions.

.. function:: eigs(A, [B,]; nev=6, which="LM", tol=0.0, maxiter=1000, sigma=nothing, ritzvec=true, v0=zeros((0,))) -> (d,[v,],nconv,niter,nmult,resid)
.. function:: eigs(A, [B,]; nev=6, which="LM", tol=0.0, maxiter=300, sigma=nothing, ritzvec=true, v0=zeros((0,))) -> (d,[v,],nconv,niter,nmult,resid)

``eigs`` computes eigenvalues ``d`` of ``A`` using Lanczos or Arnoldi iterations for real symmetric or general nonsymmetric matrices respectively. If ``B`` is provided, the generalized eigen-problem is solved. The following keyword arguments are supported:
* ``nev``: Number of eigenvalues
Expand Down Expand Up @@ -600,6 +600,23 @@ Linear algebra functions in Julia are largely implemented by calling functions f
real or complex inverse with level shift ``sigma`` :math:`(A - \sigma I )^{-1}`
=============== ================================== ==================================

.. function:: svds(A; ritzvec=true, args...) -> (left_sv, s, right_sv, nconv, niter, nmult, resid)

``svds`` computes singular values ``s`` of ``A`` using Lanczos or Arnoldi iterations. Uses ``eigs`` underneath so following keyword arguments are supported:
* ``nev``: Number of singular values.
* ``ncv``: Number of Krylov vectors used in the computation; see ``eigs`` manual.
* ``ritzvec``: Whether to return the left and right singular vectors ``left_sv`` and ``right_sv``, default is ``true``. If ``false`` the singular vectors are omitted from the output.
* ``which``: type of singular values (and vectors) to compute, default is largest values. See ``eigs`` manual.
* ``tol``: tolerance, see ``eigs``.
* ``maxiter``: Maximum number of iterations, see ``eigs``.
* ``sigma``: See ``eigs``.
* ``v0``: starting vector of right singular vector from which to start the iterations.

**Example**::

X = sprand(10, 5, 0.2)
svds(X, nev = 2)

.. function:: peakflops(n; parallel=false)

``peakflops`` computes the peak flop rate of the computer by using double precision :func:`Base.LinAlg.BLAS.gemm!`. By default, if no arguments are specified, it multiplies a matrix of size ``n x n``, where ``n = 2000``. If the underlying BLAS is using multiple threads, higher flop rates are realized. The number of BLAS threads can be set with ``blas_set_num_threads(n)``.
Expand Down
39 changes: 39 additions & 0 deletions test/linalg/arnoldi.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Base.Test

let # svds test
A = sparse([1, 1, 2, 3, 4], [2, 1, 1, 3, 1], [2.0, -1.0, 6.1, 7.0, 1.5])
S1 = svds(A, nsv = 2)
S2 = svd(full(A))

## singular values match:
@test_approx_eq S1[2] S2[2][1:2]

## 1st left singular vector
s1_left = sign(S1[1][3,1]) * S1[1][:,1]
s2_left = sign(S2[1][3,1]) * S2[1][:,1]
@test_approx_eq s1_left s2_left

## 1st right singular vector
s1_right = sign(S1[3][3,1]) * S1[3][:,1]
s2_right = sign(S2[3][3,1]) * S2[3][:,1]
@test_approx_eq s1_right s2_right
end

let # complex svds test
A = sparse([1, 1, 2, 3, 4], [2, 1, 1, 3, 1], exp(im*[2.0:2:10]))
S1 = svds(A, nsv = 2)
S2 = svd(full(A))

## singular values match:
@test_approx_eq S1[2] S2[2][1:2]

## left singular vectors
s1_left = abs(S1[1][:,1:2])
s2_left = abs(S2[1][:,1:2])
@test_approx_eq s1_left s2_left

## right singular vectors
s1_right = abs(S1[3][:,1:2])
s2_right = abs(S2[3][:,1:2])
@test_approx_eq s1_right s2_right
end