nmatrix 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/Gemfile +1 -1
  2. data/History.txt +31 -3
  3. data/Manifest.txt +5 -0
  4. data/README.rdoc +29 -27
  5. data/ext/nmatrix/binary_format.txt +53 -0
  6. data/ext/nmatrix/data/data.cpp +18 -18
  7. data/ext/nmatrix/data/data.h +38 -7
  8. data/ext/nmatrix/data/rational.h +13 -0
  9. data/ext/nmatrix/data/ruby_object.h +10 -0
  10. data/ext/nmatrix/extconf.rb +2 -0
  11. data/ext/nmatrix/nmatrix.cpp +655 -103
  12. data/ext/nmatrix/nmatrix.h +26 -14
  13. data/ext/nmatrix/ruby_constants.cpp +4 -0
  14. data/ext/nmatrix/ruby_constants.h +2 -0
  15. data/ext/nmatrix/storage/dense.cpp +99 -41
  16. data/ext/nmatrix/storage/dense.h +3 -3
  17. data/ext/nmatrix/storage/list.cpp +36 -14
  18. data/ext/nmatrix/storage/list.h +4 -4
  19. data/ext/nmatrix/storage/storage.cpp +19 -19
  20. data/ext/nmatrix/storage/storage.h +11 -11
  21. data/ext/nmatrix/storage/yale.cpp +17 -20
  22. data/ext/nmatrix/storage/yale.h +13 -11
  23. data/ext/nmatrix/util/io.cpp +25 -23
  24. data/ext/nmatrix/util/io.h +5 -5
  25. data/ext/nmatrix/util/math.cpp +634 -17
  26. data/ext/nmatrix/util/math.h +958 -9
  27. data/ext/nmatrix/util/sl_list.cpp +7 -7
  28. data/ext/nmatrix/util/sl_list.h +2 -2
  29. data/lib/nmatrix.rb +9 -0
  30. data/lib/nmatrix/blas.rb +4 -4
  31. data/lib/nmatrix/io/market.rb +227 -0
  32. data/lib/nmatrix/io/mat_reader.rb +7 -7
  33. data/lib/nmatrix/lapack.rb +80 -0
  34. data/lib/nmatrix/nmatrix.rb +78 -52
  35. data/lib/nmatrix/shortcuts.rb +486 -0
  36. data/lib/nmatrix/version.rb +1 -1
  37. data/spec/2x2_dense_double.mat +0 -0
  38. data/spec/blas_spec.rb +59 -9
  39. data/spec/elementwise_spec.rb +25 -12
  40. data/spec/io_spec.rb +69 -1
  41. data/spec/lapack_spec.rb +53 -4
  42. data/spec/math_spec.rb +9 -0
  43. data/spec/nmatrix_list_spec.rb +95 -0
  44. data/spec/nmatrix_spec.rb +10 -53
  45. data/spec/nmatrix_yale_spec.rb +17 -15
  46. data/spec/shortcuts_spec.rb +154 -0
  47. metadata +22 -15
@@ -0,0 +1,486 @@
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 - 2012, Ruby Science Foundation
12
+ # NMatrix is Copyright (c) 2012, 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
+ # == shortcuts.rb
24
+ #
25
+ # These are shortcuts for NMatrix and NVector creation, contributed by Daniel
26
+ # Carrera (dcarrera@hush.com) and Carlos Agarie (carlos@onox.com.br).
27
+
28
+ class NMatrix
29
+
30
+ class << self
31
+ # zeros() or zeroes()
32
+ #
33
+ # Creates a new matrix of zeros with the dimensions supplied as
34
+ # parameters. Optional parameters include:
35
+ #
36
+ # * A storage type as the first parameter (default is :dense).
37
+ # * A dtype as the last parameter (default is :float64).
38
+ #
39
+ # Examples:
40
+ #
41
+ # zeros(2) # => 0.0 0.0
42
+ # 0.0 0.0
43
+ #
44
+ # zeros([2, 3], :int32) # => 0 0 0
45
+ # 0 0 0
46
+ #
47
+ # zeros(:list, [1, 5], :int32) # => 0 0 0 0 0
48
+ #
49
+
50
+ def zeros(*params)
51
+ dtype = params.last.is_a?(Symbol) ? params.pop : :float64
52
+ stype = params.first.is_a?(Symbol) ? params.shift : :dense
53
+ dim = params.first
54
+
55
+ NMatrix.new(stype, dim, 0, dtype)
56
+ end
57
+
58
+ alias :zeroes :zeros
59
+
60
+ # ones()
61
+ #
62
+ # Creates a :dense matrix of ones with the dimensions supplied
63
+ # as parameters. Optionaly, one can specify a dtype as the last
64
+ # parameter (default is :float64).
65
+ #
66
+ # Examples:
67
+ #
68
+ # ones([1, 3]) # => 1.0 1.0 1.0
69
+ #
70
+ # ones([2, 3], :int32) # => 1 1 1
71
+ # 1 1 1
72
+ #
73
+
74
+ def ones(*params)
75
+ dtype = params.last.is_a?(Symbol) ? params.pop : :float64
76
+ dim = params.first
77
+
78
+ NMatrix.new(dim, 1, dtype)
79
+ end
80
+
81
+ # identity() or eye()
82
+ #
83
+ # Creates an identity matrix (square matrix rank 2) of the size
84
+ # supplied as a parameter. Optional parameters include:
85
+ #
86
+ # * A storage type as the first parameter (default is :dense).
87
+ # * A dtype as the last parameter (default is :float64).
88
+ #
89
+ # Examples:
90
+ #
91
+ # eye(3) # => 1.0 0.0 0.0
92
+ # 0.0 1.0 0.0
93
+ # 0.0 0.0 1.0
94
+ #
95
+ # eye(3, :int32) # => 1 0 0
96
+ # 0 1 0
97
+ # 0 0 1
98
+ #
99
+ # eye(:yale, 2, :int32) # => 1 0
100
+ # 0 1
101
+ #
102
+
103
+ def eye(*params)
104
+ dtype = params.last.is_a?(Symbol) ? params.pop : :float64
105
+ stype = params.first.is_a?(Symbol) ? params.shift : :dense
106
+
107
+ dim = params.first
108
+
109
+ # Fill the diagonal with 1's.
110
+ m = NMatrix.zeros(stype, dim, dtype)
111
+ (0 .. (dim - 1)).each do |i|
112
+ m[i, i] = 1
113
+ end
114
+
115
+ m
116
+ end
117
+
118
+ alias :identity :eye
119
+
120
+ # random()
121
+ #
122
+ # Creates a :dense NMatrix with random numbers between 0 and 1 generated
123
+ # by Random::rand. The parameter is the dimension of the matrix.
124
+ #
125
+ # Examples:
126
+ #
127
+ # rand([2, 2]) # => 0.4859439730644226 0.1783195585012436
128
+ # 0.23193766176700592 0.4503345191478729
129
+ #
130
+
131
+ def random(*params)
132
+ dim = params.first
133
+ rng = Random.new
134
+
135
+ # Must provide the dimension as an Integer for a square matrix or as an
136
+ # array, e.g. [2, 4, 7].
137
+ unless dim.is_a?(Integer) || dim.is_a?(Array)
138
+ raise ArgumentError, "random() accepts only integers or arrays as \
139
+ dimension."
140
+ end
141
+
142
+ random_values = []
143
+
144
+ # Construct the values of the final matrix based on the dimension.
145
+ if dim.is_a?(Integer)
146
+ (dim * dim - 1).times { |i| random_values << rng.rand }
147
+ else
148
+ # Dimensions given by an array. Get the product of the array elements
149
+ # and generate this number of random values.
150
+ dim.reduce(1, :*).times { |i| random_values << rng.rand }
151
+ end
152
+
153
+ NMatrix.new(:dense, dim, random_values, :float64)
154
+ end
155
+
156
+ # seq()
157
+ #
158
+ # Creates a :dense NMatrix with a sequence of integers starting at
159
+ # zero until the matrix is filled. The parameters to the method
160
+ # are the dimensions of the matrix. Optionaly, one can specify a
161
+ # dtype as the last parameter (default is :float64).
162
+ #
163
+ # Examples:
164
+ #
165
+ # seq(2) # => 0 1
166
+ # 2 3
167
+ #
168
+ # seq([3, 3], :float32) # => 0.0 1.0 2.0
169
+ # 3.0 4.0 5.0
170
+ # 6.0 7.0 8.0
171
+ #
172
+
173
+ def seq(*params)
174
+ dtype = params.last.is_a?(Symbol) ? params.pop : nil
175
+ dim = params.first
176
+
177
+ # Must provide the dimension as an Integer for a square matrix or as an
178
+ # 2 element array, e.g. [2,4].
179
+ unless dim.is_a?(Integer) || (dim.is_a?(Array) && dim.size < 3)
180
+ raise ArgumentError, "seq() accepts only integers or 2-element arrays \
181
+ as dimension."
182
+ end
183
+
184
+ # Construct the values of the final matrix based on the dimension.
185
+ if dim.is_a?(Integer)
186
+ values = (0 .. (dim * dim - 1)).to_a
187
+ else
188
+ # Dimensions given by a 2 element array.
189
+ values = (0 .. (dim.first * dim.last - 1)).to_a
190
+ end
191
+
192
+ # It'll produce :int32, except if a dtype is provided.
193
+ NMatrix.new(:dense, dim, values, dtype)
194
+ end
195
+
196
+ #########################################
197
+ # FUNCTIONS FOR MATLAB AND IDL REFUGEES #
198
+ #########################################
199
+
200
+ #
201
+ # These are functions that replicate existing functionality, but
202
+ # would probably be appreciated by MATLAB or IDL users.
203
+ #
204
+
205
+ # indgen() , findgen() , bindgen() , cindgen()
206
+ #
207
+ # These IDL functions are similar to seq() but less flexible.
208
+ # They produce one-dimensional vectors:
209
+ #
210
+ # indgen -- Integer vector -- seq(n, :int32)
211
+ # findgen -- Float vector -- seq(n, :float32)
212
+ # bindgen -- Byte vector -- seq(n, :byte)
213
+ # cindgen -- Complex vector -- seq(n, :complex64)
214
+ #
215
+
216
+ def indgen(n)
217
+ NMatrix.seq(n, :int32)
218
+ end
219
+
220
+ def findgen(n)
221
+ NMatrix.seq(n, :float32)
222
+ end
223
+
224
+ def bindgen(n)
225
+ NMatrix.seq(n, :byte)
226
+ end
227
+
228
+ def cindgen(n)
229
+ NMatrix.seq(n, :complex64)
230
+ end
231
+
232
+ end
233
+
234
+ #
235
+ # These shortcuts are to be called directly from a NMatrix object, i.e.:
236
+ #
237
+ # >> m = NMatrix.random(3)
238
+ # >> m.column(2)
239
+ #
240
+
241
+ # column()
242
+ #
243
+ # Returns the column specified. The second parameter defaults to
244
+ # :copy, which returns a copy of the selected column, but it can be
245
+ # specified as :reference, which will return a reference to it.
246
+ #
247
+ # Examples:
248
+ #
249
+ # m = NMatrix.new(2, [1, 4, 9, 14], :int32) # => 1 4
250
+ # 9 14
251
+ #
252
+ # m.column(1) # => 4
253
+ # 14
254
+ #
255
+
256
+ def column(column_number, get_by = :copy)
257
+ unless [:copy, :reference].include?(get_by)
258
+ raise ArgumentError, "column() 2nd parameter must be :copy or :reference"
259
+ end
260
+
261
+ if get_by == :copy
262
+ self.slice(0 ... self.shape[0], column_number)
263
+ else # by reference
264
+ self[0 ... self.shape[0], column_number]
265
+ end
266
+ end
267
+ end
268
+
269
+ class NVector < NMatrix
270
+
271
+ class << self
272
+ # zeros() or zeroes()
273
+ #
274
+ # Creates a new matrix of zeros with the dimensions supplied as
275
+ # parameters. Optional parameters include:
276
+ #
277
+ # * A storage type as the first parameter (default is :dense).
278
+ # * A dtype as the last parameter (default is :float64).
279
+ #
280
+ # Examples:
281
+ #
282
+ # zeros(2) # => 0.0 0.0
283
+ #
284
+ # zeros(3, :int32) # => 0 0 0
285
+ #
286
+
287
+ def zeros(*params)
288
+ dtype = params.last.is_a?(Symbol) ? params.pop : :float64
289
+ dim = params.first
290
+
291
+ NVector.new(dim, 0, dtype)
292
+ end
293
+
294
+ alias :zeroes :zeros
295
+
296
+ # ones()
297
+ #
298
+ # Creates a :dense matrix of ones with the dimensions supplied
299
+ # as parameters. Optionaly, one can specify a dtype as the last
300
+ # parameter (default is :float64).
301
+ #
302
+ # Examples:
303
+ #
304
+ # ones(3) # => 1.0 1.0 1.0
305
+ #
306
+ # ones(2, :int32) # => 1 1
307
+ #
308
+
309
+ def ones(*params)
310
+ dtype = params.last.is_a?(Symbol) ? params.pop : :float64
311
+ dim = params.first
312
+
313
+ NVector.new(dim, 1, dtype)
314
+ end
315
+
316
+ # random()
317
+ #
318
+ # Creates a :dense NMatrix with random numbers between 0 and 1 generated
319
+ # by Random::rand. The parameter is the dimension of the matrix.
320
+ #
321
+ # Examples:
322
+ #
323
+ # rand(2) # => 0.4859439730644226 0.1783195585012436
324
+ #
325
+
326
+ def random(*params)
327
+ rng = Random.new
328
+ dim = params.first
329
+
330
+ random_values = []
331
+ dim.times { |i| random_values << rng.rand }
332
+
333
+ NVector.new(dim, random_values, :float64)
334
+ end
335
+
336
+ # seq()
337
+ #
338
+ # Creates a :dense NMatrix with a sequence of integers starting at
339
+ # zero until the matrix is filled. The parameters to the method
340
+ # are the dimensions of the matrix. Optionaly, one can specify a
341
+ # dtype as the last parameter (default is :float64).
342
+ #
343
+ # Examples:
344
+ #
345
+ # seq(2) # => 0 1
346
+ #
347
+ # seq(3, :float32) # => 0.0 1.0 2.0
348
+ #
349
+
350
+ def seq(*params)
351
+ dtype = params.last.is_a?(Symbol) ? params.pop : nil
352
+ dim = params.first
353
+
354
+ unless dim.is_a?(Integer)
355
+ raise ArgumentError, "NVector::seq() only accepts integers as \
356
+ dimension."
357
+ end
358
+
359
+ values = (0 .. (dim - 1)).to_a
360
+
361
+ NVector.new(dim, values, dtype)
362
+ end
363
+
364
+ #########################################
365
+ # FUNCTIONS FOR MATLAB AND IDL REFUGEES #
366
+ #########################################
367
+
368
+ #
369
+ # These are functions that replicate existing functionality, but
370
+ # would probably be appreciated by MATLAB or IDL users.
371
+ #
372
+
373
+ # indgen() , findgen() , bindgen() , cindgen()
374
+ #
375
+ # These IDL functions are similar to seq() but less flexible.
376
+ # They produce one-dimensional vectors:
377
+ #
378
+ # indgen -- Integer vector -- seq(n, :int32)
379
+ # findgen -- Float vector -- seq(n, :float32)
380
+ # bindgen -- Byte vector -- seq(n, :byte)
381
+ # cindgen -- Complex vector -- seq(n, :complex64)
382
+ #
383
+
384
+ def indgen(n)
385
+ NVector.seq(n, :int32)
386
+ end
387
+
388
+ def findgen(n)
389
+ NVector.seq(n, :float32)
390
+ end
391
+
392
+ def bindgen(n)
393
+ NVector.seq(n, :byte)
394
+ end
395
+
396
+ def cindgen(n)
397
+ NVector.seq(n, :complex64)
398
+ end
399
+
400
+ # linspace()
401
+ #
402
+ # This MATLAB function somewhat resembles seq(), but it differs
403
+ # enough that is likely to be legitimately useful to non-MATLAB
404
+ # refugees. This function takes three parameter, the last one
405
+ # being an integer.
406
+ #
407
+ # linspace( a, b, n )
408
+ #
409
+ # This returns a vector with n values equally spaced from a to b,
410
+ # inclusive.
411
+ #
412
+ # Following the MATLAB implementation, if n isn't provided it's
413
+ # assumed to be 100.
414
+ #
415
+ # Ex: x = linspace(0, pi, 1000)
416
+ # y = sin(x)
417
+ #
418
+
419
+ def linspace(a, b, n = 100)
420
+ # See: http://www.mathworks.com/help/matlab/ref/linspace.html
421
+ # Formula: seq(n) * step + a
422
+
423
+ # step = ((b - a) / (n - 1))
424
+ step = (b - a) * (1.0 / (n - 1))
425
+
426
+ # dtype = :float64 is used to prevent integer coercion.
427
+ result = NVector.seq(n, :float64) * NVector.new(n, step, :float64)
428
+ result += NVector.new(n, a, :float64)
429
+ result
430
+ end
431
+ end
432
+ end
433
+
434
+ # NMatrix needs to have a succinct way to create a matrix by specifying
435
+ # the components directly. This is very usefeul for using NMatrix as an
436
+ # advanced calculator, it is useful for learning NMatrix and it is also
437
+ # useful for testing language features or developing algorithms.
438
+ #
439
+ # The N[] function provides a way to create a matrix in a way that is
440
+ # very short and very natural, simply by specifying the components in
441
+ # the traditional Ruby array syntax. Optionally, one can specify a
442
+ # dtype as the last parameter (default is :float64).
443
+ #
444
+ # a = N[ 1,2,3,4 ] => 1.0 2.0 3.0 4.0
445
+ #
446
+ # a = N[ 1,2,3,4, :int32 ] => 1 2 3 4
447
+ #
448
+ # a = N[ [1,2,3], [3,4,5] ] => 1.0 2.0 3.0
449
+ # 3.0 4.0 5.0
450
+ #
451
+ #
452
+ # SYNTAX COMPARISON:
453
+ #
454
+ # MATLAB: a = [ [1 2 3] ; [4 5 6] ] or [ 1 2 3 ; 4 5 6 ]
455
+ # IDL: a = [ [1,2,3] , [4,5,6] ]
456
+ # NumPy: a = array( [1,2,3], [4,5,6] )
457
+ #
458
+ # SciRuby: a = N[ [1,2,3], [4,5,6] ]
459
+ # Ruby array: a = [ [1,2,3], [4,5,6] ]
460
+ #
461
+
462
+ class N
463
+ class << self
464
+ def [](*params)
465
+ dtype = params.last.is_a?(Symbol) ? params.pop : nil
466
+
467
+ # First find the dimensions of the array.
468
+ i = 0
469
+ dim = []
470
+ foo = params
471
+ while foo.is_a?(Array)
472
+ dim[i] = foo.length
473
+ foo = foo[0]
474
+ i += 1
475
+ end
476
+
477
+ # Then flatten the array.
478
+ NMatrix.new(dim, params.flatten, dtype)
479
+ end
480
+ end
481
+ end
482
+
483
+ # TODO Make all the shortcuts available through modules, allowing someone
484
+ # to include them to make "MATLAB-like" scripts.
485
+ #
486
+ # There are some questions to be answered before this can be done, tho.