Module: Numo::Linalg
- Defined in:
- ext/numo/linalg/blas/blas.c,
ext/numo/linalg/blas/blas_c.c,
ext/numo/linalg/blas/blas_d.c,
ext/numo/linalg/blas/blas_s.c,
ext/numo/linalg/blas/blas_z.c,
ext/numo/linalg/lapack/lapack.c,
ext/numo/linalg/lapack/lapack_c.c,
ext/numo/linalg/lapack/lapack_d.c,
ext/numo/linalg/lapack/lapack_s.c,
ext/numo/linalg/lapack/lapack_z.c,
lib/numo/linalg/autoloader.rb,
lib/numo/linalg/function.rb,
lib/numo/linalg/loader.rb,
lib/numo/linalg/version.rb
Defined Under Namespace
Modules: Autoloader, Blas, Lapack, Loader Classes: LapackError
Constant Summary
- BLAS_CHAR =
{ SFloat => "s", DFloat => "d", SComplex => "c", DComplex => "z", }
- VERSION =
"0.1.3"
Class Method Summary collapse
-
.blas_char(*args) ⇒ Object
-
.cho_fact(a, uplo: 'U') ⇒ Numo::NArray
Computes the Cholesky factorization of a symmetric/Hermitian positive definite matrix A.
-
.cho_inv(a, uplo: 'U') ⇒ Numo::NArray
Computes the inverse of a symmetric/Hermitian positive definite matrix A using the Cholesky factorization
A = U**T*U
orA = L*L**T
computed by Linalg.cho_fact. -
.cho_solve(a, b, uplo: 'U') ⇒ Numo::NArray
Solves a system of linear equations A*X = B with a symmetric/Hermitian positive definite matrix A using the Cholesky factorization
A = U**T*U
orA = L*L**T
computed by Linalg.cho_fact. -
.cholesky(a, uplo: 'U') ⇒ Numo::NArray
Computes the Cholesky factorization of a symmetric/Hermitian positive definite matrix A.
-
.cond(a, ord = nil) ⇒ Numo::NArray
Compute the condition number of a matrix using the norm with one of the following order.
-
.det(a) ⇒ Float or Complex or Numo::NArray
Determinant of a matrix.
-
.dot(a, b) ⇒ Numo::NArray
Dot product.
-
.eig(a, left: false, right: true) ⇒ [w,vl,vr]
Computes the eigenvalues and, optionally, the left and/or right eigenvectors for a square nonsymmetric matrix A.
-
.eigh(a, b = nil, vals_only: false, vals_range: nil, uplo: 'U', turbo: false) ⇒ [w,v]
Obtains the eigenvalues and, optionally, the eigenvectors by solving an ordinary or generalized eigenvalue problem for a square symmetric / Hermitian matrix.
-
.eigvals(a) ⇒ Numo::NArray
Computes the eigenvalues only for a square nonsymmetric matrix A.
-
.eigvalsh(a, b = nil, vals_range: nil, uplo: 'U', turbo: false) ⇒ Numo::NArray
Obtains the eigenvalues by solving an ordinary or generalized eigenvalue problem for a square symmetric / Hermitian matrix.
-
.inv(a, driver: "getrf", uplo: 'U') ⇒ Numo::NArray
Inverse matrix from square matrix
a
. -
.ldl(a, uplo: 'U', hermitian: true) ⇒ [lu,d,perm]
Computes the LDLt or Bunch-Kaufman factorization of a symmetric/Hermitian matrix A.
-
.lstsq(a, b, driver: 'lsd', rcond: -1)) ⇒ [x, resids, rank, s]
Computes the minimum-norm solution to a linear least squares problem:.
-
.lu(a, permute_l: false) ⇒ [p,l,u], [pl,u]
Computes an LU factorization of a M-by-N matrix A using partial pivoting with row interchanges.
-
.lu_fact(a) ⇒ [lu, ipiv]
Computes an LU factorization of a M-by-N matrix A using partial pivoting with row interchanges.
-
.lu_inv(lu, ipiv) ⇒ Numo::NArray
Computes the inverse of a matrix using the LU factorization computed by Numo::Linalg.lu_fact.
-
.lu_solve(lu, ipiv, b, trans: "N") ⇒ Numo::NArray
Solves a system of linear equations.
-
.matmul(a, b) ⇒ Numo::NArray
Matrix product.
-
.matrix_power(a, n) ⇒ Object
Compute a square matrix
a
to the powern
. -
.matrix_rank(m, tol: nil, driver: 'svd') ⇒ Object
Compute matrix rank of array using SVD Rank is the number of singular values greater than tol.
-
.norm(a, ord = nil, axis: nil, keepdims: false) ⇒ Numo::NArray
Compute matrix or vector norm.
-
.pinv(a, driver: "svd", rcond: nil) ⇒ Numo::NArray
Compute the (Moore-Penrose) pseudo-inverse of a matrix using svd or lstsq.
-
.qr(a, mode: "reduce") ⇒ r, ...
Computes a QR factorization of a complex M-by-N matrix A: A = Q * R.
-
.slogdet(a) ⇒ [sign,logdet]
Natural logarithm of the determinant of a matrix.
-
.solve(a, b, driver: "gen", uplo: 'U') ⇒ Numo::NArray
Solves linear equation
a * x = b
forx
from square matrixa
. -
.svd(a, driver: 'svd', job: 'A') ⇒ [sigma,u,vt]
Computes the Singular Value Decomposition (SVD) of a M-by-N matrix A, and the left and/or right singular vectors.
-
.svdvals(a, driver: 'svd') ⇒ Numo::NArray
Computes the Singular Values of a M-by-N matrix A.
Class Method Details
.blas_char(*args) ⇒ Object
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/numo/linalg/function.rb', line 57 def blas_char(*args) t = Float args.each do |a| k = case a when NArray a.class when Array NArray.array_type(a) end if k && k < NArray t = k::UPCAST[t] || t::UPCAST[k] end end BLAS_CHAR[t] || raise(TypeError,"invalid data type for BLAS/LAPACK") end |
.cho_fact(a, uplo: 'U') ⇒ Numo::NArray
Computes the Cholesky factorization of a symmetric/Hermitian positive definite matrix A. The factorization has the form
A = U**H * U, if UPLO = 'U', or
A = L * L**H, if UPLO = 'L',
where U is an upper triangular matrix and L is a lower triangular matrix.
497 498 499 |
# File 'lib/numo/linalg/function.rb', line 497 def cho_fact(a, uplo:'U') Lapack.call(:potrf, a, uplo:uplo)[0] end |
.cho_inv(a, uplo: 'U') ⇒ Numo::NArray
Computes the inverse of a symmetric/Hermitian
positive definite matrix A using the Cholesky factorization
A = U**T*U
or A = L*L**T
computed by Linalg.cho_fact.
512 513 514 |
# File 'lib/numo/linalg/function.rb', line 512 def cho_inv(a, uplo:'U') Lapack.call(:potri, a, uplo:uplo)[0] end |
.cho_solve(a, b, uplo: 'U') ⇒ Numo::NArray
Solves a system of linear equations
A*X = B
with a symmetric/Hermitian positive definite matrix A
using the Cholesky factorization
A = U**T*U
or A = L*L**T
computed by Linalg.cho_fact.
528 529 530 |
# File 'lib/numo/linalg/function.rb', line 528 def cho_solve(a, b, uplo:'U') Lapack.call(:potrs, a, b, uplo:uplo)[0] end |
.cholesky(a, uplo: 'U') ⇒ Numo::NArray
Computes the Cholesky factorization of a symmetric/Hermitian positive definite matrix A. The factorization has the form
A = U**H * U, if UPLO = 'U', or
A = L * L**H, if UPLO = 'L',
where U is an upper triangular matrix and L is a lower triangular matrix.
470 471 472 473 474 475 476 477 478 479 480 481 482 |
# File 'lib/numo/linalg/function.rb', line 470 def cholesky(a, uplo: 'U') raise NArray::ShapeError, '2-d array is required' if a.ndim < 2 raise NArray::ShapeError, 'matrix a is not square matrix' if a.shape[0] != a.shape[1] factor = Lapack.call(:potrf, a, uplo: uplo)[0] if uplo == 'U' factor.triu else # TODO: Use the tril method if the verision of Numo::NArray # in the runtime dependency list becomes 0.9.1.3 or higher. m, = a.shape factor * Numo::DFloat.ones(m, m).triu.transpose end end |
.cond(a, ord = nil) ⇒ Numo::NArray
Compute the condition number of a matrix using the norm with one of the following order.
| ord | matrix norm |
| ----- | ---------------------- |
| nil | 2-norm using SVD |
| 'fro' | Frobenius norm |
| 'inf' | x.abs.sum(axis:-1).max |
| 1 | x.abs.sum(axis:-2).max |
| 2 | 2-norm (max sing_vals) |
802 803 804 805 806 807 808 809 |
# File 'lib/numo/linalg/function.rb', line 802 def cond(a,ord=nil) if ord.nil? s = svdvals(a) s[false, 0]/s[false, -1] else norm(a, ord, axis:[-2,-1]) * norm(inv(a), ord, axis:[-2,-1]) end end |
.det(a) ⇒ Float or Complex or Numo::NArray
Determinant of a matrix
816 817 818 819 820 821 822 |
# File 'lib/numo/linalg/function.rb', line 816 def det(a) lu, piv, = Lapack.call(:getrf, a) idx = piv.new_narray.store(piv.class.new(piv.shape[-1]).seq(1)) m = piv.eq(idx).count_false(axis:-1) % 2 sign = m * -2 + 1 lu.diagonal.prod(axis:-1) * sign end |
.dot(a, b) ⇒ Numo::NArray
Dot product.
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/numo/linalg/function.rb', line 82 def dot(a, b) a = NArray.asarray(a) b = NArray.asarray(b) case a.ndim when 1 case b.ndim when 1 func = blas_char(a, b) =~ /c|z/ ? :dotu : :dot Blas.call(func, a, b) else Blas.call(:gemv, b, a, trans:'t') end else case b.ndim when 1 Blas.call(:gemv, a, b) else Blas.call(:gemm, a, b) end end end |
.eig(a, left: false, right: true) ⇒ [w,vl,vr]
Computes the eigenvalues and, optionally, the left and/or right eigenvectors for a square nonsymmetric matrix A.
546 547 548 549 550 551 552 553 554 555 556 557 558 |
# File 'lib/numo/linalg/function.rb', line 546 def eig(a, left:false, right:true) jobvl, jobvr = left, right case blas_char(a) when /c|z/ w, vl, vr, info = Lapack.call(:geev, a, jobvl:jobvl, jobvr:jobvr) else wr, wi, vl, vr, info = Lapack.call(:geev, a, jobvl:jobvl, jobvr:jobvr) w = wr + wi * Complex::I vl = _make_complex_eigvecs(w,vl) if left vr = _make_complex_eigvecs(w,vr) if right end [w,vl,vr] #.compact end |
.eigh(a, b = nil, vals_only: false, vals_range: nil, uplo: 'U', turbo: false) ⇒ [w,v]
Obtains the eigenvalues and, optionally, the eigenvectors by solving an ordinary or generalized eigenvalue problem for a square symmetric / Hermitian matrix.
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 |
# File 'lib/numo/linalg/function.rb', line 578 def eigh(a, b=nil, vals_only:false, vals_range:nil, uplo:'U', turbo:false) jobz = vals_only ? 'N' : 'V' # jobz: Compute eigenvalues and eigenvectors. b = a.class.eye(a.shape[0]) if b.nil? func = blas_char(a, b) =~ /c|z/ ? 'hegv' : 'sygv' if vals_range.nil? func << 'd' if turbo v, u_, w, = Lapack.call(func.to_sym, a, b, uplo: uplo, jobz: jobz) v = nil if vals_only [w, v] else func << 'x' il = vals_range.first(1)[0] iu = vals_range.last(1)[0] a_, b_, w, v, = Lapack.call(func.to_sym, a, b, uplo: uplo, jobz: jobz, range: 'I', il: il + 1, iu: iu + 1) v = nil if vals_only [w, v] end end |
.eigvals(a) ⇒ Numo::NArray
Computes the eigenvalues only for a square nonsymmetric matrix A.
602 603 604 605 606 607 608 609 610 611 612 |
# File 'lib/numo/linalg/function.rb', line 602 def eigvals(a) jobvl, jobvr = 'N','N' case blas_char(a) when /c|z/ w, = Lapack.call(:geev, a, jobvl:jobvl, jobvr:jobvr) else wr, wi, = Lapack.call(:geev, a, jobvl:jobvl, jobvr:jobvr) w = wr + wi * Complex::I end w end |
.eigvalsh(a, b = nil, vals_range: nil, uplo: 'U', turbo: false) ⇒ Numo::NArray
Obtains the eigenvalues by solving an ordinary or generalized eigenvalue problem for a square symmetric / Hermitian matrix.
628 629 630 |
# File 'lib/numo/linalg/function.rb', line 628 def eigvalsh(a, b=nil, vals_range:nil, uplo:'U', turbo:false) eigh(a, b, vals_only: true, vals_range: vals_range, uplo: uplo, turbo: turbo).first end |
.inv(a, driver: "getrf", uplo: 'U') ⇒ Numo::NArray
Inverse matrix from square matrix a
920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 |
# File 'lib/numo/linalg/function.rb', line 920 def inv(a, driver:"getrf", uplo:'U') case driver when /(ge|sy|he|po)sv$/ d = $1 b = a.new_zeros.eye solve(a, b, driver:d, uplo:uplo) when /(ge|sy|he)tr[fi]$/ d = $1 lu, piv = lu_fact(a) lu_inv(lu, piv) when /potr[fi]$/ lu = cho_fact(a, uplo:uplo) cho_inv(lu, uplo:uplo) else raise ArgumentError, "invalid driver: #{driver}" end end |
.ldl(a, uplo: 'U', hermitian: true) ⇒ [lu,d,perm]
Computes the LDLt or Bunch-Kaufman factorization of a symmetric/Hermitian matrix A. The factorization has the form
A = U*D*U**T or A = L*D*L**T
where U (or L) is a product of permutation and unit upper (lower) triangular matrices and D is symmetric and block diagonal with 1-by-1 and 2-by-2 diagonal blocks.
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 |
# File 'lib/numo/linalg/function.rb', line 419 def ldl(a, uplo: 'U', hermitian: true) raise NArray::ShapeError, '2-d array is required' if a.ndim < 2 raise NArray::ShapeError, 'matrix a is not square matrix' if a.shape[0] != a.shape[1] is_complex = blas_char(a) =~ /c|z/ func = is_complex && hermitian ? 'hetrf' : 'sytrf' lud, ipiv, = Lapack.call(func.to_sym, a, uplo: uplo) lu = (uplo == 'U' ? lud.triu : lud.tril).tap { |mat| mat[mat.diag_indices(0)] = 1.0 } d = lud[lud.diag_indices(0)].diag m = a.shape[0] n = m - 1 changed_2x2 = false perm = Numo::Int32.new(m).seq m.times do |t| i = uplo == 'U' ? t : n - t j = uplo == 'U' ? i - 1 : i + 1; r = uplo == 'U' ? 0..i : i..n; if ipiv[i] > 0 k = ipiv[i] - 1 lu[[k, i], r] = lu[[i, k], r].dup perm[[k, i]] = perm[[i, k]].dup elsif j.between?(0, n) && ipiv[i] == ipiv[j] && !changed_2x2 k = ipiv[i].abs - 1 d[j, i] = lud[j, i] d[i, j] = is_complex && hermitian ? lud[j, i].conj : lud[j, i] lu[j, i] = 0.0 lu[[k, j], r] = lu[[j, k], r].dup perm[[k, j]] = perm[[j, k]].dup changed_2x2 = true next end changed_2x2 = false end [lu, d, perm.sort_index] end |
.lstsq(a, b, driver: 'lsd', rcond: -1)) ⇒ [x, resids, rank, s]
Computes the minimum-norm solution to a linear least squares problem:
minimize 2-norm(| b - A*x |)
using the singular value decomposition (SVD) of A. A is an M-by-N matrix which may be rank-deficient.
964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 |
# File 'lib/numo/linalg/function.rb', line 964 def lstsq(a, b, driver:'lsd', rcond:-1) a = NArray.asarray(a) b = NArray.asarray(b) b_orig = nil if b.shape.size==1 b_orig = b b = b_orig[true,:new] end m = a.shape[-2] n = a.shape[-1] #nrhs = b.shape[-1] if m != b.shape[-2] raise NArray::ShapeError, "size mismatch: A-row and B-row" end if m < n # need to extend b matrix shp = b.shape shp[-2] = n b2 = b.class.zeros(*shp) b2[false,0...m,true] = b b = b2 end case driver.to_s when /^(ge)?lsd$/i # x, s, rank, info x, s, rank, = Lapack.call(:gelsd, a, b, rcond:rcond) when /^(ge)?lss$/i # v, x, s, rank, info _, x, s, rank, = Lapack.call(:gelss, a, b, rcond:rcond) when /^(ge)?lsy$/i jpvt = Int32.zeros(*a[false,0,true].shape) # v, x, jpvt, rank, info _, x, _, rank, = Lapack.call(:gelsy, a, b, jpvt, rcond:rcond) s = nil else raise ArgumentError, "invalid driver: #{driver}" end resids = nil if m > n if /ls(d|s)$/i =~ driver case rank when n resids = (x[n..-1,true].abs**2).sum(axis:0) when NArray if true resids = (x[false,n..-1,true].abs**2).sum(axis:-2) else resids = x[false,0,true].new_zeros mask = rank.eq(n) # NArray does not suppurt this yet. resids[mask,true] = (x[mask,n..-1,true].abs**2).sum(axis:-2) end end end x = x[false,0...n,true] end if b_orig && b_orig.shape.size==1 x = x[true,0] resids &&= resids[false,0] end [x, resids, rank, s] end |
.lu(a, permute_l: false) ⇒ [p,l,u], [pl,u]
Computes an LU factorization of a M-by-N matrix A using partial pivoting with row interchanges.
The factorization has the form
A = P * L * U
where P is a permutation matrix, L is lower triangular with unit diagonal elements (lower trapezoidal if m > n), and U is upper triangular (upper trapezoidal if m < n).
319 320 321 322 323 324 325 326 327 328 329 330 |
# File 'lib/numo/linalg/function.rb', line 319 def lu(a, permute_l: false) raise NArray::ShapeError, '2-d array is required' if a.ndim < 2 m, n = a.shape k = [m, n].min lu, ip = lu_fact(a) l = lu.tril.tap { |mat| mat[mat.diag_indices(0)] = 1.0 }[true, 0...k] u = lu.triu[0...k, 0...n] p = Numo::DFloat.eye(m).tap do |mat| ip.to_a.each_with_index { |i, j| mat[true, [i - 1, j]] = mat[true, [j, i - 1]].dup } end permute_l ? [p.dot(l), u] : [p, l, u] end |
.lu_fact(a) ⇒ [lu, ipiv]
Computes an LU factorization of a M-by-N matrix A using partial pivoting with row interchanges.
The factorization has the form
A = P * L * U
where P is a permutation matrix, L is lower triangular with unit diagonal elements (lower trapezoidal if m > n), and U is upper triangular (upper trapezoidal if m < n).
350 351 352 |
# File 'lib/numo/linalg/function.rb', line 350 def lu_fact(a) Lapack.call(:getrf, a)[0..1] end |
.lu_inv(lu, ipiv) ⇒ Numo::NArray
Computes the inverse of a matrix using the LU factorization computed by Numo::Linalg.lu_fact.
This method inverts U and then computes inv(A) by solving the system
inv(A)*L = inv(U)
for inv(A).
371 372 373 |
# File 'lib/numo/linalg/function.rb', line 371 def lu_inv(lu, ipiv) Lapack.call(:getri, lu, ipiv)[0] end |
.lu_solve(lu, ipiv, b, trans: "N") ⇒ Numo::NArray
Solves a system of linear equations
A * X = B or A**T * X = B
with a N-by-N matrix A using the LU factorization computed by Numo::Linalg.lu_fact
396 397 398 |
# File 'lib/numo/linalg/function.rb', line 396 def lu_solve(lu, ipiv, b, trans:"N") Lapack.call(:getrs, lu, ipiv, b, trans:trans)[0] end |
.matmul(a, b) ⇒ Numo::NArray
Matrix product.
108 109 110 |
# File 'lib/numo/linalg/function.rb', line 108 def matmul(a, b) Blas.call(:gemm, a, b) end |
.matrix_power(a, n) ⇒ Object
Compute a square matrix a
to the power n
.
- If n > 0: return
a**n
. - If n == 0: return identity matrix.
- If n < 0: return
(a*\*-1)*\*n.abs
.
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/numo/linalg/function.rb', line 154 def matrix_power(a, n) a = NArray.asarray(a) m,k = a.shape[-2..-1] unless m==k raise NArray::ShapeError, "input must be a square array" end unless Integer===n raise ArgumentError, "exponent must be an integer" end if n == 0 return a.class.eye(m) elsif n < 0 a = inv(a) n = n.abs end if n <= 3 r = a (n-1).times do r = matmul(r,a) end else while (n & 1) == 0 a = matmul(a,a) n >>= 1 end r = a while n != 0 a = matmul(a,a) n >>= 1 if (n & 1) != 0 r = matmul(r,a) end end end r end |
.matrix_rank(m, tol: nil, driver: 'svd') ⇒ Object
Compute matrix rank of array using SVD Rank is the number of singular values greater than tol.
856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 |
# File 'lib/numo/linalg/function.rb', line 856 def matrix_rank(m, tol:nil, driver:'svd') m = Numo::NArray.asarray(m) if m.ndim < 2 m.ne(0).any? ? 1 : 0 else case driver.to_s when /^(ge)?sdd$/, "turbo" s = Lapack.call(:gesdd, m, jobz:'N')[0] when /^(ge)?svd$/ s = Lapack.call(:gesvd, m, jobu:'N', jobvt:'N')[0] else raise ArgumentError, "invalid driver: #{driver}" end tol ||= s.max(axis:-1, keepdims:true) * (m.shape[-2..-1].max * s.class::EPSILON) (s > tol).count(axis:-1) end end |
.norm(a, ord = nil, axis: nil, keepdims: false) ⇒ Numo::NArray
Compute matrix or vector norm.
| ord | matrix norm | vector norm |
| ----- | ---------------------- | --------------------------- |
| nil | Frobenius norm | 2-norm |
| 'fro' | Frobenius norm | - |
| 'inf' | x.abs.sum(axis:-1).max | x.abs.max |
| 0 | - | (x.ne 0).sum |
| 1 | x.abs.sum(axis:-2).max | same as below |
| 2 | 2-norm (max sing_vals) | same as below |
| other | - | (x.abs**ord).sum**(1.0/ord) |
654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 |
# File 'lib/numo/linalg/function.rb', line 654 def norm(a, ord=nil, axis:nil, keepdims:false) a = Numo::NArray.asarray(a) # check axis if axis case axis when Integer axis = [axis] when Array if axis.size < 1 || axis.size > 2 raise ArgmentError, "axis option should be 1- or 2-element array" end else raise ArgumentError, "invalid option for axis: #{axis}" end # swap axes if a.ndim > 1 idx = (0...a.ndim).to_a tmp = [] axis.each do |i| x = idx[i] if x.nil? raise ArgmentError, "axis contains same dimension" end tmp << x idx[i] = nil end idx.compact! idx.concat(tmp) a = a.transpose(*idx) end else case a.ndim when 0 raise ArgumentError, "zero-dimensional array" when 1 axis = [-1] else axis = [-2,-1] end end # calculate norm case axis.size when 1 # vector k = keepdims ord ||= 2 # default case ord.to_s when "0" r = a.class.cast(a.ne(0)).sum(axis:-1, keepdims:k) when "1" r = a.abs.sum(axis:-1, keepdims:k) when "2" r = Blas.call(:nrm2, a, keepdims:k) when /^-?\d+$/ o = ord.to_i r = (a.abs**o).sum(axis:-1, keepdims:k)**(1.0/o) when /^inf(inity)?$/i r = a.abs.max(axis:-1, keepdims:k) when /^-inf(inity)?$/i r = a.abs.min(axis:-1, keepdims:k) else raise ArgumentError, "ord (#{ord}) is invalid for vector norm" end when 2 # matrix if keepdims fixdims = [true] * a.ndim axis.each do |i| if i < -a.ndim || i >= a.ndim raise ArgmentError, "axis (%d) is out of range", i end fixdims[i] = :new end end ord ||= "fro" # default case ord.to_s when "1" r, = Lapack.call(:lange, a, '1') when "-1" r = a.abs.sum(axis:-2).min(axis:-1) when "2" svd, = Lapack.call(:gesvd, a, jobu:'N', jobvt:'N') r = svd.max(axis:-1) when "-2" svd, = Lapack.call(:gesvd, a, jobu:'N', jobvt:'N') r = svd.min(axis:-1) when /^f(ro)?$/i r, = Lapack.call(:lange, a, 'F') when /^inf(inity)?$/i r, = Lapack.call(:lange, a, 'I') when /^-inf(inity)?$/i r = a.abs.sum(axis:-1).min(axis:-1) else raise ArgumentError, "ord (#{ord}) is invalid for matrix norm" end if keepdims if NArray===r r = r[*fixdims] else r = a.class.new(1,1).store(r) end end end return r end |
.pinv(a, driver: "svd", rcond: nil) ⇒ Numo::NArray
Compute the (Moore-Penrose) pseudo-inverse of a matrix using svd or lstsq.
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 |
# File 'lib/numo/linalg/function.rb', line 1054 def pinv(a, driver:"svd", rcond:nil) a = NArray.asarray(a) if a.ndim < 2 raise NArray::ShapeError, "2-d array is required" end case driver when /^(ge)?s[dv]d$/ s, u, vh = svd(a, driver:driver, job:'S') if rcond.nil? || rcond < 0 rcond = ((SFloat===s) ? 1e3 : 1e6) * s.class::EPSILON elsif ! Numeric === rcond raise ArgumentError, "rcond must be Numeric" end cond = (s > rcond * s.max(axis:-1, keepdims:true)) if cond.all? r = s.reciprocal else r = s.new_zeros r[cond] = s[cond].reciprocal end u *= r[false,:new,true] dot(u,vh).conj.swapaxes(-2,-1) when /^(ge)?ls[dsy]$/ b = a.class.eye(a.shape[-2]) x, = lstsq(a, b, driver:driver, rcond:rcond) x else raise ArgumentError, "#{driver.inspect} is not one of drivers: "+ "svd, sdd, lsd, lss, lsy" end end |
.qr(a, mode: "reduce") ⇒ r, ...
Computes a QR factorization of a complex M-by-N matrix A: A = Q * R.
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/numo/linalg/function.rb', line 206 def qr(a, mode:"reduce") qr,tau, = Lapack.call(:geqrf, a) *shp,m,n = qr.shape r = (m >= n && %w[economic raw].include?(mode)) ? qr[false, 0...n, true].triu : qr.triu mode = mode.to_s.downcase case mode when "r" return r when "raw" return [qr,tau] when "reduce","economic" # skip else raise ArgumentError, "invalid mode:#{mode}" end if m < n q, = Lapack.call(:orgqr, qr[false, 0...m], tau) elsif mode == "economic" q, = Lapack.call(:orgqr, qr, tau) else qqr = qr.class.zeros(*(shp+[m,m])) qqr[false,0...n] = qr q, = Lapack.call(:orgqr, qqr, tau) end return [q,r] end |
.slogdet(a) ⇒ [sign,logdet]
Natural logarithm of the determinant of a matrix
831 832 833 834 835 836 837 838 839 840 841 842 843 844 |
# File 'lib/numo/linalg/function.rb', line 831 def slogdet(a) lu, piv, = Lapack.call(:getrf, a) idx = piv.new_narray.store(piv.class.new(piv.shape[-1]).seq(1)) m = piv.eq(idx).count_false(axis:-1) % 2 sign = m * -2 + 1 lud = lu.diagonal if (lud.eq 0).any? return 0, (-Float::INFINITY) end lud_abs = lud.abs sign *= (lud/lud_abs).prod [sign, NMath.log(lud_abs).sum(axis:-1)] end |
.solve(a, b, driver: "gen", uplo: 'U') ⇒ Numo::NArray
Solves linear equation a * x = b
for x
from square matrix a
889 890 891 892 893 894 895 896 897 898 899 900 |
# File 'lib/numo/linalg/function.rb', line 889 def solve(a, b, driver:"gen", uplo:'U') case driver.to_s when /^gen?(sv)?$/i # returns lu, x, ipiv, info Lapack.call(:gesv, a, b)[1] when /^(sym?|her?|pos?)(sv)?$/i func = driver[0..1].downcase+"sv" Lapack.call(func, a, b, uplo:uplo)[1] else raise ArgumentError, "invalid driver: #{driver}" end end |
.svd(a, driver: 'svd', job: 'A') ⇒ [sigma,u,vt]
Computes the Singular Value Decomposition (SVD) of a M-by-N matrix A, and the left and/or right singular vectors. The SVD is written
A = U * SIGMA * transpose(V)
where SIGMA is an M-by-N matrix which is zero except for its min(m,n) diagonal elements, U is an M-by-M orthogonal matrix, and V is an N-by-N orthogonal matrix. The diagonal elements of SIGMA are the singular values of A; they are real and non-negative, and are returned in descending order. The first min(m,n) columns of U and V are the left and right singular vectors of A. Note that the routine returns V**T, not V.
259 260 261 262 263 264 265 266 267 268 269 270 271 |
# File 'lib/numo/linalg/function.rb', line 259 def svd(a, driver:'svd', job:'A') unless /^[ASN]/i =~ job raise ArgumentError, "invalid job: #{job.inspect}" end case driver.to_s when /^(ge)?sdd$/i, "turbo" Lapack.call(:gesdd, a, jobz:job)[0..2] when /^(ge)?svd$/i Lapack.call(:gesvd, a, jobu:job, jobvt:job)[0..2] else raise ArgumentError, "invalid driver: #{driver}" end end |
.svdvals(a, driver: 'svd') ⇒ Numo::NArray
Computes the Singular Values of a M-by-N matrix A. The SVD is written
A = U * SIGMA * transpose(V)
where SIGMA is an M-by-N matrix which is zero except for its min(m,n) diagonal elements. The diagonal elements of SIGMA are the singular values of A; they are real and non-negative, and are returned in descending order.
288 289 290 291 292 293 294 295 296 297 |
# File 'lib/numo/linalg/function.rb', line 288 def svdvals(a, driver:'svd') case driver.to_s when /^(ge)?sdd$/i, "turbo" Lapack.call(:gesdd, a, jobz:'N')[0] when /^(ge)?svd$/i Lapack.call(:gesvd, a, jobu:'N', jobvt:'N')[0] else raise ArgumentError, "invalid driver: #{driver}" end end |