nmatrix 0.1.0.rc5 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -1
- data/Gemfile +0 -2
- data/History.txt +39 -4
- data/LICENSE.txt +3 -1
- data/Manifest.txt +2 -0
- data/README.rdoc +6 -14
- data/Rakefile +4 -1
- data/ext/nmatrix/data/data.cpp +1 -1
- data/ext/nmatrix/data/data.h +2 -1
- data/ext/nmatrix/data/rational.h +230 -226
- data/ext/nmatrix/extconf.rb +7 -4
- data/ext/nmatrix/math.cpp +259 -172
- data/ext/nmatrix/math/getri.h +2 -2
- data/ext/nmatrix/math/math.h +1 -1
- data/ext/nmatrix/ruby_constants.cpp +0 -1
- data/ext/nmatrix/ruby_nmatrix.c +55 -32
- data/ext/nmatrix/storage/dense/dense.cpp +1 -0
- data/ext/nmatrix/storage/yale/yale.cpp +12 -14
- data/ext/nmatrix/ttable_helper.rb +0 -1
- data/lib/nmatrix.rb +5 -0
- data/lib/nmatrix/homogeneous.rb +98 -0
- data/lib/nmatrix/io/fortran_format.rb +135 -0
- data/lib/nmatrix/io/harwell_boeing.rb +220 -0
- data/lib/nmatrix/io/market.rb +18 -8
- data/lib/nmatrix/io/mat5_reader.rb +16 -111
- data/lib/nmatrix/io/mat_reader.rb +3 -5
- data/lib/nmatrix/io/point_cloud.rb +27 -28
- data/lib/nmatrix/lapack.rb +3 -1
- data/lib/nmatrix/math.rb +112 -43
- data/lib/nmatrix/monkeys.rb +67 -11
- data/lib/nmatrix/nmatrix.rb +56 -33
- data/lib/nmatrix/rspec.rb +2 -2
- data/lib/nmatrix/shortcuts.rb +42 -25
- data/lib/nmatrix/version.rb +4 -4
- data/nmatrix.gemspec +4 -3
- data/spec/03_nmatrix_monkeys_spec.rb +72 -0
- data/spec/blas_spec.rb +4 -0
- data/spec/homogeneous_spec.rb +12 -4
- data/spec/io/fortran_format_spec.rb +88 -0
- data/spec/io/harwell_boeing_spec.rb +98 -0
- data/spec/io/test.rua +9 -0
- data/spec/math_spec.rb +51 -9
- metadata +38 -9
@@ -30,13 +30,12 @@
|
|
30
30
|
require 'packable'
|
31
31
|
|
32
32
|
module NMatrix::IO::Matlab
|
33
|
-
|
33
|
+
|
34
34
|
# Class for parsing a .mat file stream.
|
35
35
|
#
|
36
36
|
# The full format of .mat files is available here:
|
37
37
|
# * http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf
|
38
|
-
|
39
|
-
class MatReader
|
38
|
+
class MatReader #:nodoc:
|
40
39
|
MDTYPE_UNPACK_ARGS = {
|
41
40
|
:miINT8 => [Integer, {:signed => true, :bytes => 1}],
|
42
41
|
:miUINT8 => [Integer, {:signed => false, :bytes => 1}],
|
@@ -146,7 +145,7 @@ module NMatrix::IO::Matlab
|
|
146
145
|
|
147
146
|
attr_reader :byte_order
|
148
147
|
|
149
|
-
|
148
|
+
|
150
149
|
# call-seq:
|
151
150
|
# new(stream, options = {}) -> MatReader
|
152
151
|
#
|
@@ -160,7 +159,6 @@ module NMatrix::IO::Matlab
|
|
160
159
|
@byte_order = options[:byte_order] || guess_byte_order
|
161
160
|
end
|
162
161
|
|
163
|
-
#
|
164
162
|
# call-seq:
|
165
163
|
# guess_byte_order -> Symbol
|
166
164
|
#
|
@@ -27,15 +27,34 @@
|
|
27
27
|
#
|
28
28
|
#++
|
29
29
|
|
30
|
-
# Reader for
|
30
|
+
# Reader for Point Cloud Data (PCD) file format.
|
31
31
|
#
|
32
|
-
#
|
32
|
+
# The documentation of this format can be found in:
|
33
33
|
#
|
34
|
-
#
|
34
|
+
# http://pointclouds.org/documentation/tutorials/pcd_file_format.php
|
35
35
|
#
|
36
|
+
# Note that this implementation does not take the width or height parameters
|
37
|
+
# into account.
|
36
38
|
module NMatrix::IO::PointCloud
|
37
39
|
|
38
|
-
|
40
|
+
# For UINT, just add 1 to the index.
|
41
|
+
INT_DTYPE_BY_SIZE = [:int8, :int8, :int16, :int32, :int64, :int64] #:nodoc:
|
42
|
+
FLOAT_DTYPE_BY_SIZE = {4 => :float32, 8 => :float64} #:nodoc:
|
43
|
+
|
44
|
+
class << self
|
45
|
+
# call-seq:
|
46
|
+
# load(filename) -> NMatrix
|
47
|
+
#
|
48
|
+
# * *Arguments* :
|
49
|
+
# - +filename+ -> String giving the name of the file to be loaded.
|
50
|
+
#
|
51
|
+
# Load a Point Cloud Library PCD file as a matrix.
|
52
|
+
def load(filename)
|
53
|
+
MetaReader.new(filename).matrix
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class MetaReader #:nodoc:
|
39
58
|
ENTRIES = [:version, :fields, :size, :type, :count, :width, :height, :viewpoint, :points, :data]
|
40
59
|
ASSIGNS = [:version=, :fields=, :size=, :type=, :count=, :width=, :height=, :viewpoint=, :points=, :data=]
|
41
60
|
CONVERT = [:to_s, :downcase_to_sym, :to_i, :downcase_to_sym, :to_i, :to_i, :to_i, :to_f, :to_i, :downcase_to_sym]
|
@@ -49,7 +68,7 @@ module NMatrix::IO::PointCloud
|
|
49
68
|
class << self
|
50
69
|
|
51
70
|
# Given a type and a number of bytes, figure out an appropriate dtype
|
52
|
-
def dtype_by_type_and_size t, s
|
71
|
+
def dtype_by_type_and_size t, s
|
53
72
|
if t == :f
|
54
73
|
FLOAT_DTYPE_BY_SIZE[s]
|
55
74
|
elsif t == :u
|
@@ -104,7 +123,7 @@ module NMatrix::IO::PointCloud
|
|
104
123
|
|
105
124
|
protected
|
106
125
|
# Read the current entry of the header.
|
107
|
-
def read_entry f, entry, assign=nil, convert=nil
|
126
|
+
def read_entry f, entry, assign=nil, convert=nil
|
108
127
|
assign ||= (entry.to_s + "=").to_sym
|
109
128
|
|
110
129
|
while line = f.gets
|
@@ -133,7 +152,7 @@ module NMatrix::IO::PointCloud
|
|
133
152
|
|
134
153
|
# Determine the dtype for a matrix based on the types and sizes given in the PCD.
|
135
154
|
# Call this only after read_entry has been called.
|
136
|
-
def dtype
|
155
|
+
def dtype
|
137
156
|
@dtype ||= begin
|
138
157
|
dtypes = self.type.map.with_index do |t,k|
|
139
158
|
MetaReader.dtype_by_type_and_size(t, size[k])
|
@@ -158,25 +177,5 @@ module NMatrix::IO::PointCloud
|
|
158
177
|
self.fields.size
|
159
178
|
]
|
160
179
|
end
|
161
|
-
|
162
|
-
end
|
163
|
-
|
164
|
-
# For UINT, just add 1 to the index.
|
165
|
-
INT_DTYPE_BY_SIZE = [:int8, :int8, :int16, :int32, :int64, :int64]
|
166
|
-
FLOAT_DTYPE_BY_SIZE = {4 => :float32, 8 => :float64}
|
167
|
-
|
168
|
-
class << self
|
169
|
-
|
170
|
-
#
|
171
|
-
# call-seq:
|
172
|
-
# load(filename) -> NMatrix
|
173
|
-
#
|
174
|
-
# * *Arguments* :
|
175
|
-
# - +filename+ -> String giving the name of the file to be loaded.
|
176
|
-
#
|
177
|
-
# Load a Point Cloud Library PCD file as a matrix.
|
178
|
-
def load(filename)
|
179
|
-
MetaReader.new(filename).matrix
|
180
|
-
end
|
181
180
|
end
|
182
|
-
end
|
181
|
+
end
|
data/lib/nmatrix/lapack.rb
CHANGED
@@ -168,7 +168,9 @@ class NMatrix
|
|
168
168
|
# Optionally accepts a +workspace_size+ parameter, which will be honored only if it is larger than what LAPACK
|
169
169
|
# requires.
|
170
170
|
#
|
171
|
-
def gesdd(matrix, workspace_size=
|
171
|
+
def gesdd(matrix, workspace_size=nil)
|
172
|
+
min_workspace_size = matrix.shape.min * (6 + 4 * matrix.shape.min) + matrix.shape.max
|
173
|
+
workspace_size = min_workspace_size if workspace_size.nil? || workspace_size < min_workspace_size
|
172
174
|
result = alloc_svd_result(matrix)
|
173
175
|
NMatrix::LAPACK::lapack_gesdd(:a, matrix.shape[0], matrix.shape[1], matrix, matrix.shape[0], result[1], result[0], matrix.shape[0], result[2], matrix.shape[1], workspace_size)
|
174
176
|
result
|
data/lib/nmatrix/math.rb
CHANGED
@@ -30,7 +30,7 @@
|
|
30
30
|
|
31
31
|
class NMatrix
|
32
32
|
|
33
|
-
module NMMath
|
33
|
+
module NMMath #:nodoc:
|
34
34
|
METHODS_ARITY_2 = [:atan2, :ldexp, :hypot]
|
35
35
|
METHODS_ARITY_1 = [:cos, :sin, :tan, :acos, :asin, :atan, :cosh, :sinh, :tanh, :acosh,
|
36
36
|
:asinh, :atanh, :exp, :log2, :log10, :sqrt, :cbrt, :erf, :erfc, :gamma, :-@]
|
@@ -40,48 +40,65 @@ class NMatrix
|
|
40
40
|
# call-seq:
|
41
41
|
# invert! -> NMatrix
|
42
42
|
#
|
43
|
-
# Use LAPACK to calculate the inverse of the matrix (in-place)
|
44
|
-
# dense matrices.
|
45
|
-
#
|
46
|
-
# Note: If you don't have LAPACK, e.g., on a Mac, this may not work yet. Use
|
47
|
-
# invert instead (which still probably won't work if your matrix is larger than 3x3).
|
43
|
+
# Use LAPACK to calculate the inverse of the matrix (in-place) if available.
|
44
|
+
# Only works on dense matrices. Alternatively uses in-place Gauss-Jordan
|
45
|
+
# elimination.
|
48
46
|
#
|
49
47
|
def invert!
|
50
|
-
|
51
|
-
|
48
|
+
if NMatrix.has_clapack?
|
49
|
+
# Get the pivot array; factor the matrix
|
50
|
+
pivot = self.getrf!
|
52
51
|
|
53
|
-
|
54
|
-
|
52
|
+
# Now calculate the inverse using the pivot array
|
53
|
+
NMatrix::LAPACK::clapack_getri(:row, self.shape[1], self, self.shape[1], pivot)
|
55
54
|
|
56
|
-
|
55
|
+
self
|
56
|
+
else
|
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
|
57
64
|
end
|
58
65
|
|
59
66
|
#
|
60
67
|
# call-seq:
|
61
68
|
# invert -> NMatrix
|
62
69
|
#
|
63
|
-
# Make a copy of the matrix, then invert
|
64
|
-
#
|
70
|
+
# Make a copy of the matrix, then invert using Gauss-Jordan elimination.
|
71
|
+
# Works without LAPACK.
|
65
72
|
#
|
66
73
|
#
|
67
74
|
# * *Returns* :
|
68
75
|
# - A dense NMatrix.
|
69
76
|
#
|
70
|
-
def invert
|
71
|
-
if
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
+
def invert lda=nil, ldb=nil
|
78
|
+
if lda.nil? and ldb.nil?
|
79
|
+
if NMatrix.has_clapack?
|
80
|
+
begin
|
81
|
+
self.cast(:dense, self.dtype).invert! # call CLAPACK version
|
82
|
+
rescue NotImplementedError # probably a rational matrix
|
83
|
+
inverse = self.clone
|
84
|
+
__inverse__(inverse, false)
|
85
|
+
end
|
86
|
+
elsif self.integer_dtype? # FIXME: This check is probably too slow.
|
87
|
+
rational_self = self.cast(dtype: :rational128)
|
88
|
+
inverse = rational_self.clone
|
89
|
+
rational_self.__inverse__(inverse, false)
|
90
|
+
else
|
91
|
+
inverse = self.clone
|
92
|
+
__inverse__(inverse, false)
|
77
93
|
end
|
78
|
-
elsif self.integer_dtype? # FIXME: This check is probably too slow.
|
79
|
-
rational_self = self.cast(dtype: :rational128)
|
80
|
-
inverse = rational_self.clone_structure
|
81
|
-
rational_self.__inverse_exact__(inverse)
|
82
94
|
else
|
83
|
-
inverse
|
84
|
-
|
95
|
+
inverse = self.clone_structure
|
96
|
+
if self.integer_dtype?
|
97
|
+
__inverse_exact__(inverse.cast(dtype: :rational128), lda, ldb)
|
98
|
+
else
|
99
|
+
dtype = self.dtype
|
100
|
+
__inverse_exact__(inverse, lda, ldb)
|
101
|
+
end
|
85
102
|
end
|
86
103
|
end
|
87
104
|
alias :inverse :invert
|
@@ -182,7 +199,7 @@ class NMatrix
|
|
182
199
|
# gesvd! -> [u, sigma, v_transpose]
|
183
200
|
# gesvd! -> [u, sigma, v_conjugate_transpose] # complex
|
184
201
|
#
|
185
|
-
# Compute the singular value decomposition of a matrix using LAPACK's GESVD function.
|
202
|
+
# Compute the singular value decomposition of a matrix using LAPACK's GESVD function.
|
186
203
|
# This is destructive, modifying the source NMatrix. See also #gesdd.
|
187
204
|
#
|
188
205
|
# Optionally accepts a +workspace_size+ parameter, which will be honored only if it is larger than what LAPACK
|
@@ -219,7 +236,7 @@ class NMatrix
|
|
219
236
|
# Optionally accepts a +workspace_size+ parameter, which will be honored only if it is larger than what LAPACK
|
220
237
|
# requires.
|
221
238
|
#
|
222
|
-
def gesdd!(workspace_size=
|
239
|
+
def gesdd!(workspace_size=nil)
|
223
240
|
NMatrix::LAPACK::gesdd(self, workspace_size)
|
224
241
|
end
|
225
242
|
|
@@ -234,7 +251,7 @@ class NMatrix
|
|
234
251
|
# Optionally accepts a +workspace_size+ parameter, which will be honored only if it is larger than what LAPACK
|
235
252
|
# requires.
|
236
253
|
#
|
237
|
-
def gesdd(workspace_size=
|
254
|
+
def gesdd(workspace_size=nil)
|
238
255
|
self.clone.gesdd!(workspace_size)
|
239
256
|
end
|
240
257
|
#
|
@@ -360,6 +377,26 @@ class NMatrix
|
|
360
377
|
end
|
361
378
|
end
|
362
379
|
|
380
|
+
#
|
381
|
+
# call-seq:
|
382
|
+
# trace -> Numeric
|
383
|
+
#
|
384
|
+
# Calculates the trace of an nxn matrix.
|
385
|
+
#
|
386
|
+
# * *Raises* :
|
387
|
+
# - +ShapeError+ -> Expected square matrix
|
388
|
+
#
|
389
|
+
# * *Returns* :
|
390
|
+
# - The trace of the matrix (a numeric value)
|
391
|
+
#
|
392
|
+
def trace
|
393
|
+
raise(ShapeError, "Expected square matrix") unless self.shape[0] == self.shape[1] && self.dim == 2
|
394
|
+
|
395
|
+
(0...self.shape[0]).inject(0) do |total,i|
|
396
|
+
total + self[i,i]
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
363
400
|
##
|
364
401
|
# call-seq:
|
365
402
|
# mean() -> NMatrix
|
@@ -518,6 +555,7 @@ class NMatrix
|
|
518
555
|
#
|
519
556
|
# Return the sum of the contents of the vector. This is the BLAS asum routine.
|
520
557
|
def asum incx=1, n=nil
|
558
|
+
return self[0].abs if self.shape == [1]
|
521
559
|
return method_missing(:asum, incx, n) unless vector?
|
522
560
|
NMatrix::BLAS::asum(self, incx, self.size / incx)
|
523
561
|
end
|
@@ -569,7 +607,8 @@ protected
|
|
569
607
|
|
570
608
|
# These don't actually take an argument -- they're called reverse-polish style on the matrix.
|
571
609
|
# This group always gets casted to float64.
|
572
|
-
[:log, :log2, :log10, :sqrt, :sin, :cos, :tan, :acos, :asin, :atan, :cosh, :sinh, :tanh, :acosh,
|
610
|
+
[: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|
|
573
612
|
define_method("__list_unary_#{ewop}__") do
|
574
613
|
self.__list_map_stored__(nil) { |l| Math.send(ewop, l) }.cast(stype, NMatrix.upcast(dtype, :float64))
|
575
614
|
end
|
@@ -581,7 +620,8 @@ protected
|
|
581
620
|
end
|
582
621
|
end
|
583
622
|
|
584
|
-
|
623
|
+
#:stopdoc:
|
624
|
+
# log takes an optional single argument, the base. Default to natural log.
|
585
625
|
def __list_unary_log__(base)
|
586
626
|
self.__list_map_stored__(nil) { |l| Math.log(l, base) }.cast(stype, NMatrix.upcast(dtype, :float64))
|
587
627
|
end
|
@@ -606,6 +646,35 @@ protected
|
|
606
646
|
def __dense_unary_negate__
|
607
647
|
self.__dense_map__ { |l| -l }.cast(stype, dtype)
|
608
648
|
end
|
649
|
+
#:startdoc:
|
650
|
+
|
651
|
+
# These are for rounding each value of a matrix
|
652
|
+
def __list_unary_round__
|
653
|
+
if self.complex_dtype?
|
654
|
+
self.__list_map_stored__(nil) { |l| Complex(l.real.round, l.imag.round) }
|
655
|
+
.cast(stype, dtype)
|
656
|
+
else
|
657
|
+
self.__list_map_stored__(nil) { |l| l.round }.cast(stype, dtype)
|
658
|
+
end
|
659
|
+
end
|
660
|
+
|
661
|
+
def __yale_unary_round__
|
662
|
+
if self.complex_dtype?
|
663
|
+
self.__yale_map_stored__ { |l| Complex(l.real.round, l.imag.round) }
|
664
|
+
.cast(stype, dtype)
|
665
|
+
else
|
666
|
+
self.__yale_map_stored__ { |l| l.round }.cast(stype, dtype)
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
def __dense_unary_round__
|
671
|
+
if self.complex_dtype?
|
672
|
+
self.__dense_map__ { |l| Complex(l.real.round, l.imag.round) }
|
673
|
+
.cast(stype, dtype)
|
674
|
+
else
|
675
|
+
self.__dense_map__ { |l| l.round }.cast(stype, dtype)
|
676
|
+
end
|
677
|
+
end
|
609
678
|
|
610
679
|
# These are for calculating the floor or ceil of matrix
|
611
680
|
def dtype_for_floor_or_ceil
|
@@ -618,36 +687,36 @@ protected
|
|
618
687
|
return_dtype
|
619
688
|
end
|
620
689
|
|
621
|
-
[:floor, :ceil].each do |meth|
|
690
|
+
[:floor, :ceil].each do |meth|
|
622
691
|
define_method("__list_unary_#{meth}__") do
|
623
692
|
return_dtype = dtype_for_floor_or_ceil
|
624
693
|
|
625
694
|
if [:complex64, :complex128].include?(self.dtype)
|
626
|
-
self.__list_map_stored__(nil) { |l| Complex(l.real.send(meth), l.imag.send(meth)) }.cast(stype, return_dtype)
|
695
|
+
self.__list_map_stored__(nil) { |l| Complex(l.real.send(meth), l.imag.send(meth)) }.cast(stype, return_dtype)
|
627
696
|
else
|
628
|
-
self.__list_map_stored__(nil) { |l| l.send(meth) }.cast(stype, return_dtype)
|
697
|
+
self.__list_map_stored__(nil) { |l| l.send(meth) }.cast(stype, return_dtype)
|
629
698
|
end
|
630
699
|
end
|
631
|
-
|
632
|
-
define_method("__yale_unary_#{meth}__") do
|
700
|
+
|
701
|
+
define_method("__yale_unary_#{meth}__") do
|
633
702
|
return_dtype = dtype_for_floor_or_ceil
|
634
703
|
|
635
704
|
if [:complex64, :complex128].include?(self.dtype)
|
636
|
-
self.__yale_map_stored__ { |l| Complex(l.real.send(meth), l.imag.send(meth)) }.cast(stype, return_dtype)
|
705
|
+
self.__yale_map_stored__ { |l| Complex(l.real.send(meth), l.imag.send(meth)) }.cast(stype, return_dtype)
|
637
706
|
else
|
638
|
-
self.__yale_map_stored__ { |l| l.send(meth) }.cast(stype, return_dtype)
|
707
|
+
self.__yale_map_stored__ { |l| l.send(meth) }.cast(stype, return_dtype)
|
639
708
|
end
|
640
709
|
end
|
641
|
-
|
710
|
+
|
642
711
|
define_method("__dense_unary_#{meth}__") do
|
643
712
|
return_dtype = dtype_for_floor_or_ceil
|
644
|
-
|
713
|
+
|
645
714
|
if [:complex64, :complex128].include?(self.dtype)
|
646
|
-
self.__dense_map__ { |l| Complex(l.real.send(meth), l.imag.send(meth)) }.cast(stype, return_dtype)
|
715
|
+
self.__dense_map__ { |l| Complex(l.real.send(meth), l.imag.send(meth)) }.cast(stype, return_dtype)
|
647
716
|
else
|
648
|
-
self.__dense_map__ { |l| l.send(meth) }.cast(stype, return_dtype)
|
717
|
+
self.__dense_map__ { |l| l.send(meth) }.cast(stype, return_dtype)
|
649
718
|
end
|
650
|
-
end
|
719
|
+
end
|
651
720
|
end
|
652
721
|
|
653
722
|
# These take two arguments. One might be a matrix, and one might be a scalar.
|
data/lib/nmatrix/monkeys.rb
CHANGED
@@ -38,19 +38,55 @@ class Array
|
|
38
38
|
# You must provide a shape for the matrix as the first argument.
|
39
39
|
#
|
40
40
|
# == Arguments:
|
41
|
-
# <tt>shape</tt> :: Array describing matrix dimensions (or Fixnum for square)
|
42
|
-
#
|
41
|
+
# <tt>shape</tt> :: Array describing matrix dimensions (or Fixnum for square).
|
42
|
+
# If not provided, will be intuited through #shape.
|
43
|
+
# <tt>dtype</tt> :: Override data type (e.g., to store a Float as :float32
|
44
|
+
# instead of :float64) -- optional.
|
43
45
|
# <tt>stype</tt> :: Optional storage type (defaults to :dense)
|
44
|
-
def to_nm(shape, dtype = nil, stype = :dense)
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
when
|
50
|
-
when
|
46
|
+
def to_nm(shape = nil, dtype = nil, stype = :dense)
|
47
|
+
elements = self
|
48
|
+
|
49
|
+
guess_dtype = ->(type) {
|
50
|
+
case type
|
51
|
+
when Fixnum then :int64
|
52
|
+
when Float then :float64
|
53
|
+
when Rational then :rational128
|
54
|
+
when Complex then :complex128
|
55
|
+
end
|
56
|
+
}
|
57
|
+
|
58
|
+
guess_shape = lambda { |shapey; shape|
|
59
|
+
# Get the size of the current dimension
|
60
|
+
shape = [shapey.size]
|
61
|
+
shape << shapey.map {|s|
|
62
|
+
if s.respond_to?(:size) && s.respond_to?(:map)
|
63
|
+
guess_shape.call(s)
|
64
|
+
else
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
}
|
68
|
+
if shape.last.any? {|s| (s != shape.last.first) || s.nil?}
|
69
|
+
shape.pop
|
70
|
+
end
|
71
|
+
if (shape.first != shape.last) && shape.last.all? {|s| s == shape.last.first}
|
72
|
+
shape[-1] = shape.last.first
|
51
73
|
end
|
74
|
+
shape.flatten
|
75
|
+
}
|
52
76
|
|
53
|
-
|
77
|
+
unless shape
|
78
|
+
shape = guess_shape.call(self)
|
79
|
+
elements.flatten!(shape.size - 1)
|
80
|
+
if elements.flatten != elements
|
81
|
+
dtype = :object
|
82
|
+
else
|
83
|
+
dtype ||= guess_dtype[elements[0]]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
dtype ||= guess_dtype[self[0]]
|
88
|
+
|
89
|
+
matrix = NMatrix.new(:dense, shape, elements, dtype)
|
54
90
|
|
55
91
|
if stype != :dense then matrix.cast(stype, dtype) else matrix end
|
56
92
|
end
|
@@ -64,7 +100,7 @@ class Object #:nodoc:
|
|
64
100
|
end
|
65
101
|
|
66
102
|
|
67
|
-
module Math
|
103
|
+
module Math #:nodoc:
|
68
104
|
class << self
|
69
105
|
NMatrix::NMMath::METHODS_ARITY_2.each do |meth|
|
70
106
|
define_method "nm_#{meth}" do |arg0, arg1|
|
@@ -82,3 +118,23 @@ module Math
|
|
82
118
|
end
|
83
119
|
end
|
84
120
|
|
121
|
+
class String
|
122
|
+
def underscore
|
123
|
+
self.gsub(/::/, '/').
|
124
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
125
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
126
|
+
tr("-", "_").
|
127
|
+
downcase
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Since `autoload` will most likely be deprecated (due to multi-threading concerns),
|
132
|
+
# we'll use `const_missing`. See: https://www.ruby-forum.com/topic/3036681 for more info.
|
133
|
+
module AutoloadPatch #:nodoc
|
134
|
+
def const_missing(name)
|
135
|
+
file = name.to_s.underscore
|
136
|
+
require "nmatrix/io/#{file}"
|
137
|
+
klass = const_get(name)
|
138
|
+
return klass if klass
|
139
|
+
end
|
140
|
+
end
|