Module: Numo::Linalg
- Defined in:
- lib/numo/linalg/loader.rb,
lib/numo/linalg/version.rb,
lib/numo/linalg/function.rb,
lib/numo/linalg/autoloader.rb,
ext/numo/linalg/blas/blas.c,
ext/numo/linalg/lapack/lapack.c
Defined Under Namespace
Modules: Autoloader, Blas, Lapack, Loader
Constant Summary collapse
- VERSION =
"0.1.7"- BLAS_CHAR =
{ SFloat => "s", DFloat => "d", SComplex => "c", DComplex => "z", }
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` or `A = 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` or `A = 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.
-
.expm(a, ord = 8) ⇒ Numo::NArray
Compute the matrix exponential using Pade approximation method.
-
.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 power `n`.
-
.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.
-
.null_space(a, rcond: -1)) ⇒ Numo::NArray
Computes an orthonormal basis for the null space of matrix A.
-
.orth(a, rcond: -1)) ⇒ Numo::NArray
Computes an orthonormal basis for the range of matrix A.
-
.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` for `x` from square matrix `a`.
-
.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
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/numo/linalg/function.rb', line 71 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.
591 592 593 |
# File 'lib/numo/linalg/function.rb', line 591 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.
606 607 608 |
# File 'lib/numo/linalg/function.rb', line 606 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.
622 623 624 |
# File 'lib/numo/linalg/function.rb', line 622 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.
564 565 566 567 568 569 570 571 572 573 574 575 576 |
# File 'lib/numo/linalg/function.rb', line 564 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) |
896 897 898 899 900 901 902 903 |
# File 'lib/numo/linalg/function.rb', line 896 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
910 911 912 913 914 915 916 |
# File 'lib/numo/linalg/function.rb', line 910 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.
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/numo/linalg/function.rb', line 96 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 if b.contiguous? trans = 't' else if b.fortran_contiguous? trans = 'n' b = b.transpose else trans = 't' b = b.dup end end Blas.call(:gemv, b, a, trans:trans) end else case b.ndim when 1 if a.contiguous? trans = 'n' else if a.fortran_contiguous? trans = 't' a = a.transpose else trans = 'n' a = a.dup end end Blas.call(:gemv, a, b, trans:trans) else if a.contiguous? transa = 'n' else if a.fortran_contiguous? transa = 't' a = a.transpose else transa = 'n' a = a.dup end end if b.contiguous? transb = 'n' else if b.fortran_contiguous? transb='t' b = b.transpose else transb='n' b = b.dup end end Blas.call(:gemm, a, b, transa:transa, transb:transb) 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.
640 641 642 643 644 645 646 647 648 649 650 651 652 |
# File 'lib/numo/linalg/function.rb', line 640 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.
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 |
# File 'lib/numo/linalg/function.rb', line 672 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.
696 697 698 699 700 701 702 703 704 705 706 |
# File 'lib/numo/linalg/function.rb', line 696 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.
722 723 724 |
# File 'lib/numo/linalg/function.rb', line 722 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 |
.expm(a, ord = 8) ⇒ Numo::NArray
Compute the matrix exponential using Pade approximation method.
1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 |
# File 'lib/numo/linalg/function.rb', line 1192 def expm(a, ord=8) raise NArray::ShapeError, 'matrix a is not square matrix' if a.shape[0] != a.shape[1] inf_norm = norm(a, 'inf') n_squarings = inf_norm.zero? ? 0 : [0, Math.log2(inf_norm).ceil.to_i].max a = a / (2**n_squarings) sz_mat = a.shape[0] c = 1 s = -1 x = Numo::DFloat.eye(sz_mat) n = Numo::DFloat.eye(sz_mat) d = Numo::DFloat.eye(sz_mat) (1..ord).each do |k| c *= (ord - k + 1).fdiv((2 * ord - k + 1) * k) x = a.dot(x) cx = c * x n += cx d += s * cx s *= -1 end res = solve(d, n) n_squarings.times { res = res.dot(res) } res end |
.inv(a, driver: "getrf", uplo: 'U') ⇒ Numo::NArray
Inverse matrix from square matrix ‘a`
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 |
# File 'lib/numo/linalg/function.rb', line 1014 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.
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 |
# File 'lib/numo/linalg/function.rb', line 513 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.
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 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 |
# File 'lib/numo/linalg/function.rb', line 1058 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 resids = (x[false,n..-1,true].abs**2).sum(axis:-2) ## NArray does not suppurt this yet. # resids = x[false,0,true].new_zeros # mask = rank.eq(n) # resids[mask,true] = (x[mask,n..-1,true].abs**2).sum(axis:-2) 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).
413 414 415 416 417 418 419 420 421 422 423 424 |
# File 'lib/numo/linalg/function.rb', line 413 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).
444 445 446 |
# File 'lib/numo/linalg/function.rb', line 444 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).
465 466 467 |
# File 'lib/numo/linalg/function.rb', line 465 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
490 491 492 |
# File 'lib/numo/linalg/function.rb', line 490 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.
166 167 168 |
# File 'lib/numo/linalg/function.rb', line 166 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`.
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/numo/linalg/function.rb', line 212 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.
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 |
# File 'lib/numo/linalg/function.rb', line 950 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) |
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 |
# File 'lib/numo/linalg/function.rb', line 748 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 |
.null_space(a, rcond: -1)) ⇒ Numo::NArray
Computes an orthonormal basis for the null space of matrix A.
383 384 385 386 387 388 389 390 391 |
# File 'lib/numo/linalg/function.rb', line 383 def null_space(a, rcond: -1) raise NArray::ShapeError, '2-d array is required' if a.ndim < 2 s, _u, vh = svd(a) tol = s.max * (rcond.nil? || rcond < 0 ? a.class::EPSILON * a.shape.max : rcond) k = (s > tol).count return a.class.new if k == vh.shape[0] r = vh[k..-1, true].transpose.dup blas_char(vh) =~ /c|z/ ? r.conj : r end |
.orth(a, rcond: -1)) ⇒ Numo::NArray
Computes an orthonormal basis for the range of matrix A.
366 367 368 369 370 371 372 |
# File 'lib/numo/linalg/function.rb', line 366 def orth(a, rcond: -1) raise NArray::ShapeError, '2-d array is required' if a.ndim < 2 s, u, = svd(a) tol = s.max * (rcond.nil? || rcond < 0 ? a.class::EPSILON * a.shape.max : rcond) k = (s > tol).count u[true, 0...k] end |
.pinv(a, driver: "svd", rcond: nil) ⇒ Numo::NArray
Compute the (Moore-Penrose) pseudo-inverse of a matrix using svd or lstsq.
1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 |
# File 'lib/numo/linalg/function.rb', line 1145 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.
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/numo/linalg/function.rb', line 264 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
925 926 927 928 929 930 931 932 933 934 935 936 937 938 |
# File 'lib/numo/linalg/function.rb', line 925 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`
983 984 985 986 987 988 989 990 991 992 993 994 |
# File 'lib/numo/linalg/function.rb', line 983 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.
317 318 319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/numo/linalg/function.rb', line 317 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.
346 347 348 349 350 351 352 353 354 355 |
# File 'lib/numo/linalg/function.rb', line 346 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 |