nmatrix 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
|