nmatrix 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/nmatrix/data/complex.h +20 -55
- data/ext/nmatrix/data/data.cpp +11 -44
- data/ext/nmatrix/data/data.h +174 -311
- data/ext/nmatrix/data/meta.h +1 -7
- data/ext/nmatrix/data/ruby_object.h +3 -85
- data/ext/nmatrix/extconf.rb +2 -73
- data/ext/nmatrix/math.cpp +170 -813
- data/ext/nmatrix/math/asum.h +2 -25
- data/ext/nmatrix/math/{inc.h → cblas_enums.h} +11 -22
- data/ext/nmatrix/math/cblas_templates_core.h +507 -0
- data/ext/nmatrix/math/gemm.h +2 -32
- data/ext/nmatrix/math/gemv.h +1 -35
- data/ext/nmatrix/math/getrf.h +21 -6
- data/ext/nmatrix/math/getrs.h +0 -8
- data/ext/nmatrix/math/imax.h +0 -22
- data/ext/nmatrix/math/long_dtype.h +0 -3
- data/ext/nmatrix/math/math.h +11 -337
- data/ext/nmatrix/math/nrm2.h +2 -23
- data/ext/nmatrix/math/rot.h +1 -25
- data/ext/nmatrix/math/rotg.h +4 -13
- data/ext/nmatrix/math/scal.h +0 -22
- data/ext/nmatrix/math/trsm.h +0 -55
- data/ext/nmatrix/math/util.h +148 -0
- data/ext/nmatrix/nmatrix.cpp +0 -14
- data/ext/nmatrix/nmatrix.h +92 -84
- data/ext/nmatrix/ruby_constants.cpp +0 -2
- data/ext/nmatrix/ruby_constants.h +0 -2
- data/ext/nmatrix/ruby_nmatrix.c +86 -45
- data/ext/nmatrix/storage/dense/dense.cpp +1 -7
- data/ext/nmatrix/storage/storage.h +0 -1
- data/ext/nmatrix/ttable_helper.rb +0 -6
- data/ext/nmatrix/util/io.cpp +1 -1
- data/lib/nmatrix.rb +1 -19
- data/lib/nmatrix/blas.rb +33 -11
- data/lib/nmatrix/io/market.rb +3 -3
- data/lib/nmatrix/lapack_core.rb +181 -0
- data/lib/nmatrix/lapack_plugin.rb +44 -0
- data/lib/nmatrix/math.rb +382 -131
- data/lib/nmatrix/monkeys.rb +2 -3
- data/lib/nmatrix/nmatrix.rb +166 -13
- data/lib/nmatrix/shortcuts.rb +72 -7
- data/lib/nmatrix/version.rb +2 -2
- data/spec/00_nmatrix_spec.rb +154 -5
- data/spec/02_slice_spec.rb +2 -6
- data/spec/03_nmatrix_monkeys_spec.rb +7 -1
- data/spec/blas_spec.rb +60 -33
- data/spec/homogeneous_spec.rb +10 -10
- data/spec/lapack_core_spec.rb +482 -0
- data/spec/math_spec.rb +436 -52
- data/spec/shortcuts_spec.rb +28 -4
- data/spec/spec_helper.rb +14 -2
- data/spec/utm5940.mtx +83844 -0
- metadata +49 -76
- data/.gitignore +0 -27
- data/.rspec +0 -2
- data/.travis.yml +0 -15
- data/CONTRIBUTING.md +0 -82
- data/Gemfile +0 -2
- data/History.txt +0 -677
- data/LICENSE.txt +0 -23
- data/Manifest.txt +0 -92
- data/README.rdoc +0 -150
- data/Rakefile +0 -216
- data/ext/nmatrix/data/rational.h +0 -440
- data/ext/nmatrix/math/geev.h +0 -82
- data/ext/nmatrix/math/ger.h +0 -96
- data/ext/nmatrix/math/gesdd.h +0 -80
- data/ext/nmatrix/math/gesvd.h +0 -78
- data/ext/nmatrix/math/getf2.h +0 -86
- data/ext/nmatrix/math/getri.h +0 -108
- data/ext/nmatrix/math/potrs.h +0 -129
- data/ext/nmatrix/math/swap.h +0 -52
- data/lib/nmatrix/lapack.rb +0 -240
- data/nmatrix.gemspec +0 -55
- data/scripts/mac-brew-gcc.sh +0 -50
- data/scripts/mac-mavericks-brew-gcc.sh +0 -22
- data/spec/lapack_spec.rb +0 -459
@@ -0,0 +1,181 @@
|
|
1
|
+
#--
|
2
|
+
# = NMatrix
|
3
|
+
#
|
4
|
+
# A linear algebra library for scientific computation in Ruby.
|
5
|
+
# NMatrix is part of SciRuby.
|
6
|
+
#
|
7
|
+
# NMatrix was originally inspired by and derived from NArray, by
|
8
|
+
# Masahiro Tanaka: http://narray.rubyforge.org
|
9
|
+
#
|
10
|
+
# == Copyright Information
|
11
|
+
#
|
12
|
+
# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
|
13
|
+
# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
|
14
|
+
#
|
15
|
+
# Please see LICENSE.txt for additional copyright notices.
|
16
|
+
#
|
17
|
+
# == Contributing
|
18
|
+
#
|
19
|
+
# By contributing source code to SciRuby, you agree to be bound by
|
20
|
+
# our Contributor Agreement:
|
21
|
+
#
|
22
|
+
# * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
|
23
|
+
#
|
24
|
+
# == lapack_core.rb
|
25
|
+
#
|
26
|
+
# This file contains friendlier interfaces to LAPACK functions
|
27
|
+
# implemented in C.
|
28
|
+
# This file is only for functions available with the core nmatrix gem
|
29
|
+
# (no external libraries needed).
|
30
|
+
#
|
31
|
+
# Note: most of these functions are borrowed from ATLAS, which is available under a BSD-
|
32
|
+
# style license.
|
33
|
+
#++
|
34
|
+
|
35
|
+
class NMatrix
|
36
|
+
|
37
|
+
module LAPACK
|
38
|
+
|
39
|
+
#Add functions from C extension to main LAPACK module
|
40
|
+
class << self
|
41
|
+
NMatrix::Internal::LAPACK.singleton_methods.each do |m|
|
42
|
+
define_method m, NMatrix::Internal::LAPACK.method(m).to_proc
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class << self
|
47
|
+
# Solve the matrix equation AX = B, where A is a symmetric (or Hermitian)
|
48
|
+
# positive-definite matrix. If A is a nxn matrix, B must be mxn.
|
49
|
+
# Depending on the value of uplo, only the upper or lower half of +a+
|
50
|
+
# is read.
|
51
|
+
# This uses the Cholesky decomposition so it should be faster than
|
52
|
+
# the generic NMatrix#solve method.
|
53
|
+
# Doesn't modify inputs.
|
54
|
+
# Requires either the nmatrix-atlas or nmatrix-lapacke gem.
|
55
|
+
# * *Arguments* :
|
56
|
+
# - +uplo+ -> Either +:upper+ or +:lower+. Specifies which half of +a+ to read.
|
57
|
+
# - +a+ -> The matrix A.
|
58
|
+
# - +b+ -> The right-hand side B.
|
59
|
+
# * *Returns* :
|
60
|
+
# - The solution X
|
61
|
+
def posv(uplo, a, b)
|
62
|
+
raise(NotImplementedError, "Either the nmatrix-atlas or nmatrix-lapacke gem must be installed to use posv")
|
63
|
+
end
|
64
|
+
|
65
|
+
# laswp(matrix, ipiv) -> NMatrix
|
66
|
+
#
|
67
|
+
# Permute the columns of a matrix (in-place) according to the Array +ipiv+.
|
68
|
+
#
|
69
|
+
def laswp(matrix, ipiv)
|
70
|
+
raise(ArgumentError, "expected NMatrix for argument 0") unless matrix.is_a?(NMatrix)
|
71
|
+
raise(StorageTypeError, "LAPACK functions only work on :dense NMatrix instances") unless matrix.stype == :dense
|
72
|
+
raise(ArgumentError, "expected Array ipiv to have no more entries than NMatrix a has columns") if ipiv.size > matrix.shape[1]
|
73
|
+
|
74
|
+
clapack_laswp(matrix.shape[0], matrix, matrix.shape[1], 0, ipiv.size-1, ipiv, 1)
|
75
|
+
end
|
76
|
+
|
77
|
+
def alloc_svd_result(matrix)
|
78
|
+
[
|
79
|
+
NMatrix.new(matrix.shape[0], dtype: matrix.dtype),
|
80
|
+
NMatrix.new([[matrix.shape[0],matrix.shape[1]].min,1], dtype: matrix.abs_dtype),
|
81
|
+
NMatrix.new(matrix.shape[1], dtype: matrix.dtype)
|
82
|
+
]
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
#
|
87
|
+
# call-seq:
|
88
|
+
# gesvd(matrix) -> [u, sigma, v_transpose]
|
89
|
+
# gesvd(matrix) -> [u, sigma, v_conjugate_transpose] # complex
|
90
|
+
#
|
91
|
+
# Compute the singular value decomposition of a matrix using LAPACK's GESVD function.
|
92
|
+
#
|
93
|
+
# Optionally accepts a +workspace_size+ parameter, which will be honored only if it is larger than what LAPACK
|
94
|
+
# requires.
|
95
|
+
#
|
96
|
+
# Requires either the nmatrix-lapacke or nmatrix-atlas gem.
|
97
|
+
#
|
98
|
+
def gesvd(matrix, workspace_size=1)
|
99
|
+
raise(NotImplementedError,"gesvd requires either the nmatrix-atlas or nmatrix-lapacke gem")
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# call-seq:
|
104
|
+
# gesdd(matrix) -> [u, sigma, v_transpose]
|
105
|
+
# gesdd(matrix) -> [u, sigma, v_conjugate_transpose] # complex
|
106
|
+
#
|
107
|
+
# Compute the singular value decomposition of a matrix using LAPACK's GESDD function. This uses a divide-and-conquer
|
108
|
+
# strategy. See also #gesvd.
|
109
|
+
#
|
110
|
+
# Optionally accepts a +workspace_size+ parameter, which will be honored only if it is larger than what LAPACK
|
111
|
+
# requires.
|
112
|
+
#
|
113
|
+
# Requires either the nmatrix-lapacke or nmatrix-atlas gem.
|
114
|
+
#
|
115
|
+
def gesdd(matrix, workspace_size=nil)
|
116
|
+
raise(NotImplementedError,"gesvd requires either the nmatrix-atlas or nmatrix-lapacke gem")
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# call-seq:
|
121
|
+
# geev(matrix) -> [eigenvalues, left_eigenvectors, right_eigenvectors]
|
122
|
+
# geev(matrix, :left) -> [eigenvalues, left_eigenvectors]
|
123
|
+
# geev(matrix, :right) -> [eigenvalues, right_eigenvectors]
|
124
|
+
#
|
125
|
+
# Perform eigenvalue decomposition on a matrix using LAPACK's xGEEV function.
|
126
|
+
#
|
127
|
+
# +eigenvalues+ is a n-by-1 NMatrix containing the eigenvalues.
|
128
|
+
#
|
129
|
+
# +right_eigenvalues+ is a n-by-n matrix such that its j'th column
|
130
|
+
# contains the (right) eigenvalue of +matrix+ corresponding
|
131
|
+
# to the j'th eigenvalue.
|
132
|
+
# This means that +matrix+ = RDR^(-1),
|
133
|
+
# where R is +right_eigenvalues+ and D is the diagonal matrix formed
|
134
|
+
# from +eigenvalues+.
|
135
|
+
#
|
136
|
+
# +left_eigenvalues+ is n-by-n and its columns are the left
|
137
|
+
# eigenvalues of +matrix+, using the {definition of left eigenvalue
|
138
|
+
# from LAPACK}[https://software.intel.com/en-us/node/521147].
|
139
|
+
#
|
140
|
+
# For real dtypes, +eigenvalues+ and the eigenvector matrices
|
141
|
+
# will be complex if and only if +matrix+ has complex eigenvalues.
|
142
|
+
#
|
143
|
+
# Only available if nmatrix-lapack or nmatrix-atlas is installed.
|
144
|
+
#
|
145
|
+
def geev(matrix, which=:both)
|
146
|
+
raise(NotImplementedError, "geev requires either the nmatrix-atlas or nmatrix-lapack gem")
|
147
|
+
end
|
148
|
+
|
149
|
+
# The following are functions that used to be implemented in C, but
|
150
|
+
# now require nmatrix-atlas to run properly, so we can just
|
151
|
+
# implemented their stubs in Ruby.
|
152
|
+
def lapack_gesvd(jobu, jobvt, m, n, a, lda, s, u, ldu, vt, ldvt, lwork)
|
153
|
+
raise(NotImplementedError,"lapack_gesvd requires the nmatrix-atlas gem")
|
154
|
+
end
|
155
|
+
|
156
|
+
def lapack_gesdd(jobz, m, n, a, lda, s, u, ldu, vt, ldvt, lwork)
|
157
|
+
raise(NotImplementedError,"lapack_gesdd requires the nmatrix-atlas gem")
|
158
|
+
end
|
159
|
+
|
160
|
+
def lapack_geev(jobvl, jobvr, n, a, lda, w, wi, vl, ldvl, vr, ldvr, lwork)
|
161
|
+
raise(NotImplementedError,"lapack_geev requires the nmatrix-atlas gem")
|
162
|
+
end
|
163
|
+
|
164
|
+
def clapack_potrf(order, uplo, n, a, lda)
|
165
|
+
raise(NotImplementedError,"clapack_potrf requires the nmatrix-atlas gem")
|
166
|
+
end
|
167
|
+
|
168
|
+
def clapack_potri(order, uplo, n, a, lda)
|
169
|
+
raise(NotImplementedError,"clapack_potri requires the nmatrix-atlas gem")
|
170
|
+
end
|
171
|
+
|
172
|
+
def clapack_potrs(order, uplo, n, nrhs, a, lda, b, ldb)
|
173
|
+
raise(NotImplementedError,"clapack_potrs requires the nmatrix-atlas gem")
|
174
|
+
end
|
175
|
+
|
176
|
+
def clapack_getri(order, n, a, lda, ipiv)
|
177
|
+
raise(NotImplementedError,"clapack_getri requires the nmatrix-atlas gem")
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#--
|
2
|
+
# = NMatrix
|
3
|
+
#
|
4
|
+
# A linear algebra library for scientific computation in Ruby.
|
5
|
+
# NMatrix is part of SciRuby.
|
6
|
+
#
|
7
|
+
# NMatrix was originally inspired by and derived from NArray, by
|
8
|
+
# Masahiro Tanaka: http://narray.rubyforge.org
|
9
|
+
#
|
10
|
+
# == Copyright Information
|
11
|
+
#
|
12
|
+
# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
|
13
|
+
# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
|
14
|
+
#
|
15
|
+
# Please see LICENSE.txt for additional copyright notices.
|
16
|
+
#
|
17
|
+
# == Contributing
|
18
|
+
#
|
19
|
+
# By contributing source code to SciRuby, you agree to be bound by
|
20
|
+
# our Contributor Agreement:
|
21
|
+
#
|
22
|
+
# * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
|
23
|
+
#
|
24
|
+
# == lapack_plugin.rb
|
25
|
+
#
|
26
|
+
# This file `require`s either nmatrix-atlas or nmatrix-lapacke depending on which
|
27
|
+
# is available.
|
28
|
+
#
|
29
|
+
# The idea is that if a developer wants to use a LAPACK feature which is provided
|
30
|
+
# by both of these gems (e.g. NMatrix#potrf! or NMatrix::LAPACK.geev),
|
31
|
+
# but doesn't care which one is installed, they can
|
32
|
+
# just `require 'nmatrix/lapack_plugin'` rather than having to choose between
|
33
|
+
# `require 'nmatrix/lapacke'` or `require 'nmatrix/lapacke'`
|
34
|
+
#++
|
35
|
+
|
36
|
+
begin
|
37
|
+
require 'nmatrix/atlas'
|
38
|
+
rescue LoadError
|
39
|
+
begin
|
40
|
+
require 'nmatrix/lapacke'
|
41
|
+
rescue LoadError
|
42
|
+
raise(LoadError,"Either nmatrix-atlas or nmatrix-lapacke must be installed")
|
43
|
+
end
|
44
|
+
end
|
data/lib/nmatrix/math.rb
CHANGED
@@ -36,6 +36,31 @@ class NMatrix
|
|
36
36
|
:asinh, :atanh, :exp, :log2, :log10, :sqrt, :cbrt, :erf, :erfc, :gamma, :-@]
|
37
37
|
end
|
38
38
|
|
39
|
+
# Methods for generating permutation matrix from LU factorization results.
|
40
|
+
module FactorizeLUMethods
|
41
|
+
class << self
|
42
|
+
def permutation_matrix_from(pivot_array)
|
43
|
+
perm_arry = permutation_array_for(pivot_array)
|
44
|
+
n = NMatrix.zeros(perm_arry.size, dtype: :byte)
|
45
|
+
|
46
|
+
perm_arry.each_with_index { |e, i| n[e,i] = 1 }
|
47
|
+
|
48
|
+
n
|
49
|
+
end
|
50
|
+
|
51
|
+
def permutation_array_for(pivot_array)
|
52
|
+
perm_arry = Array.new(pivot_array.size) { |i| i }
|
53
|
+
perm_arry.each_index do |i|
|
54
|
+
#the pivot indices returned by LAPACK getrf are indexed starting
|
55
|
+
#from 1, so we need to subtract 1 here
|
56
|
+
perm_arry[i], perm_arry[pivot_array[i]-1] = perm_arry[pivot_array[i]-1], perm_arry[i]
|
57
|
+
end
|
58
|
+
|
59
|
+
perm_arry
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
39
64
|
#
|
40
65
|
# call-seq:
|
41
66
|
# invert! -> NMatrix
|
@@ -44,23 +69,18 @@ class NMatrix
|
|
44
69
|
# Only works on dense matrices. Alternatively uses in-place Gauss-Jordan
|
45
70
|
# elimination.
|
46
71
|
#
|
72
|
+
# * *Raises* :
|
73
|
+
# - +StorageTypeError+ -> only implemented on dense matrices.
|
74
|
+
# - +ShapeError+ -> matrix must be square.
|
75
|
+
# - +DataTypeError+ -> cannot invert an integer matrix in-place.
|
76
|
+
#
|
47
77
|
def invert!
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
# Now calculate the inverse using the pivot array
|
53
|
-
NMatrix::LAPACK::clapack_getri(:row, self.shape[1], self, self.shape[1], pivot)
|
78
|
+
raise(StorageTypeError, "invert only works on dense matrices currently") unless self.dense?
|
79
|
+
raise(ShapeError, "Cannot invert non-square matrix") unless self.dim == 2 && self.shape[0] == self.shape[1]
|
80
|
+
raise(DataTypeError, "Cannot invert an integer matrix in-place") if self.integer_dtype?
|
54
81
|
|
55
|
-
|
56
|
-
|
57
|
-
if self.integer_dtype?
|
58
|
-
__inverse__(self.cast(dtype: :rational128), true)
|
59
|
-
else
|
60
|
-
dtype = self.dtype
|
61
|
-
__inverse__(self, true)
|
62
|
-
end
|
63
|
-
end
|
82
|
+
#No internal implementation of getri, so use this other function
|
83
|
+
__inverse__(self, true)
|
64
84
|
end
|
65
85
|
|
66
86
|
#
|
@@ -70,45 +90,42 @@ class NMatrix
|
|
70
90
|
# Make a copy of the matrix, then invert using Gauss-Jordan elimination.
|
71
91
|
# Works without LAPACK.
|
72
92
|
#
|
73
|
-
#
|
74
93
|
# * *Returns* :
|
75
|
-
# - A dense NMatrix.
|
76
|
-
#
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
rational_self.__inverse__(inverse, false)
|
90
|
-
else
|
91
|
-
inverse = self.clone
|
92
|
-
__inverse__(inverse, false)
|
93
|
-
end
|
94
|
+
# - A dense NMatrix. Will be the same type as the input NMatrix,
|
95
|
+
# except if the input is an integral dtype, in which case it will be a
|
96
|
+
# :float64 NMatrix.
|
97
|
+
#
|
98
|
+
# * *Raises* :
|
99
|
+
# - +StorageTypeError+ -> only implemented on dense matrices.
|
100
|
+
# - +ShapeError+ -> matrix must be square.
|
101
|
+
#
|
102
|
+
def invert
|
103
|
+
#write this in terms of invert! so plugins will only have to overwrite
|
104
|
+
#invert! and not invert
|
105
|
+
if self.integer_dtype?
|
106
|
+
cloned = self.cast(dtype: :float64)
|
107
|
+
cloned.invert!
|
94
108
|
else
|
95
|
-
|
96
|
-
|
97
|
-
__inverse_exact__(inverse.cast(dtype: :rational128), lda, ldb)
|
98
|
-
else
|
99
|
-
dtype = self.dtype
|
100
|
-
__inverse_exact__(inverse, lda, ldb)
|
101
|
-
end
|
109
|
+
cloned = self.clone
|
110
|
+
cloned.invert!
|
102
111
|
end
|
103
112
|
end
|
104
113
|
alias :inverse :invert
|
105
114
|
|
106
115
|
#
|
107
116
|
# call-seq:
|
108
|
-
# getrf! ->
|
117
|
+
# getrf! -> Array
|
109
118
|
#
|
110
119
|
# LU factorization of a general M-by-N matrix +A+ using partial pivoting with
|
111
|
-
# row interchanges.
|
120
|
+
# row interchanges. The LU factorization is A = PLU, where P is a row permutation
|
121
|
+
# matrix, L is a lower triangular matrix with unit diagonals, and U is an upper
|
122
|
+
# triangular matrix (note that this convention is different from the
|
123
|
+
# clapack_getrf behavior, but matches the standard LAPACK getrf).
|
124
|
+
# +A+ is overwritten with the elements of L and U (the unit
|
125
|
+
# diagonal elements of L are not saved). P is not returned directly and must be
|
126
|
+
# constructed from the pivot array ipiv. The row indices in ipiv are indexed
|
127
|
+
# starting from 1.
|
128
|
+
# Only works for dense matrices.
|
112
129
|
#
|
113
130
|
# * *Returns* :
|
114
131
|
# - The IPIV vector. The L and U matrices are stored in A.
|
@@ -117,44 +134,51 @@ class NMatrix
|
|
117
134
|
#
|
118
135
|
def getrf!
|
119
136
|
raise(StorageTypeError, "ATLAS functions only work on dense matrices") unless self.dense?
|
120
|
-
NMatrix::LAPACK::clapack_getrf(:row, self.shape[0], self.shape[1], self, self.shape[1])
|
121
|
-
end
|
122
|
-
|
123
137
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
138
|
+
#For row-major matrices, clapack_getrf uses a different convention than
|
139
|
+
#described above (U has unit diagonal elements instead of L and columns
|
140
|
+
#are interchanged rather than rows). For column-major matrices, clapack
|
141
|
+
#uses the stanard conventions. So we just transpose the matrix before
|
142
|
+
#and after calling clapack_getrf.
|
143
|
+
#Unfortunately, this is not a very good way, uses a lot of memory.
|
144
|
+
temp = self.transpose
|
145
|
+
ipiv = NMatrix::LAPACK::clapack_getrf(:col, self.shape[0], self.shape[1], temp, self.shape[0])
|
146
|
+
temp = temp.transpose
|
147
|
+
self[0...self.shape[0], 0...self.shape[1]] = temp
|
148
|
+
|
149
|
+
#for some reason, in clapack_getrf, the indices in ipiv start from 0
|
150
|
+
#instead of 1 as in LAPACK.
|
151
|
+
ipiv.each_index { |i| ipiv[i]+=1 }
|
152
|
+
|
153
|
+
return ipiv
|
137
154
|
end
|
138
155
|
|
139
|
-
|
140
156
|
#
|
141
157
|
# call-seq:
|
142
158
|
# potrf!(upper_or_lower) -> NMatrix
|
143
159
|
#
|
144
160
|
# Cholesky factorization of a symmetric positive-definite matrix -- or, if complex,
|
145
|
-
# a Hermitian positive-definite matrix +A+.
|
146
|
-
#
|
147
|
-
# matrix
|
161
|
+
# a Hermitian positive-definite matrix +A+.
|
162
|
+
# The result will be written in either the upper or lower triangular portion of the
|
163
|
+
# matrix, depending on whether the argument is +:upper+ or +:lower+.
|
164
|
+
# Also the function only reads in the upper or lower part of the matrix,
|
165
|
+
# so it doesn't actually have to be symmetric/Hermitian.
|
166
|
+
# However, if the matrix (i.e. the symmetric matrix implied by the lower/upper
|
167
|
+
# half) is not positive-definite, the function will return nonsense.
|
168
|
+
#
|
169
|
+
# This functions requires either the nmatrix-atlas or nmatrix-lapacke gem
|
170
|
+
# installed.
|
148
171
|
#
|
149
172
|
# * *Returns* :
|
150
173
|
# the triangular portion specified by the parameter
|
151
174
|
# * *Raises* :
|
152
175
|
# - +StorageTypeError+ -> ATLAS functions only work on dense matrices.
|
176
|
+
# - +ShapeError+ -> Must be square.
|
177
|
+
# - +NotImplementedError+ -> If called without nmatrix-atlas or nmatrix-lapacke gem
|
153
178
|
#
|
154
179
|
def potrf!(which)
|
155
|
-
|
156
|
-
|
157
|
-
NMatrix::LAPACK::clapack_potrf(:row, which, self.shape[0], self, self.shape[1])
|
180
|
+
# The real implementation is in the plugin files.
|
181
|
+
raise(NotImplementedError, "potrf! requires either the nmatrix-atlas or nmatrix-lapacke gem")
|
158
182
|
end
|
159
183
|
|
160
184
|
def potrf_upper!
|
@@ -168,30 +192,102 @@ class NMatrix
|
|
168
192
|
|
169
193
|
#
|
170
194
|
# call-seq:
|
171
|
-
# factorize_cholesky ->
|
195
|
+
# factorize_cholesky -> [upper NMatrix, lower NMatrix]
|
172
196
|
#
|
173
|
-
# Cholesky factorization of a matrix
|
197
|
+
# Calculates the Cholesky factorization of a matrix and returns the
|
198
|
+
# upper and lower matrices such that A=LU and L=U*, where * is
|
199
|
+
# either the transpose or conjugate transpose.
|
200
|
+
#
|
201
|
+
# Unlike potrf!, this makes method requires that the original is matrix is
|
202
|
+
# symmetric or Hermitian. However, it is still your responsibility to make
|
203
|
+
# sure it is positive-definite.
|
174
204
|
def factorize_cholesky
|
175
|
-
|
176
|
-
self.clone.potrf_lower!.tril!
|
205
|
+
raise "Matrix must be symmetric/Hermitian for Cholesky factorization" unless self.hermitian?
|
206
|
+
l = self.clone.potrf_lower!.tril!
|
207
|
+
u = l.conjugate_transpose
|
208
|
+
[u,l]
|
177
209
|
end
|
178
210
|
|
179
211
|
#
|
180
212
|
# call-seq:
|
181
213
|
# factorize_lu -> ...
|
182
214
|
#
|
183
|
-
# LU factorization of a matrix.
|
184
|
-
#
|
185
|
-
#
|
186
|
-
#
|
187
|
-
#
|
188
|
-
|
215
|
+
# LU factorization of a matrix. Optionally return the permutation matrix.
|
216
|
+
# Note that computing the permutation matrix will introduce a slight memory
|
217
|
+
# and time overhead.
|
218
|
+
#
|
219
|
+
# == Arguments
|
220
|
+
#
|
221
|
+
# +with_permutation_matrix+ - If set to *true* will return the permutation
|
222
|
+
# matrix alongwith the LU factorization as a second return value.
|
223
|
+
#
|
224
|
+
def factorize_lu with_permutation_matrix=nil
|
189
225
|
raise(NotImplementedError, "only implemented for dense storage") unless self.stype == :dense
|
190
226
|
raise(NotImplementedError, "matrix is not 2-dimensional") unless self.dimensions == 2
|
191
227
|
|
192
|
-
t
|
193
|
-
|
194
|
-
t
|
228
|
+
t = self.clone
|
229
|
+
pivot = t.getrf!
|
230
|
+
return t unless with_permutation_matrix
|
231
|
+
|
232
|
+
[t, FactorizeLUMethods.permutation_matrix_from(pivot)]
|
233
|
+
end
|
234
|
+
|
235
|
+
# Reduce self to upper hessenberg form using householder transforms.
|
236
|
+
#
|
237
|
+
# == References
|
238
|
+
#
|
239
|
+
# * http://en.wikipedia.org/wiki/Hessenberg_matrix
|
240
|
+
# * http://www.mymathlib.com/c_source/matrices/eigen/hessenberg_orthog.c
|
241
|
+
def hessenberg
|
242
|
+
clone.hessenberg!
|
243
|
+
end
|
244
|
+
|
245
|
+
# Destructive version of #hessenberg
|
246
|
+
def hessenberg!
|
247
|
+
raise ShapeError, "Trying to reduce non 2D matrix to hessenberg form" if
|
248
|
+
shape.size != 2
|
249
|
+
raise ShapeError, "Trying to reduce non-square matrix to hessenberg form" if
|
250
|
+
shape[0] != shape[1]
|
251
|
+
raise StorageTypeError, "Matrix must be dense" if stype != :dense
|
252
|
+
raise TypeError, "Works with float matrices only" unless
|
253
|
+
[:float64,:float32].include?(dtype)
|
254
|
+
|
255
|
+
__hessenberg__(self)
|
256
|
+
self
|
257
|
+
end
|
258
|
+
|
259
|
+
# Solve the matrix equation AX = B, where A is +self+, B is the first
|
260
|
+
# argument, and X is returned. A must be a nxn square matrix, while B must be
|
261
|
+
# nxm. Only works with dense
|
262
|
+
# matrices and non-integer, non-object data types.
|
263
|
+
#
|
264
|
+
# == Usage
|
265
|
+
#
|
266
|
+
# a = NMatrix.new [2,2], [3,1,1,2], dtype: dtype
|
267
|
+
# b = NMatrix.new [2,1], [9,8], dtype: dtype
|
268
|
+
# a.solve(b)
|
269
|
+
def solve b
|
270
|
+
raise(ShapeError, "Must be called on square matrix") unless self.dim == 2 && self.shape[0] == self.shape[1]
|
271
|
+
raise(ShapeError, "number of rows of b must equal number of cols of self") if
|
272
|
+
self.shape[1] != b.shape[0]
|
273
|
+
raise ArgumentError, "only works with dense matrices" if self.stype != :dense
|
274
|
+
raise ArgumentError, "only works for non-integer, non-object dtypes" if
|
275
|
+
integer_dtype? or object_dtype? or b.integer_dtype? or b.object_dtype?
|
276
|
+
|
277
|
+
x = b.clone
|
278
|
+
clone = self.clone
|
279
|
+
n = self.shape[0]
|
280
|
+
nrhs = b.shape[1]
|
281
|
+
|
282
|
+
ipiv = NMatrix::LAPACK.clapack_getrf(:row, n, n, clone, n)
|
283
|
+
# When we call clapack_getrs with :row, actually only the first matrix
|
284
|
+
# (i.e. clone) is interpreted as row-major, while the other matrix (x)
|
285
|
+
# is interpreted as column-major. See here: http://math-atlas.sourceforge.net/faq.html#RowSolve
|
286
|
+
# So we must transpose x before and after
|
287
|
+
# calling it.
|
288
|
+
x = x.transpose
|
289
|
+
NMatrix::LAPACK.clapack_getrs(:row, :no_transpose, n, nrhs, clone, n, ipiv, x, n)
|
290
|
+
x.transpose
|
195
291
|
end
|
196
292
|
|
197
293
|
#
|
@@ -254,24 +350,80 @@ class NMatrix
|
|
254
350
|
def gesdd(workspace_size=nil)
|
255
351
|
self.clone.gesdd!(workspace_size)
|
256
352
|
end
|
353
|
+
|
257
354
|
#
|
258
355
|
# call-seq:
|
259
356
|
# laswp!(ary) -> NMatrix
|
260
357
|
#
|
261
|
-
# In-place permute the columns of a dense matrix using LASWP according to the order given
|
262
|
-
#
|
263
|
-
|
264
|
-
|
358
|
+
# In-place permute the columns of a dense matrix using LASWP according to the order given as an array +ary+.
|
359
|
+
#
|
360
|
+
# If +:convention+ is +:lapack+, then +ary+ represents a sequence of pair-wise permutations which are
|
361
|
+
# performed successively. That is, the i'th entry of +ary+ is the index of the column to swap
|
362
|
+
# the i'th column with, having already applied all earlier swaps.
|
363
|
+
#
|
364
|
+
# If +:convention+ is +:intuitive+, then +ary+ represents the order of columns after the permutation.
|
365
|
+
# That is, the i'th entry of +ary+ is the index of the column that will be in position i after the
|
366
|
+
# reordering (Matlab-like behaviour). This is the default.
|
367
|
+
#
|
368
|
+
# Not yet implemented for yale or list.
|
369
|
+
#
|
370
|
+
# == Arguments
|
371
|
+
#
|
372
|
+
# * +ary+ - An Array specifying the order of the columns. See above for details.
|
373
|
+
#
|
374
|
+
# == Options
|
375
|
+
#
|
376
|
+
# * +:covention+ - Possible values are +:lapack+ and +:intuitive+. Default is +:intuitive+. See above for details.
|
377
|
+
#
|
378
|
+
def laswp!(ary, opts={})
|
379
|
+
raise(StorageTypeError, "ATLAS functions only work on dense matrices") unless self.dense?
|
380
|
+
opts = { convention: :intuitive }.merge(opts)
|
381
|
+
|
382
|
+
if opts[:convention] == :intuitive
|
383
|
+
if ary.length != ary.uniq.length
|
384
|
+
raise(ArgumentError, "No duplicated entries in the order array are allowed under convention :intuitive")
|
385
|
+
end
|
386
|
+
n = self.shape[1]
|
387
|
+
p = []
|
388
|
+
order = (0...n).to_a
|
389
|
+
0.upto(n-2) do |i|
|
390
|
+
p[i] = order.index(ary[i])
|
391
|
+
order[i], order[p[i]] = order[p[i]], order[i]
|
392
|
+
end
|
393
|
+
p[n-1] = n-1
|
394
|
+
else
|
395
|
+
p = ary
|
396
|
+
end
|
397
|
+
|
398
|
+
NMatrix::LAPACK::laswp(self, p)
|
265
399
|
end
|
266
400
|
|
267
401
|
#
|
268
402
|
# call-seq:
|
269
403
|
# laswp(ary) -> NMatrix
|
270
404
|
#
|
271
|
-
# Permute the columns of a dense matrix using LASWP according to the order given in an
|
272
|
-
#
|
273
|
-
|
274
|
-
|
405
|
+
# Permute the columns of a dense matrix using LASWP according to the order given in an array +ary+.
|
406
|
+
#
|
407
|
+
# If +:convention+ is +:lapack+, then +ary+ represents a sequence of pair-wise permutations which are
|
408
|
+
# performed successively. That is, the i'th entry of +ary+ is the index of the column to swap
|
409
|
+
# the i'th column with, having already applied all earlier swaps. This is the default.
|
410
|
+
#
|
411
|
+
# If +:convention+ is +:intuitive+, then +ary+ represents the order of columns after the permutation.
|
412
|
+
# That is, the i'th entry of +ary+ is the index of the column that will be in position i after the
|
413
|
+
# reordering (Matlab-like behaviour).
|
414
|
+
#
|
415
|
+
# Not yet implemented for yale or list.
|
416
|
+
#
|
417
|
+
# == Arguments
|
418
|
+
#
|
419
|
+
# * +ary+ - An Array specifying the order of the columns. See above for details.
|
420
|
+
#
|
421
|
+
# == Options
|
422
|
+
#
|
423
|
+
# * +:covention+ - Possible values are +:lapack+ and +:intuitive+. Default is +:lapack+. See above for details.
|
424
|
+
#
|
425
|
+
def laswp(ary, opts={})
|
426
|
+
self.clone.laswp!(ary, opts)
|
275
427
|
end
|
276
428
|
|
277
429
|
#
|
@@ -279,7 +431,7 @@ class NMatrix
|
|
279
431
|
# det -> determinant
|
280
432
|
#
|
281
433
|
# Calculate the determinant by way of LU decomposition. This is accomplished
|
282
|
-
# using clapack_getrf, and then by
|
434
|
+
# using clapack_getrf, and then by taking the product of the diagonal elements. There is a
|
283
435
|
# risk of underflow/overflow.
|
284
436
|
#
|
285
437
|
# There are probably also more efficient ways to calculate the determinant.
|
@@ -290,35 +442,38 @@ class NMatrix
|
|
290
442
|
#
|
291
443
|
# This function is guaranteed to return the same type of data in the matrix
|
292
444
|
# upon which it is called.
|
293
|
-
# In other words, if you call it on a rational matrix, you'll get a rational
|
294
|
-
# number back.
|
295
445
|
#
|
296
|
-
# Integer matrices are converted to
|
446
|
+
# Integer matrices are converted to floating point matrices for the purposes of
|
297
447
|
# performing the calculation, as xGETRF can't work on integer matrices.
|
298
448
|
#
|
299
449
|
# * *Returns* :
|
300
450
|
# - The determinant of the matrix. It's the same type as the matrix's dtype.
|
301
451
|
# * *Raises* :
|
302
|
-
# - +
|
452
|
+
# - +ShapeError+ -> Must be used on square matrices.
|
303
453
|
#
|
304
454
|
def det
|
305
|
-
raise(
|
455
|
+
raise(ShapeError, "determinant can be calculated only for square matrices") unless self.dim == 2 && self.shape[0] == self.shape[1]
|
306
456
|
|
307
457
|
# Cast to a dtype for which getrf is implemented
|
308
|
-
new_dtype =
|
458
|
+
new_dtype = self.integer_dtype? ? :float64 : self.dtype
|
309
459
|
copy = self.cast(:dense, new_dtype)
|
310
460
|
|
311
461
|
# Need to know the number of permutations. We'll add up the diagonals of
|
312
462
|
# the factorized matrix.
|
313
463
|
pivot = copy.getrf!
|
314
464
|
|
315
|
-
|
465
|
+
num_perm = 0 #number of permutations
|
466
|
+
pivot.each_with_index do |swap, i|
|
467
|
+
#pivot indexes rows starting from 1, instead of 0, so need to subtract 1 here
|
468
|
+
num_perm += 1 if swap-1 != i
|
469
|
+
end
|
470
|
+
prod = num_perm % 2 == 1 ? -1 : 1 # odd permutations => negative
|
316
471
|
[shape[0],shape[1]].min.times do |i|
|
317
472
|
prod *= copy[i,i]
|
318
473
|
end
|
319
474
|
|
320
475
|
# Convert back to an integer if necessary
|
321
|
-
new_dtype != self.dtype ? prod.
|
476
|
+
new_dtype != self.dtype ? prod.round : prod #prevent rounding errors
|
322
477
|
end
|
323
478
|
|
324
479
|
#
|
@@ -342,6 +497,120 @@ class NMatrix
|
|
342
497
|
self.cast(new_stype, NMatrix::upcast(dtype, :complex64)).complex_conjugate!
|
343
498
|
end
|
344
499
|
|
500
|
+
# Calculate the variance co-variance matrix
|
501
|
+
#
|
502
|
+
# == Options
|
503
|
+
#
|
504
|
+
# * +:for_sample_data+ - Default true. If set to false will consider the denominator for
|
505
|
+
# population data (i.e. N, as opposed to N-1 for sample data).
|
506
|
+
#
|
507
|
+
# == References
|
508
|
+
#
|
509
|
+
# * http://stattrek.com/matrix-algebra/covariance-matrix.aspx
|
510
|
+
def cov(opts={})
|
511
|
+
raise TypeError, "Only works for non-integer dtypes" if integer_dtype?
|
512
|
+
opts = {
|
513
|
+
for_sample_data: true
|
514
|
+
}.merge(opts)
|
515
|
+
|
516
|
+
denominator = opts[:for_sample_data] ? rows - 1 : rows
|
517
|
+
ones = NMatrix.ones [rows,1]
|
518
|
+
deviation_scores = self - ones.dot(ones.transpose).dot(self) / rows
|
519
|
+
deviation_scores.transpose.dot(deviation_scores) / denominator
|
520
|
+
end
|
521
|
+
|
522
|
+
# Calculate the correlation matrix.
|
523
|
+
def corr
|
524
|
+
raise NotImplementedError, "Does not work for complex dtypes" if complex_dtype?
|
525
|
+
standard_deviation = std
|
526
|
+
cov / (standard_deviation.transpose.dot(standard_deviation))
|
527
|
+
end
|
528
|
+
|
529
|
+
# Raise a square matrix to a power. Be careful of numeric overflows!
|
530
|
+
# In case *n* is 0, an identity matrix of the same dimension is returned. In case
|
531
|
+
# of negative *n*, the matrix is inverted and the absolute value of *n* taken
|
532
|
+
# for computing the power.
|
533
|
+
#
|
534
|
+
# == Arguments
|
535
|
+
#
|
536
|
+
# * +n+ - Integer to which self is to be raised.
|
537
|
+
#
|
538
|
+
# == References
|
539
|
+
#
|
540
|
+
# * R.G Dromey - How to Solve it by Computer. Link -
|
541
|
+
# http://www.amazon.com/Solve-Computer-Prentice-Hall-International-Science/dp/0134340019/ref=sr_1_1?ie=UTF8&qid=1422605572&sr=8-1&keywords=how+to+solve+it+by+computer
|
542
|
+
def pow n
|
543
|
+
raise ShapeError, "Only works with 2D square matrices." if
|
544
|
+
shape[0] != shape[1] or shape.size != 2
|
545
|
+
raise TypeError, "Only works with integer powers" unless n.is_a?(Integer)
|
546
|
+
|
547
|
+
sequence = (integer_dtype? ? self.cast(dtype: :int64) : self).clone
|
548
|
+
product = NMatrix.eye shape[0], dtype: sequence.dtype, stype: sequence.stype
|
549
|
+
|
550
|
+
if n == 0
|
551
|
+
return NMatrix.eye(shape, dtype: dtype, stype: stype)
|
552
|
+
elsif n == 1
|
553
|
+
return sequence
|
554
|
+
elsif n < 0
|
555
|
+
n = n.abs
|
556
|
+
sequence.invert!
|
557
|
+
product = NMatrix.eye shape[0], dtype: sequence.dtype, stype: sequence.stype
|
558
|
+
end
|
559
|
+
|
560
|
+
# Decompose n to reduce the number of multiplications.
|
561
|
+
while n > 0
|
562
|
+
product = product.dot(sequence) if n % 2 == 1
|
563
|
+
n = n / 2
|
564
|
+
sequence = sequence.dot(sequence)
|
565
|
+
end
|
566
|
+
|
567
|
+
product
|
568
|
+
end
|
569
|
+
|
570
|
+
# Compute the Kronecker product of +self+ and other NMatrix
|
571
|
+
#
|
572
|
+
# === Arguments
|
573
|
+
#
|
574
|
+
# * +mat+ - A 2D NMatrix object
|
575
|
+
#
|
576
|
+
# === Usage
|
577
|
+
#
|
578
|
+
# a = NMatrix.new([2,2],[1,2,
|
579
|
+
# 3,4])
|
580
|
+
# b = NMatrix.new([2,3],[1,1,1,
|
581
|
+
# 1,1,1], dtype: :float64)
|
582
|
+
# a.kron_prod(b) # => [ [1.0, 1.0, 1.0, 2.0, 2.0, 2.0]
|
583
|
+
# [1.0, 1.0, 1.0, 2.0, 2.0, 2.0]
|
584
|
+
# [3.0, 3.0, 3.0, 4.0, 4.0, 4.0]
|
585
|
+
# [3.0, 3.0, 3.0, 4.0, 4.0, 4.0] ]
|
586
|
+
#
|
587
|
+
def kron_prod(mat)
|
588
|
+
unless self.dimensions==2 and mat.dimensions==2
|
589
|
+
raise ShapeError, "Implemented for 2D NMatrix objects only."
|
590
|
+
end
|
591
|
+
|
592
|
+
# compute the shape [n,m] of the product matrix
|
593
|
+
n, m = self.shape[0]*mat.shape[0], self.shape[1]*mat.shape[1]
|
594
|
+
# compute the entries of the product matrix
|
595
|
+
kron_prod_array = []
|
596
|
+
if self.yale?
|
597
|
+
# +:yale+ requires to get the row by copy in order to apply +#transpose+ to it
|
598
|
+
self.each_row(getby=:copy) do |selfr|
|
599
|
+
mat.each_row do |matr|
|
600
|
+
kron_prod_array += (selfr.transpose.dot matr).to_flat_a
|
601
|
+
end
|
602
|
+
end
|
603
|
+
else
|
604
|
+
self.each_row do |selfr|
|
605
|
+
mat.each_row do |matr|
|
606
|
+
kron_prod_array += (selfr.transpose.dot matr).to_flat_a
|
607
|
+
end
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
NMatrix.new([n,m], kron_prod_array)
|
612
|
+
end
|
613
|
+
|
345
614
|
#
|
346
615
|
# call-seq:
|
347
616
|
# conjugate_transpose -> NMatrix
|
@@ -356,27 +625,6 @@ class NMatrix
|
|
356
625
|
self.transpose.complex_conjugate!
|
357
626
|
end
|
358
627
|
|
359
|
-
#
|
360
|
-
# call-seq:
|
361
|
-
# hermitian? -> Boolean
|
362
|
-
#
|
363
|
-
# A hermitian matrix is a complex square matrix that is equal to its
|
364
|
-
# conjugate transpose. (http://en.wikipedia.org/wiki/Hermitian_matrix)
|
365
|
-
#
|
366
|
-
# * *Returns* :
|
367
|
-
# - True if +self+ is a hermitian matrix, nil otherwise.
|
368
|
-
#
|
369
|
-
def hermitian?
|
370
|
-
return false if self.dim != 2 or self.shape[0] != self.shape[1]
|
371
|
-
|
372
|
-
if [:complex64, :complex128].include?(self.dtype)
|
373
|
-
# TODO: Write much faster Hermitian test in C
|
374
|
-
self.eql?(self.conjugate_transpose)
|
375
|
-
else
|
376
|
-
symmetric?
|
377
|
-
end
|
378
|
-
end
|
379
|
-
|
380
628
|
#
|
381
629
|
# call-seq:
|
382
630
|
# trace -> Numeric
|
@@ -555,7 +803,10 @@ class NMatrix
|
|
555
803
|
#
|
556
804
|
# Return the sum of the contents of the vector. This is the BLAS asum routine.
|
557
805
|
def asum incx=1, n=nil
|
558
|
-
|
806
|
+
if self.shape == [1]
|
807
|
+
return self[0].abs unless self.complex_dtype?
|
808
|
+
return self[0].real.abs + self[0].imag.abs
|
809
|
+
end
|
559
810
|
return method_missing(:asum, incx, n) unless vector?
|
560
811
|
NMatrix::BLAS::asum(self, incx, self.size / incx)
|
561
812
|
end
|
@@ -608,7 +859,7 @@ protected
|
|
608
859
|
# These don't actually take an argument -- they're called reverse-polish style on the matrix.
|
609
860
|
# This group always gets casted to float64.
|
610
861
|
[:log, :log2, :log10, :sqrt, :sin, :cos, :tan, :acos, :asin, :atan, :cosh, :sinh, :tanh, :acosh,
|
611
|
-
:asinh, :atanh, :exp, :erf, :erfc, :gamma, :cbrt].each do |ewop|
|
862
|
+
:asinh, :atanh, :exp, :erf, :erfc, :gamma, :cbrt, :round].each do |ewop|
|
612
863
|
define_method("__list_unary_#{ewop}__") do
|
613
864
|
self.__list_map_stored__(nil) { |l| Math.send(ewop, l) }.cast(stype, NMatrix.upcast(dtype, :float64))
|
614
865
|
end
|
@@ -648,31 +899,31 @@ protected
|
|
648
899
|
end
|
649
900
|
#:startdoc:
|
650
901
|
|
651
|
-
# These are for rounding each value of a matrix
|
652
|
-
def __list_unary_round__
|
902
|
+
# These are for rounding each value of a matrix. Takes an optional argument
|
903
|
+
def __list_unary_round__(precision)
|
653
904
|
if self.complex_dtype?
|
654
|
-
self.__list_map_stored__(nil) { |l| Complex(l.real.round, l.imag.round) }
|
905
|
+
self.__list_map_stored__(nil) { |l| Complex(l.real.round(precision), l.imag.round(precision)) }
|
655
906
|
.cast(stype, dtype)
|
656
907
|
else
|
657
|
-
self.__list_map_stored__(nil) { |l| l.round }.cast(stype, dtype)
|
908
|
+
self.__list_map_stored__(nil) { |l| l.round(precision) }.cast(stype, dtype)
|
658
909
|
end
|
659
910
|
end
|
660
911
|
|
661
|
-
def __yale_unary_round__
|
912
|
+
def __yale_unary_round__(precision)
|
662
913
|
if self.complex_dtype?
|
663
|
-
self.__yale_map_stored__ { |l| Complex(l.real.round, l.imag.round) }
|
914
|
+
self.__yale_map_stored__ { |l| Complex(l.real.round(precision), l.imag.round(precision)) }
|
664
915
|
.cast(stype, dtype)
|
665
916
|
else
|
666
|
-
self.__yale_map_stored__ { |l| l.round }.cast(stype, dtype)
|
917
|
+
self.__yale_map_stored__ { |l| l.round(precision) }.cast(stype, dtype)
|
667
918
|
end
|
668
919
|
end
|
669
920
|
|
670
|
-
def __dense_unary_round__
|
921
|
+
def __dense_unary_round__(precision)
|
671
922
|
if self.complex_dtype?
|
672
|
-
self.__dense_map__ { |l| Complex(l.real.round, l.imag.round) }
|
923
|
+
self.__dense_map__ { |l| Complex(l.real.round(precision), l.imag.round(precision)) }
|
673
924
|
.cast(stype, dtype)
|
674
925
|
else
|
675
|
-
self.__dense_map__ { |l| l.round }.cast(stype, dtype)
|
926
|
+
self.__dense_map__ { |l| l.round(precision) }.cast(stype, dtype)
|
676
927
|
end
|
677
928
|
end
|
678
929
|
|
@@ -680,7 +931,7 @@ protected
|
|
680
931
|
def dtype_for_floor_or_ceil
|
681
932
|
if self.integer_dtype? or [:complex64, :complex128, :object].include?(self.dtype)
|
682
933
|
return_dtype = dtype
|
683
|
-
elsif [:float32, :float64
|
934
|
+
elsif [:float32, :float64].include?(self.dtype)
|
684
935
|
return_dtype = :int64
|
685
936
|
end
|
686
937
|
|