Decompositions, factorisations, inverses and equation solvers (sparse matrices)
Source:vignettes/decompositions-factorisations-inverses-and-equation-solvers-sparse.Rmd
decompositions-factorisations-inverses-and-equation-solvers-sparse.Rmd
This vignette is adapted from the official Armadillo documentation.
Eigen decomposition of a symmetric matrix
Obtain a limited number of eigenvalues and eigenvectors of sparse symmetric real matrix X
.
Usage:
vec eigval = eigs_sym(X, k)
vec eigval = eigs_sym(X, k, form)
vec eigval = eigs_sym(X, k, form, opts)
vec eigval = eigs_sym(X, k, sigma)
vec eigval = eigs_sym(X, k, sigma, opts)
eigs_sym(eigval, X, k)
eigs_sym(eigval, X, k, form)
eigs_sym(eigval, X, k, form, opts)
eigs_sym(eigval, X, k, sigma)
eigs_sym(eigval, X, k, sigma, opts)
eigs_sym(eigval, eigvec, X, k)
eigs_sym(eigval, eigvec, X, k, form)
eigs_sym(eigval, eigvec, X, k, form, opts)
eigs_sym(eigval, eigvec, X, k, sigma) eigs_sym(eigval, eigvec, X, k, sigma, opts)
k
specifies the number of eigenvalues and eigenvectors.
The argument form
is optional and is one of the following:
-
"lm"
: obtain eigenvalues with largest magnitude (default operation). -
"sm"
: obtain eigenvalues with smallest magnitude (see the caveats below). -
"la"
: obtain eigenvalues with largest algebraic value.
The argument sigma
is optional; if sigma
is given, eigenvalues closest to sigma
are found via shift-invert mode. Note that to use sigma
, both ARMA_USE_ARPACK
and ARMA_USE_SUPERLU
must be enabled in armadillo/config.hpp
.
The opts
argument is optional; opts
is an instance of the eigs_opts
structure:
struct eigs_opts
{double tol; // default: 0
unsigned int maxiter; // default: 1000
unsigned int subdim; // default: max(2*k+1, 20)
};
-
tol
specifies the tolerance for convergence. -
maxiter
specifies the maximum number of Arnoldi iterations. -
subdim
specifies the dimension of the Krylov subspace, with the constraintk < subdim <= X.n_rows
; the recommended value issubdim >= 2*k
.
The eigenvalues and corresponding eigenvectors are stored in eigval
and eigvec
, respectively.
If X
is not square sized, an error is thrown.
If the decomposition fails:
-
eigval = eigs_sym(X,k)
resetseigval
and throws an error. -
eigs_sym(eigval,X,k)
resetseigval
and returns a bool set to false (an error is not thrown). -
eigs_sym(eigval,eigvec,X,k)
resetseigval
andeigvec
and returns a bool set to false (an error is not thrown).
Caveats:
- The number of obtained eigenvalues/eigenvectors may be lower than requested, depending on the given data.
- If the decomposition fails, try first increasing
opts.subdim
(Krylov subspace dimension), and, as secondary options, try increasingopts.maxiter
(maximum number of iterations), and/oropts.tol
(tolerance for convergence), and/ork
(number of eigenvalues). - For an alternative to the
"sm"
form, use the shift-invert mode withsigma
set to0.0
. - The implementation in Armadillo 12.6 is considerably faster than earlier versions; further speedups can be obtained by enabling OpenMP in your compiler (e.g.,
-fopenmp
in GCC and clang).
Examples
cpp11::register]] list eig_sym2_(const doubles_matrix<>& x,
[[const char* method,
const int& k) {
sp_mat X = as_SpMat(x);
sp_mat Y = X.t() * X;
vec eigval;
mat eigvec;
eigs_opts opts;10000;
opts.maxiter = bool ok = eigs_sym(eigval, eigvec, Y, k, method, opts);
3);
writable::list out(0] = writable::logicals({ok});
out[1] = as_doubles(eigval);
out[2] = as_doubles_matrix(eigvec);
out[
return out;
}
Eigen decomposition of a general matrix
Obtain a limited number of eigenvalues and eigenvectors of sparse general (non-symmetric/non-hermitian) square matrix X
.
Usage:
cx_vec eigval = eigs_gen(X, k)
cx_vec eigval = eigs_gen(X, k, form)
cx_vec eigval = eigs_gen(X, k, sigma)
cx_vec eigval = eigs_gen(X, k, form, opts)
cx_vec eigval = eigs_gen(X, k, sigma, opts)
eigs_gen(eigval, X, k)
eigs_gen(eigval, X, k, form)
eigs_gen(eigval, X, k, sigma)
eigs_gen(eigval, X, k, form, opts)
eigs_gen(eigval, X, k, sigma, opts)
eigs_gen(eigval, eigvec, X, k)
eigs_gen(eigval, eigvec, X, k, form)
eigs_gen(eigval, eigvec, X, k, sigma)
eigs_gen(eigval, eigvec, X, k, form, opts) eigs_gen(eigval, eigvec, X, k, sigma, opts)
k
specifies the number of eigenvalues and eigenvectors.
The argument form
is optional; form
is one of the following:
-
"lm"
: obtain eigenvalues with largest magnitude (default operation). -
"sm"
: obtain eigenvalues with smallest magnitude (see the caveats below). -
"lr"
: obtain eigenvalues with largest real part. -
"sr"
: obtain eigenvalues with smallest real part. -
"li"
: obtain eigenvalues with largest imaginary part. -
"si"
: obtain eigenvalues with smallest imaginary part.
The argument sigma
is optional; if sigma
is given, eigenvalues closest to sigma
are found via shift-invert mode. Note that to use sigma
, both ARMA_USE_ARPACK
and ARMA_USE_SUPERLU
must be enabled in armadillo/config.hpp
.
The opts
argument is optional; opts
is an instance of the eigs_opts
structure:
struct eigs_opts
{double tol; // default: 0
unsigned int maxiter; // default: 1000
unsigned int subdim; // default: max(2*k+1, 20)
};
-
tol
specifies the tolerance for convergence. -
maxiter
specifies the maximum number of Arnoldi iterations. -
subdim
specifies the dimension of the Krylov subspace, with the constraintk < subdim <= X.n_rows
; the recommended value issubdim >= 2*k
.
The eigenvalues and corresponding eigenvectors are stored in eigval
and eigvec
, respectively.
If X
is not square sized, an error is thrown.
If the decomposition fails:
-
eigval = eigs_gen(X, k)
resetseigval
and throws an error. -
eigs_gen(eigval, X, k)
resetseigval
and returns a bool set to false (an error is not thrown). -
eigs_gen(eigval,eigvec, X, k)
resetseigval
andeigvec
and returns a bool set to false (an error is not thrown).
Caveats:
- The number of obtained eigenvalues/eigenvectors may be lower than requested, depending on the given data.
- If the decomposition fails, try first increasing
opts.subdim
(Krylov subspace dimension), and, as secondary options, try increasingopts.maxiter
(maximum number of iterations), and/oropts.tol
(tolerance for convergence), and/ork
(number of eigenvalues). - For an alternative to the
"sm"
form, use the shift-invert mode withsigma
set to0.0
.
Examples
cpp11::register]] list eig_gen2_(const doubles_matrix<>& x,
[[const char* method,
const int& k) {
sp_mat X = as_SpMat(x);
cx_vec eigval;
cx_mat eigvec;
eigs_opts opts;10000;
opts.maxiter =
bool ok = eigs_gen(eigval, eigvec, X, k, method, opts);
3);
writable::list out(0] = writable::logicals({ok});
out[1] = as_complex_doubles(eigval);
out[2] = as_complex_matrix(eigvec);
out[
return out;
}
Truncated singular value decomposition
Obtain a limited number of singular values and singular vectors (truncated SVD) of a sparse matrix.
The singular values and vectors are calculated via sparse eigen decomposition of:
where and are the number of rows and columns of X
, respectively.
Usage:
vec s = svds(X, k)
vec s = svds(X, k, tol)
svds(vec s, X, k)
svds(vec s, X, k, tol)
svds(mat U, vec s, mat V, sp_mat X, k)
svds(mat U, vec s, mat V, sp_mat X, k, tol)
svds(cx_mat U, vec s, cx_mat V, sp_cx_mat X, k) svds(cx_mat U, vec s, cx_mat V, sp_cx_mat X, k, tol)
k
specifies the number of singular values and singular vectors.
The singular values are in descending order.
The argument tol
is optional; it specifies the tolerance for convergence, and it is passed as tol / sqrt(2)
to eigs_sym
.
If the decomposition fails:
-
s = svds(X, k)
resetss
and throws an error. -
svds(s, X, k)
resetss
and returns a bool set to false (an error is not thrown). -
svds(U, s, V, X, k)
resetsU
,s
, andV
and returns a bool set to false (an error is not thrown).
Caveats:
-
svds
is intended only for finding a few singular values from a large sparse matrix; to find all singular values, usesvd
instead. - Depending on the given matrix,
svds
may find fewer singular values than specified. - The implementation in Armadillo 12.6 is considerably faster than earlier versions; further speedups can be obtained by enabling OpenMP in your compiler (e.g.,
-fopenmp
in GCC and clang).
Examples
cpp11::register]] list svds1_(const doubles_matrix<>& x, const int& k) {
[[
sp_mat X = as_SpMat(x);
// convert all values below 0.1 to zero
double val) { return (std::abs(val) < 0.1) ? 0 : val; });
X.transform([](
mat U;
vec s;
mat V;
bool ok = svds(U, s, V, X, k);
4);
writable::list out(0] = writable::logicals({ok});
out[1] = as_doubles(s);
out[2] = as_doubles_matrix(U);
out[3] = as_doubles_matrix(V);
out[
return out;
}
Solve a system of linear equations
Solve a sparse system of linear equations, , where is a sparse matrix, is a dense matrix or vector, and is unknown.
The number of rows in and must be the same.
Usage:
// A = matrix, b = vector
vec x = spsolve(A, b)
vec x = spsolve(A, b, solver)
vec x = spsolve(A, b, solver, opts)
// A = matrix, B = matrix
mat X = spsolve(A, B)
spsolve(x, A, b)
spsolve(x, A, b, solver) spsolve(x, A, b, solver, opts)
The solver
argument is optional; solver
is either "superlu"
(default) or "lapack"
.
- For
"superlu"
,ARMA_USE_SUPERLU
must be enabled inarmadillo/config.hpp
. - For
"lapack"
, the sparse matrix is converted to a dense matrix before using the LAPACK solver. This considerably increases memory usage.
The opts
argument is optional and applicable to the SuperLU solver, and is an instance of the superlu_opts
structure:
struct superlu_opts
{bool allow_ugly; // default: false
bool equilibrate; // default: false
bool symmetric; // default: false
double pivot_thresh; // default: 1.0
permutation_type permutation; // default: superlu_opts::COLAMD
refine_type refine; // default: superlu_opts::REF_NONE
};
-
allow_ugly
is eithertrue
orfalse
; it indicates whether to keep solutions of systems singular to working precision. -
equilibrate
is eithertrue
orfalse
; it indicates whether to equilibrate the system (scale the rows and columns of to have unit norm). -
symmetric
is eithertrue
orfalse
; it indicates whether to use SuperLU symmetric mode, which gives preference to diagonal pivots. -
pivot_thresh
is in the range [0.0, 1.0], used for determining whether a diagonal entry is an acceptable pivot (details in SuperLU documentation). -
permutation
specifies the type of column permutation; it is one of:-
superlu_opts::NATURAL
: natural ordering. -
superlu_opts::MMD_ATA
: minimum degree ordering on structure of . -
superlu_opts::MMD_AT_PLUS_A
: minimum degree ordering on structure of . -
superlu_opts::COLAMD
: approximate minimum degree column ordering.
-
-
refine
specifies the type of iterative refinement; it is one of:-
superlu_opts::REF_NONE
: no refinement. -
superlu_opts::REF_SINGLE
: iterative refinement in single precision. -
superlu_opts::REF_DOUBLE
: iterative refinement in double precision. -
superlu_opts::REF_EXTRA
: iterative refinement in extra precision.
-
If no solution is found:
-
x = spsolve(A, b)
resetsx
and throws an error. -
spsolve(x, A, b)
resetsx
and returns a bool set to false (an error is not thrown).
The SuperLU solver is mainly useful for very large and/or highly sparse matrices.
To reuse the SuperLU factorisation of for finding solutions where is iteratively changed, see the spsolve_factoriser
class.
If there is sufficient memory to store a dense version of matrix , the LAPACK solver can be faster.
Factorise a sparse matrix for solving linear systems
Class for factorisation of sparse matrix for solving systems of linear equations in the form .
Allows the SuperLU factorisation of to be reused for finding solutions in cases where is iteratively changed.
For an instance of spsolve_factoriser
named as SF
, the member functions are:
-
SF.factorise(A. opts)
: factorise square-sized sparse matrix $A. Optional settings are given in the
optsargument as per the
spsolve()` function. If the factorisation fails, a bool set to false is returned. -
SF.solve(X, B)
: using the given dense matrix and the computed factorisation, store in the solution to $AX = B`. If computing the solution fails, is reset and a bool set to false is returned. -
SF.rcond()
: return the 1-norm estimate of the reciprocal condition number computed during the factorisation. Values close to 1 suggest that the factorised matrix is well-conditioned. Values close to 0 suggest that the factorised matrix is badly conditioned. -
SF.reset()
: reset the instance and release all memory related to the stored factorisation; this is automatically done when the instance goes out of scope.
Caveats:
- If the factorisation of does not need to be reused, use
spsolve()
instead. - This class internally uses the SuperLU solver;
ARMA_USE_SUPERLU
must be enabled inconfig.hpp
.
Examples
cpp11::register]] list spsolve_factoriser1_(const doubles_matrix<>& a,
[[const list& b) {
sp_mat A = as_SpMat(a);
bool status = SF.factorise(A);
if (status == false) {
"factorisation failed");
stop(
}
double rcond_value = SF.rcond();
0]);
vec B1 = as_Col(b[1]);
vec B2 = as_Col(b[
vec X1, X2;
bool ok1 = SF.solve(X1, B1);
bool ok2 = SF.solve(X2, B2);
if (ok1 == false) {
"couldn't find X1");
stop(
}
if (ok2 == false) {
"couldn't find X2");
stop(
}
3);
writable::list out(0] = writable::logicals({status && ok1 && ok2});
out[1] = as_doubles(X1);
out[2] = as_doubles(X2);
out[
return out;
}