pnmatrix 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. data/ext/nmatrix/binary_format.txt +53 -0
  3. data/ext/nmatrix/data/complex.h +388 -0
  4. data/ext/nmatrix/data/data.cpp +274 -0
  5. data/ext/nmatrix/data/data.h +651 -0
  6. data/ext/nmatrix/data/meta.h +64 -0
  7. data/ext/nmatrix/data/ruby_object.h +386 -0
  8. data/ext/nmatrix/extconf.rb +70 -0
  9. data/ext/nmatrix/math/asum.h +99 -0
  10. data/ext/nmatrix/math/cblas_enums.h +36 -0
  11. data/ext/nmatrix/math/cblas_templates_core.h +507 -0
  12. data/ext/nmatrix/math/gemm.h +241 -0
  13. data/ext/nmatrix/math/gemv.h +178 -0
  14. data/ext/nmatrix/math/getrf.h +255 -0
  15. data/ext/nmatrix/math/getrs.h +121 -0
  16. data/ext/nmatrix/math/imax.h +82 -0
  17. data/ext/nmatrix/math/laswp.h +165 -0
  18. data/ext/nmatrix/math/long_dtype.h +62 -0
  19. data/ext/nmatrix/math/magnitude.h +54 -0
  20. data/ext/nmatrix/math/math.h +751 -0
  21. data/ext/nmatrix/math/nrm2.h +165 -0
  22. data/ext/nmatrix/math/rot.h +117 -0
  23. data/ext/nmatrix/math/rotg.h +106 -0
  24. data/ext/nmatrix/math/scal.h +71 -0
  25. data/ext/nmatrix/math/trsm.h +336 -0
  26. data/ext/nmatrix/math/util.h +162 -0
  27. data/ext/nmatrix/math.cpp +1368 -0
  28. data/ext/nmatrix/nm_memory.h +60 -0
  29. data/ext/nmatrix/nmatrix.cpp +285 -0
  30. data/ext/nmatrix/nmatrix.h +476 -0
  31. data/ext/nmatrix/ruby_constants.cpp +151 -0
  32. data/ext/nmatrix/ruby_constants.h +106 -0
  33. data/ext/nmatrix/ruby_nmatrix.c +3130 -0
  34. data/ext/nmatrix/storage/common.cpp +77 -0
  35. data/ext/nmatrix/storage/common.h +183 -0
  36. data/ext/nmatrix/storage/dense/dense.cpp +1096 -0
  37. data/ext/nmatrix/storage/dense/dense.h +129 -0
  38. data/ext/nmatrix/storage/list/list.cpp +1628 -0
  39. data/ext/nmatrix/storage/list/list.h +138 -0
  40. data/ext/nmatrix/storage/storage.cpp +730 -0
  41. data/ext/nmatrix/storage/storage.h +99 -0
  42. data/ext/nmatrix/storage/yale/class.h +1139 -0
  43. data/ext/nmatrix/storage/yale/iterators/base.h +143 -0
  44. data/ext/nmatrix/storage/yale/iterators/iterator.h +131 -0
  45. data/ext/nmatrix/storage/yale/iterators/row.h +450 -0
  46. data/ext/nmatrix/storage/yale/iterators/row_stored.h +140 -0
  47. data/ext/nmatrix/storage/yale/iterators/row_stored_nd.h +169 -0
  48. data/ext/nmatrix/storage/yale/iterators/stored_diagonal.h +124 -0
  49. data/ext/nmatrix/storage/yale/math/transpose.h +110 -0
  50. data/ext/nmatrix/storage/yale/yale.cpp +2074 -0
  51. data/ext/nmatrix/storage/yale/yale.h +203 -0
  52. data/ext/nmatrix/types.h +55 -0
  53. data/ext/nmatrix/util/io.cpp +279 -0
  54. data/ext/nmatrix/util/io.h +115 -0
  55. data/ext/nmatrix/util/sl_list.cpp +627 -0
  56. data/ext/nmatrix/util/sl_list.h +144 -0
  57. data/ext/nmatrix/util/util.h +78 -0
  58. data/lib/nmatrix/blas.rb +378 -0
  59. data/lib/nmatrix/cruby/math.rb +744 -0
  60. data/lib/nmatrix/enumerate.rb +253 -0
  61. data/lib/nmatrix/homogeneous.rb +241 -0
  62. data/lib/nmatrix/io/fortran_format.rb +138 -0
  63. data/lib/nmatrix/io/harwell_boeing.rb +221 -0
  64. data/lib/nmatrix/io/market.rb +263 -0
  65. data/lib/nmatrix/io/point_cloud.rb +189 -0
  66. data/lib/nmatrix/jruby/decomposition.rb +24 -0
  67. data/lib/nmatrix/jruby/enumerable.rb +13 -0
  68. data/lib/nmatrix/jruby/error.rb +4 -0
  69. data/lib/nmatrix/jruby/math.rb +501 -0
  70. data/lib/nmatrix/jruby/nmatrix_java.rb +840 -0
  71. data/lib/nmatrix/jruby/operators.rb +283 -0
  72. data/lib/nmatrix/jruby/slice.rb +264 -0
  73. data/lib/nmatrix/lapack_core.rb +181 -0
  74. data/lib/nmatrix/lapack_plugin.rb +44 -0
  75. data/lib/nmatrix/math.rb +953 -0
  76. data/lib/nmatrix/mkmf.rb +100 -0
  77. data/lib/nmatrix/monkeys.rb +137 -0
  78. data/lib/nmatrix/nmatrix.rb +1172 -0
  79. data/lib/nmatrix/rspec.rb +75 -0
  80. data/lib/nmatrix/shortcuts.rb +1163 -0
  81. data/lib/nmatrix/version.rb +39 -0
  82. data/lib/nmatrix/yale_functions.rb +118 -0
  83. data/lib/nmatrix.rb +28 -0
  84. data/spec/00_nmatrix_spec.rb +892 -0
  85. data/spec/01_enum_spec.rb +196 -0
  86. data/spec/02_slice_spec.rb +407 -0
  87. data/spec/03_nmatrix_monkeys_spec.rb +80 -0
  88. data/spec/2x2_dense_double.mat +0 -0
  89. data/spec/4x4_sparse.mat +0 -0
  90. data/spec/4x5_dense.mat +0 -0
  91. data/spec/blas_spec.rb +215 -0
  92. data/spec/elementwise_spec.rb +311 -0
  93. data/spec/homogeneous_spec.rb +100 -0
  94. data/spec/io/fortran_format_spec.rb +88 -0
  95. data/spec/io/harwell_boeing_spec.rb +98 -0
  96. data/spec/io/test.rua +9 -0
  97. data/spec/io_spec.rb +159 -0
  98. data/spec/lapack_core_spec.rb +482 -0
  99. data/spec/leakcheck.rb +16 -0
  100. data/spec/math_spec.rb +1363 -0
  101. data/spec/nmatrix_yale_resize_test_associations.yaml +2802 -0
  102. data/spec/nmatrix_yale_spec.rb +286 -0
  103. data/spec/rspec_monkeys.rb +56 -0
  104. data/spec/rspec_spec.rb +35 -0
  105. data/spec/shortcuts_spec.rb +474 -0
  106. data/spec/slice_set_spec.rb +162 -0
  107. data/spec/spec_helper.rb +172 -0
  108. data/spec/stat_spec.rb +214 -0
  109. data/spec/test.pcd +20 -0
  110. data/spec/utm5940.mtx +83844 -0
  111. metadata +295 -0
@@ -0,0 +1,221 @@
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 - 2016, Ruby Science Foundation
13
+ # NMatrix is Copyright (c) 2012 - 2016, 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
+ # == io/matlab/harwell_boeing.rb
25
+ #
26
+ # Harwell Boeing file reader (and eventually writer too).
27
+ # => Supports only assembled, non-symmetric, real matrices
28
+ # => Data types supported are exponential, floating point and integer
29
+ # => Returned NMatrix is of type :float64
30
+ #++
31
+
32
+ require_relative './fortran_format.rb'
33
+
34
+ class NMatrix
35
+ module IO
36
+ module HarwellBoeing
37
+
38
+ class << self
39
+ # Loads the contents of a valid Harwell Boeing format file and
40
+ # returns an NMatrix object with the values of the file and optionally
41
+ # only the header info.
42
+ #
43
+ # Supports only assembled, non-symmetric, real matrices. File name must
44
+ # have matrix type as extension.
45
+ #
46
+ # Example - test_file.rua
47
+ #
48
+ # == Arguments
49
+ #
50
+ # * +file_path+ - Path of the Harwell Boeing file to load.
51
+ # * +opts+ - Options for specifying whether you want
52
+ # the values and header or only the header.
53
+ #
54
+ # == Options
55
+ #
56
+ # * +:header+ - If specified as *true*, will return only the header of
57
+ # the HB file.Will return the NMatrix object and
58
+ # header as an array if left blank.
59
+ #
60
+ # == Usage
61
+ #
62
+ # mat, head = NMatrix::IO::HarwellBoeing.load("test_file.rua")
63
+ #
64
+ # head = NMatrix::IO::HarwellBoeing.load("test_file.rua", {header: true})
65
+ #
66
+ # == Alternate Usage
67
+ #
68
+ # You can specify the file using NMatrix::IO::Reader.new("path/to/file")
69
+ # and then call *header* or *values* on the resulting object.
70
+ def load file_path, opts={}
71
+ hb_obj = NMatrix::IO::HarwellBoeing::Reader.new(file_path)
72
+
73
+ return hb_obj.header if opts[:header]
74
+
75
+ [hb_obj.values, hb_obj.header]
76
+ end
77
+ end
78
+
79
+ class Reader
80
+ def initialize file_name
81
+ raise(IOError, "Unsupported file format. Specify file as \
82
+ file_name.rua.") if !file_name.match(/.*\.[rR][uU][aA]/)
83
+
84
+ @file_name = file_name
85
+ @header = {}
86
+ @body = nil
87
+ end
88
+
89
+ def header
90
+ return @header if !@header.empty?
91
+ @file = File.open @file_name, "r"
92
+
93
+ line = @file.gets
94
+
95
+ @header[:title] = line[0...72].strip
96
+ @header[:key] = line[72...80].strip
97
+
98
+ line = @file.gets
99
+
100
+ @header[:totcrd] = line[0...14] .strip.to_i
101
+ @header[:ptrcrd] = line[14...28].strip.to_i
102
+ @header[:indcrd] = line[28...42].strip.to_i
103
+ @header[:valcrd] = line[42...56].strip.to_i
104
+ @header[:rhscrd] = line[56...70].strip.to_i
105
+
106
+ raise(IOError, "Right hand sides not supported.") \
107
+ if @header[:rhscrd] > 0
108
+
109
+ line = @file.gets
110
+
111
+ @header[:mxtype] = line[0...3]
112
+
113
+ raise(IOError, "Currently supports only real, assembled, unsymmetric \
114
+ matrices.") if !@header[:mxtype].match(/RUA/)
115
+
116
+ @header[:nrow] = line[13...28].strip.to_i
117
+ @header[:ncol] = line[28...42].strip.to_i
118
+ @header[:nnzero] = line[42...56].strip.to_i
119
+ @header[:neltvl] = line[56...70].strip.to_i
120
+
121
+ line = @file.gets
122
+
123
+ fortran_reader = NMatrix::IO::FortranFormat::Reader
124
+
125
+ @header[:ptrfmt] = fortran_reader.new(line[0...16].strip) .parse
126
+ @header[:indfmt] = fortran_reader.new(line[16...32].strip).parse
127
+ @header[:valfmt] = fortran_reader.new(line[32...52].strip).parse
128
+ @header[:rhsfmt] = fortran_reader.new(line[52...72].strip).parse
129
+
130
+ @header
131
+ end
132
+
133
+ def values
134
+ @header = header if @header.empty?
135
+ @file.lineno = 5 if @file.lineno != 5
136
+ @matrix = NMatrix.new([ @header[:nrow], @header[:ncol] ],
137
+ 0, dtype: :float64)
138
+
139
+ read_column_pointers
140
+ read_row_indices
141
+ read_values
142
+
143
+ @file.close
144
+
145
+ assemble_matrix
146
+
147
+ @matrix
148
+ end
149
+
150
+ private
151
+
152
+ def read_column_pointers
153
+ @col_ptrs = []
154
+ pointer_lines = @header[:ptrcrd]
155
+ pointers_per_line = @header[:ptrfmt][:repeat]
156
+ pointer_width = @header[:ptrfmt][:field_width]
157
+
158
+ @col_ptrs = read_numbers :to_i, pointer_lines, pointers_per_line,
159
+ pointer_width
160
+
161
+ @col_ptrs.map! {|c| c -= 1}
162
+ end
163
+
164
+ def read_row_indices
165
+ @row_indices = []
166
+ row_lines = @header[:indcrd]
167
+ indices_per_line = @header[:indfmt][:repeat]
168
+ row_width = @header[:indfmt][:field_width]
169
+
170
+ @row_indices = read_numbers :to_i, row_lines, indices_per_line,
171
+ row_width
172
+
173
+ @row_indices.map! {|r| r -= 1}
174
+ end
175
+
176
+ def read_values
177
+ @vals = []
178
+ value_lines = @header[:valcrd]
179
+ values_per_line = @header[:valfmt][:repeat]
180
+ value_width = @header[:valfmt][:field_width]
181
+
182
+ @vals = read_numbers :to_f, value_lines, values_per_line,
183
+ value_width
184
+ end
185
+
186
+ def read_numbers to_dtype, num_of_lines, numbers_per_line, number_width
187
+ data = []
188
+
189
+ num_of_lines.times do
190
+ line = @file.gets
191
+ index = 0
192
+
193
+ numbers_per_line.times do
194
+ delimiter = index + number_width
195
+
196
+ data << line[index...delimiter].strip.send(to_dtype)
197
+
198
+ break if line.length <= delimiter
199
+ index += number_width
200
+ end
201
+ end
202
+
203
+ data
204
+ end
205
+
206
+ def assemble_matrix
207
+ col = 0
208
+ @col_ptrs[0..-2].each_index do |index|
209
+ @col_ptrs[index].upto(@col_ptrs[index+1] - 1) do |row_ptr|
210
+ row = @row_indices[row_ptr]
211
+ @matrix[row, col] = @vals[row_ptr]
212
+ end
213
+
214
+ col += 1
215
+ end
216
+ end
217
+ end
218
+
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,263 @@
1
+ # = NMatrix
2
+ #
3
+ # A linear algebra library for scientific computation in Ruby.
4
+ # NMatrix is part of SciRuby.
5
+ #
6
+ # NMatrix was originally inspired by and derived from NArray, by
7
+ # Masahiro Tanaka: http://narray.rubyforge.org
8
+ #
9
+ # == Copyright Information
10
+ #
11
+ # SciRuby is Copyright (c) 2010 - 2016, Ruby Science Foundation
12
+ # NMatrix is Copyright (c) 2012 - 2016, John Woods and the Ruby Science Foundation
13
+ #
14
+ # Please see LICENSE.txt for additional copyright notices.
15
+ #
16
+ # == Contributing
17
+ #
18
+ # By contributing source code to SciRuby, you agree to be bound by
19
+ # our Contributor Agreement:
20
+ #
21
+ # * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
22
+ #
23
+ # == io/market.rb
24
+ #
25
+ # MatrixMarket reader and writer.
26
+ #
27
+ #++
28
+
29
+ # Matrix Market is a repository of test data for use in studies of algorithms
30
+ # for numerical linear algebra. There are 3 file formats used:
31
+ #
32
+ # - Matrix Market Exchange Format.
33
+ # - Harwell-Boeing Exchange Format.
34
+ # - Coordinate Text File Format. (to be phased out)
35
+ #
36
+ # This module can load and save the first format. We might support
37
+ # Harwell-Boeing in the future.
38
+ #
39
+ # The MatrixMarket format is documented in:
40
+ # * http://math.nist.gov/MatrixMarket/formats.html
41
+ module NMatrix::IO::Market
42
+ CONVERTER_AND_DTYPE = {
43
+ :real => [:to_f, :float64],
44
+ :complex => [:to_c, :complex128],
45
+ :integer => [:to_i, :int64],
46
+ :pattern => [:to_i, :byte]
47
+ } #:nodoc:
48
+
49
+ ENTRY_TYPE = {
50
+ :byte => :integer, :int8 => :integer, :int16 => :integer,
51
+ :int32 => :integer, :int64 => :integer,:float32 => :real,
52
+ :float64 => :real, :complex64 => :complex, :complex128 => :complex
53
+ } #:nodoc:
54
+
55
+ class << self
56
+
57
+ # call-seq:
58
+ # load(filename) -> NMatrix
59
+ #
60
+ # Load a MatrixMarket file. Requires a +filename+ as an argument.
61
+ #
62
+ # * *Arguments* :
63
+ # - +filename+ -> String with the filename to be saved.
64
+ # * *Raises* :
65
+ # - +IOError+ -> expected type code line beginning with '%%MatrixMarket matrix'
66
+ def load(filename)
67
+
68
+ f = File.new(filename, "r")
69
+
70
+ header = f.gets
71
+ header.chomp!
72
+ raise(IOError, "expected type code line beginning with '%%MatrixMarket matrix'") \
73
+ if header !~ /^\%\%MatrixMarket\ matrix/
74
+
75
+ header = header.split
76
+
77
+ entry_type = header[3].downcase.to_sym
78
+ symmetry = header[4].downcase.to_sym
79
+ converter, default_dtype = CONVERTER_AND_DTYPE[entry_type]
80
+
81
+ if header[2] == 'coordinate'
82
+ load_coordinate f, converter, default_dtype, entry_type, symmetry
83
+ else
84
+ load_array f, converter, default_dtype, entry_type, symmetry
85
+ end
86
+ end
87
+
88
+ # call-seq:
89
+ # save(matrix, filename, options = {}) -> true
90
+ #
91
+ # Can optionally set :symmetry to :general, :symmetric, :hermitian; and can
92
+ # set :pattern => true if you're writing a sparse matrix and don't want
93
+ # values stored.
94
+ #
95
+ # * *Arguments* :
96
+ # - +matrix+ -> NMatrix with the data to be saved.
97
+ # - +filename+ -> String with the filename to be saved.
98
+ # * *Raises* :
99
+ # - +DataTypeError+ -> MatrixMarket does not support Ruby objects.
100
+ # - +ArgumentError+ -> Expected two-dimensional NMatrix.
101
+ def save(matrix, filename, options = {})
102
+ options = {:pattern => false,
103
+ :symmetry => :general}.merge(options)
104
+
105
+ mode = matrix.stype == :dense ? :array : :coordinate
106
+ if [:object].include?(matrix.dtype)
107
+ raise(DataTypeError, "MatrixMarket does not support Ruby objects")
108
+ end
109
+ entry_type = options[:pattern] ? :pattern : ENTRY_TYPE[matrix.dtype]
110
+
111
+ raise(ArgumentError, "expected two-dimensional NMatrix") \
112
+ if matrix.dim != 2
113
+
114
+ f = File.new(filename, 'w')
115
+
116
+ f.puts "%%MatrixMarket matrix #{mode} #{entry_type} #{options[:symmetry]}"
117
+
118
+ if matrix.stype == :dense
119
+ save_array matrix, f, options[:symmetry]
120
+ elsif [:list,:yale].include?(matrix.stype)
121
+ save_coordinate matrix, f, options[:symmetry], options[:pattern]
122
+ end
123
+
124
+ f.close
125
+
126
+ true
127
+ end
128
+
129
+
130
+ protected
131
+
132
+ def save_coordinate matrix, file, symmetry, pattern
133
+ # Convert to a hash in order to store
134
+ rows = matrix.to_h
135
+
136
+ # Count non-zeros
137
+ count = 0
138
+ rows.each_pair do |i, columns|
139
+ columns.each_pair do |j, val|
140
+ next if symmetry != :general && j > i
141
+ count += 1
142
+ end
143
+ end
144
+
145
+ # Print dimensions and non-zeros
146
+ file.puts "#{matrix.shape[0]}\t#{matrix.shape[1]}\t#{count}"
147
+
148
+ # Print coordinates
149
+ rows.each_pair do |i, columns|
150
+ columns.each_pair do |j, val|
151
+ next if symmetry != :general && j > i
152
+ file.puts(pattern ? "\t#{i+1}\t#{j+1}" : "\t#{i+1}\t#{j+1}\t#{val}")
153
+ end
154
+ end
155
+
156
+ file
157
+ end
158
+
159
+
160
+ def save_array matrix, file, symmetry
161
+ file.puts [matrix.shape[0], matrix.shape[1]].join("\t")
162
+
163
+ if symmetry == :general
164
+ (0...matrix.shape[1]).each do |j|
165
+ (0...matrix.shape[0]).each do |i|
166
+ file.puts matrix[i,j]
167
+ end
168
+ end
169
+ else # :symmetric, :'skew-symmetric', :hermitian
170
+ (0...matrix.shape[1]).each do |j|
171
+ (j...matrix.shape[0]).each do |i|
172
+ file.puts matrix[i,j]
173
+ end
174
+ end
175
+ end
176
+
177
+ file
178
+ end
179
+
180
+
181
+ def load_array file, converter, dtype, entry_type, symmetry
182
+ mat = nil
183
+
184
+ line = file.gets
185
+ line.chomp!
186
+ line.lstrip!
187
+
188
+ fields = line.split
189
+
190
+ mat = NMatrix.new :dense, [fields[0].to_i, fields[1].to_i], dtype
191
+
192
+ (0...mat.shape[1]).each do |j|
193
+ (0...mat.shape[0]).each do |i|
194
+ datum = file.gets.chomp.send(converter)
195
+ mat[i,j] = datum
196
+
197
+ unless i == j || symmetry == :general
198
+ if symmetry == :symmetric
199
+ mat[j,i] = datum
200
+ elsif symmetry == :hermitian
201
+ mat[j,i] = Complex.new(datum.real, -datum.imag)
202
+ elsif symmetry == :'skew-symmetric'
203
+ mat[j,i] = -datum
204
+ end
205
+ end
206
+ end
207
+ end
208
+
209
+ file.close
210
+
211
+ mat
212
+ end
213
+
214
+
215
+ # Creates a :list NMatrix from a coordinate-list MatrixMarket file.
216
+ def load_coordinate file, converter, dtype, entry_type, symmetry
217
+
218
+ mat = nil
219
+
220
+ # Read until we get the dimensions and nonzeros
221
+ while line = file.gets
222
+ line.chomp!
223
+ line.lstrip!
224
+ line, comment = line.split('%', 2) # ignore comments
225
+ if line.size > 4
226
+ shape0, shape1 = line.split
227
+ mat = NMatrix.new(:list, [shape0.to_i, shape1.to_i], 0, dtype)
228
+ break
229
+ end
230
+ end
231
+
232
+ # Now read the coordinates
233
+ while line = file.gets
234
+ line.chomp!
235
+ line.lstrip!
236
+ line, comment = line.split('%', 2) # ignore comments
237
+
238
+ next unless line.size >= 5 # ignore empty lines
239
+
240
+ fields = line.split
241
+
242
+ i = fields[0].to_i - 1
243
+ j = fields[1].to_i - 1
244
+ datum = entry_type == :pattern ? 1 : fields[2].send(converter)
245
+
246
+ mat[i, j] = datum # add to the matrix
247
+ unless i == j || symmetry == :general
248
+ if symmetry == :symmetric
249
+ mat[j, i] = datum
250
+ elsif symmetry == :'skew-symmetric'
251
+ mat[j, i] = -datum
252
+ elsif symmetry == :hermitian
253
+ mat[j, i] = Complex.new(datum.real, -datum.imag)
254
+ end
255
+ end
256
+ end
257
+
258
+ file.close
259
+
260
+ mat
261
+ end
262
+ end
263
+ end
@@ -0,0 +1,189 @@
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 - 2016, Ruby Science Foundation
13
+ # NMatrix is Copyright (c) 2012 - 2016, 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
+ # == io/point_cloud.rb
25
+ #
26
+ # Point Cloud Library (PCL) PCD file IO functions.
27
+ #
28
+ #++
29
+
30
+ # Reader for Point Cloud Data (PCD) file format.
31
+ #
32
+ # The documentation of this format can be found in:
33
+ #
34
+ # http://pointclouds.org/documentation/tutorials/pcd_file_format.php
35
+ #
36
+ # Note that this implementation does not take the width or height parameters
37
+ # into account.
38
+ module NMatrix::IO::PointCloud
39
+
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:
58
+ ENTRIES = [:version, :fields, :size, :type,
59
+ :count, :width, :height, :viewpoint, :points, :data]
60
+ ASSIGNS = [:version=, :fields=, :size=, :type=,
61
+ :count=, :width=, :height=, :viewpoint=, :points=, :data=]
62
+ CONVERT = [:to_s, :downcase_to_sym, :to_i, :downcase_to_sym,
63
+ :to_i, :to_i, :to_i, :to_f, :to_i, :downcase_to_sym]
64
+
65
+ DTYPE_CONVERT = {:byte => :to_i, :int8 => :to_i, :int16 => :to_i,
66
+ :int32 => :to_i, :float32 => :to_f, :float64 => :to_f}
67
+
68
+ # For UINT, just add 1 to the index.
69
+ INT_DTYPE_BY_SIZE = {1 => :int8, 2 => :int16, 4 => :int32,
70
+ 8 => :int64, 16 => :int64}
71
+ FLOAT_DTYPE_BY_SIZE = {1 => :float32, 2 => :float32, 4 => :float32,
72
+ 8 => :float64,16 => :float64}
73
+
74
+ class << self
75
+
76
+ # Given a type and a number of bytes, figure out an appropriate dtype
77
+ def dtype_by_type_and_size t, s
78
+ if t == :f
79
+ FLOAT_DTYPE_BY_SIZE[s]
80
+ elsif t == :u
81
+ return :byte if s == 1
82
+ INT_DTYPE_BY_SIZE[s*2]
83
+ else
84
+ INT_DTYPE_BY_SIZE[s]
85
+ end
86
+ end
87
+ end
88
+
89
+ # call-seq:
90
+ # PointCloudReader::MetaReader.new(filename) -> MetaReader
91
+ #
92
+ # * *Arguments* :
93
+ # - +filename+ -> String giving the name of the file to be loaded.
94
+ # * *Raises* :
95
+ # - +NotImplementedError+ -> only ASCII supported currently
96
+ # - +IOError+ -> premature end of file
97
+ #
98
+ # Open a file and read the metadata at the top; then read the PCD into an
99
+ # NMatrix.
100
+ #
101
+ # In addition to the fields in the PCD file, there will be at least one
102
+ # additional attribute, :matrix, storing the data.
103
+ def initialize filename
104
+ f = File.new(filename, "r")
105
+
106
+ ENTRIES.each.with_index do |entry,i|
107
+ read_entry(f, entry, ASSIGNS[i], CONVERT[i])
108
+ end
109
+
110
+ raise(NotImplementedError, "only ASCII supported currently") \
111
+ unless self.data.first == :ascii
112
+
113
+ @matrix = NMatrix.new(self.shape, dtype: self.dtype)
114
+
115
+ # Do we want to use to_i or to_f?
116
+ convert = DTYPE_CONVERT[self.dtype]
117
+
118
+ i = 0
119
+ while line = f.gets
120
+ @matrix[i,:*] = line.chomp.split.map { |f| f.send(convert) }
121
+ i += 1
122
+ end
123
+
124
+ raise(IOError, "premature end of file") if i < self.points[0]
125
+
126
+ end
127
+
128
+ attr_accessor *ENTRIES
129
+ attr_reader :matrix
130
+
131
+ protected
132
+ # Read the current entry of the header.
133
+ def read_entry f, entry, assign=nil, convert=nil
134
+ assign ||= (entry.to_s + "=").to_sym
135
+
136
+ while line = f.gets
137
+ next if line =~ /^\s*#/ # ignore comment lines
138
+ line = line.chomp.split(/\s*#/)[0] # ignore the comments after any data
139
+
140
+ # Split, remove the entry name, and convert to the correct type.
141
+ self.send(assign,
142
+ line.split.tap { |t| t.shift }.map do |f|
143
+ if convert.nil?
144
+ f
145
+ elsif convert == :downcase_to_sym
146
+ f.downcase.to_sym
147
+ else
148
+ f.send(convert)
149
+ end
150
+ end)
151
+
152
+ # We don't really want to loop.
153
+ break
154
+ end
155
+
156
+ self.send(entry)
157
+ end
158
+
159
+
160
+ # Determine the dtype for a matrix based on the types and
161
+ # sizes given in the PCD.
162
+ # Call this only after read_entry has been called.
163
+ def dtype
164
+ @dtype ||= begin
165
+ dtypes = self.type.map.with_index do |t,k|
166
+ MetaReader.dtype_by_type_and_size(t, size[k])
167
+ end.sort.uniq
168
+
169
+ # This could probably save one comparison at most, but we assume that
170
+ # worst case isn't going to happen very often.
171
+ while dtypes.size > 1
172
+ d = NMatrix.upcast(dtypes[0], dtypes[1])
173
+ dtypes.shift
174
+ dtypes[0] = d
175
+ end
176
+
177
+ dtypes[0]
178
+ end
179
+ end
180
+
181
+ # Determine the shape of the matrix.
182
+ def shape
183
+ @shape ||= [
184
+ self.points[0],
185
+ self.fields.size
186
+ ]
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,24 @@
1
+ class NMatrix
2
+
3
+ # discussion in https://github.com/SciRuby/nmatrix/issues/374
4
+
5
+ def matrix_solve rhs
6
+ if rhs.shape[1] > 1
7
+ nmatrix = NMatrix.new :copy
8
+ nmatrix.shape = rhs.shape
9
+ res = []
10
+ #Solve a matrix and store the vectors in a matrix
11
+ (0...rhs.shape[1]).each do |i|
12
+ res << self.solve(rhs.col(i)).s.toArray.to_a
13
+ end
14
+ #res is in col major format
15
+ result = ArrayGenerator.getArrayColMajorDouble res.to_java :double, rhs.shape[0], rhs.shape[1]
16
+ nmatrix.s = ArrayRealVector.new result
17
+
18
+ return nmatrix
19
+ else
20
+ return self.solve rhs
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,13 @@
1
+ # Source: https://github.com/marcandre/backports/blob/master/lib/backports/rails/enumerable.rb
2
+ module Enumerable
3
+ # Standard in rails... See official documentation[http://api.rubyonrails.org/classes/Enumerable.html]
4
+ # Modified from rails 2.3 to not rely on size
5
+ def sum(identity = 0, &block)
6
+ if block_given?
7
+ map(&block).sum(identity)
8
+ else
9
+ inject { |sum, element| sum + element } || identity
10
+ end
11
+ end unless method_defined? :sum
12
+
13
+ end