Module: NuLin
- Defined in:
- lib/nulin.rb,
lib/nulin/qr.rb,
lib/nulin/det.rb,
lib/nulin/lls.rb,
lib/nulin/svd.rb,
lib/nulin/gemm.rb,
lib/nulin/cholesky.rb,
lib/nulin/eigensystem.rb,
ext/nulin_native.c
Defined Under Namespace
Modules: Native Classes: Cholesky, ComplexEigenDecomposition, DimensionError, EigenDecomposition, LLS, LLS_QR, LLS_SVD, LinalgError, QR, RealEigenDecomposition, SVD
Constant Summary collapse
- TYPECODES =
[NArray::SFLOAT, NArray::DFLOAT, NArray::SCOMPLEX, NArray::DCOMPLEX]
- LAPACK_PREFIX =
{ NArray::SFLOAT => "s", NArray::DFLOAT => "d", NArray::SCOMPLEX => "c", NArray::DCOMPLEX => "z", }
Class Method Summary collapse
-
.cholesky(matrix, options = {}) ⇒ Object
Compute the Cholesky decomposition for a positive definite symmetric/Hermitian ‘matrix`.
-
.det(matrix) ⇒ Object
Compute the determinant of the ‘matrix` and return it.
-
.eigensystem(matrix, options = {}) ⇒ Object
Return the eigensystem(a pair of eigenvalues and left/right eigenvectors) of ‘matrix` as an instance of (a subclass of) EigenDecomposition.
-
.linear_least_square(a, b, options = {}) ⇒ Object
Solve the linear least square problem of min_x |a*x - b|.
- .lls(*args) ⇒ Object
- .mm_transpose_character(t) ⇒ Object
-
.multiply_matrix_and_add(alpha, a, b, beta, c, options = {}) ⇒ Object
(also: mm)
Return alpha*a*b + beta*c.
-
.qr(matrix, options = {}) ⇒ Object
Compute the QR Decomposition of ‘matrix` and return the result as NuLin::QR object.
-
.svd(matrix, options = {}) ⇒ Object
Compute the Singular Value Decomposition of ‘matrix` and return the result as an instance of SVD.
- .to_complex_typecode(typecode) ⇒ Object
- .to_real_typecode(typecode) ⇒ Object
Class Method Details
.cholesky(matrix, options = {}) ⇒ Object
This method doesn’t validate the matrix is symmetric/Hermitian. The only upper/lower half of the matrix is used for the computation.
Compute the Cholesky decomposition for a positive definite symmetric/Hermitian ‘matrix`.
Options
-
:type (default :U) - select whether the result matrix is upper(:U) or lower(:L).
If the matrix is not positive definite, NuLin::LinalgError is raised.
16 17 18 19 20 21 22 |
# File 'lib/nulin/cholesky.rb', line 16 def cholesky(matrix, ={}) unless matrix.square? raise DimensionError, "Cholesky decomposition is computable for a square matrix" end return Cholesky.new(matrix, ).result end |
.det(matrix) ⇒ Object
Compute the determinant of the ‘matrix` and return it.
You get an exception NuLin::DimensionError if the matrix is not square The determinant of given matrix is computed using QR factorization.
9 10 11 12 13 14 15 16 |
# File 'lib/nulin/det.rb', line 9 def det(matrix) unless matrix.square? raise DimensionError, "Determinant is computable only on a square matrix" end n, = matrix.shape r = qr(matrix).R (0 ... n).inject(1.0){|u, i| u*r[i,i] } end |
.eigensystem(matrix, options = {}) ⇒ Object
Return the eigensystem(a pair of eigenvalues and left/right eigenvectors) of ‘matrix` as an instance of (a subclass of) EigenDecomposition
You can call this with some ‘options` as follows
* :use_complex (default true) Return complex NArray objects
If you enable this, the return object (EigenDecomposition)
holds complex NArray objects.
If you disable this, it is assumed that all eigenvalues are
real and the return object holds the real NArray objects.
If you disable this but there is a complex eigenvalue,
the exception ArgumentError is raised.
This options is ignored if `matrix` is complex.
* :use_right (default true) - Compute right eigenvectors
* :use_left (default true) - Compute left eigenvectors
21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/nulin/eigensystem.rb', line 21 def eigensystem(matrix, ={}) unless matrix.square? raise DimensionError, "The eigensystem is computable only for a square matrix" end case when matrix.real? RealEigenDecomposition.new(matrix, ) when matrix.complex? ComplexEigenDecomposition.new(matrix, ) else raise ArgumentError, "The eigensystem is comptable for a complex/float matrix" end end |
.linear_least_square(a, b, options = {}) ⇒ Object
Solve the linear least square problem of min_x |a*x - b|.
The solution is returned as an instance of NuLin::LLS.
When n >= m and rank(a) = n, the problem has a unique solution.
When n < m and rank(a) = n, the equation a*x - b = 0 has infinitely many solutions. In this case, we consider the problem of finding the (unique) solution which minimizes |x|.
If rank(a)=min(m, n), we can find the solution of the two problems using QR or LQ factorization.
In the general case, if rank(a) < min(m, n), the above two problems don’t have a unique solution. Therefore we consider the problem of finding x which minimize |a*x-b| and |x|^2. We can solve the problem using complete orthogonal factorization or SVD.
options
-
:algorithm (default :qr) - select algorithm :qr, :svd
-
:rcond (default -1.0) - The threshold to determine effective rank of matrix a. This parameter is used only for svd algorithm. The number of singular values greater than rcond * (the largest singular value) is regarded as the effective rank.
Example # Using QR/LQ algorithm x = NuLin.linear_least_square(a, b).solution
# Using SVD x = NuLin.linear_least_square(a, b, algorithm: :svd, rcond: 0.0001).solution
40 41 42 43 44 45 46 47 |
# File 'lib/nulin/lls.rb', line 40 def linear_least_square(a, b, ={}) case .fetch(:algorithm, :qr) when :qr NuLin::LLS_QR.new(a, b, ) when :svd NuLin::LLS_SVD.new(a, b, ) end end |
.lls(*args) ⇒ Object
49 |
# File 'lib/nulin/lls.rb', line 49 def lls(*args); linear_least_square(*args); end |
.mm_transpose_character(t) ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/nulin/gemm.rb', line 38 def mm_transpose_character(t) case t when nil, false "N" when :T, :transpose "T" when :C, :H, :adjoint "C" else raise ArgumentError, "Unkdnown transposing spec: #{t.inspect}" end end |
.multiply_matrix_and_add(alpha, a, b, beta, c, options = {}) ⇒ Object Also known as: mm
Return alpha*a*b + beta*c
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/nulin/gemm.rb', line 5 def multiply_matrix_and_add(alpha, a, b, beta, c, ={}) c = c.dup if .fetch(:overwrite_c, false) trans_a = .fetch(:trans_a, nil) trans_b = .fetch(:trans_b, nil) if !trans_a k, n = a.shape else n, k = a.shape end lda = a.shape[0] if !trans_b m, kk = b.shape else kk, m = b.shape end ldb = b.shape[0] mm, nn = c.shape ldc = mm unless k == kk && m == mm && n == nn raise DimensionError, "Invalid matrix shape" end NuLin::Native.call(a.typecode, "gemm", mm_transpose_character(trans_b), mm_transpose_character(trans_a), m, n, k, alpha, b, ldb, a, lda, beta, c, ldc) c end |
.qr(matrix, options = {}) ⇒ Object
Compute the QR Decomposition of ‘matrix` and return the result as NuLin::QR object.
You can call this with ‘options`, but now the argument is ignored.
12 13 14 |
# File 'lib/nulin/qr.rb', line 12 def qr(matrix, ={}) NuLin::QR.new(matrix, ) end |
.svd(matrix, options = {}) ⇒ Object
Compute the Singular Value Decomposition of ‘matrix` and return the result as an instance of SVD
You can call this with some ‘options` as follows
* :use_U (default true) Compute the matrix U
* :use_V (default true) Compute the matrix V and V^t
* :full_matrix (default true)
From the theory of SVD, Any matrix is decomposable into U * sigma * transpose(V) where
* U, V: orthogonal(real) or unitary(complex) matrices
* sigma: diagonal matrix
19 20 21 |
# File 'lib/nulin/svd.rb', line 19 def svd(matrix, ={}) SVD.new(matrix, ) end |