nmatrix 0.0.1 → 0.0.2

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.
Files changed (91) hide show
  1. data/.gitignore +27 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +3 -5
  4. data/Guardfile +6 -0
  5. data/History.txt +33 -0
  6. data/Manifest.txt +41 -38
  7. data/README.rdoc +88 -11
  8. data/Rakefile +35 -53
  9. data/ext/nmatrix/data/complex.h +372 -0
  10. data/ext/nmatrix/data/data.cpp +275 -0
  11. data/ext/nmatrix/data/data.h +707 -0
  12. data/ext/nmatrix/data/rational.h +421 -0
  13. data/ext/nmatrix/data/ruby_object.h +446 -0
  14. data/ext/nmatrix/extconf.rb +101 -51
  15. data/ext/nmatrix/new_extconf.rb +56 -0
  16. data/ext/nmatrix/nmatrix.cpp +1609 -0
  17. data/ext/nmatrix/nmatrix.h +265 -849
  18. data/ext/nmatrix/ruby_constants.cpp +134 -0
  19. data/ext/nmatrix/ruby_constants.h +103 -0
  20. data/ext/nmatrix/storage/common.cpp +70 -0
  21. data/ext/nmatrix/storage/common.h +170 -0
  22. data/ext/nmatrix/storage/dense.cpp +665 -0
  23. data/ext/nmatrix/storage/dense.h +116 -0
  24. data/ext/nmatrix/storage/list.cpp +1088 -0
  25. data/ext/nmatrix/storage/list.h +129 -0
  26. data/ext/nmatrix/storage/storage.cpp +658 -0
  27. data/ext/nmatrix/storage/storage.h +99 -0
  28. data/ext/nmatrix/storage/yale.cpp +1601 -0
  29. data/ext/nmatrix/storage/yale.h +208 -0
  30. data/ext/nmatrix/ttable_helper.rb +126 -0
  31. data/ext/nmatrix/{yale/smmp1_header.template.c → types.h} +36 -9
  32. data/ext/nmatrix/util/io.cpp +295 -0
  33. data/ext/nmatrix/util/io.h +117 -0
  34. data/ext/nmatrix/util/lapack.h +1175 -0
  35. data/ext/nmatrix/util/math.cpp +557 -0
  36. data/ext/nmatrix/util/math.h +1363 -0
  37. data/ext/nmatrix/util/sl_list.cpp +475 -0
  38. data/ext/nmatrix/util/sl_list.h +255 -0
  39. data/ext/nmatrix/util/util.h +78 -0
  40. data/lib/nmatrix/blas.rb +70 -0
  41. data/lib/nmatrix/io/mat5_reader.rb +567 -0
  42. data/lib/nmatrix/io/mat_reader.rb +162 -0
  43. data/lib/{string.rb → nmatrix/monkeys.rb} +49 -2
  44. data/lib/nmatrix/nmatrix.rb +199 -0
  45. data/lib/nmatrix/nvector.rb +103 -0
  46. data/lib/nmatrix/version.rb +27 -0
  47. data/lib/nmatrix.rb +22 -230
  48. data/nmatrix.gemspec +59 -0
  49. data/scripts/mac-brew-gcc.sh +47 -0
  50. data/spec/4x4_sparse.mat +0 -0
  51. data/spec/4x5_dense.mat +0 -0
  52. data/spec/blas_spec.rb +47 -0
  53. data/spec/elementwise_spec.rb +164 -0
  54. data/spec/io_spec.rb +60 -0
  55. data/spec/lapack_spec.rb +52 -0
  56. data/spec/math_spec.rb +96 -0
  57. data/spec/nmatrix_spec.rb +93 -89
  58. data/spec/nmatrix_yale_spec.rb +52 -36
  59. data/spec/nvector_spec.rb +1 -1
  60. data/spec/slice_spec.rb +257 -0
  61. data/spec/spec_helper.rb +51 -0
  62. data/spec/utm5940.mtx +83844 -0
  63. metadata +113 -71
  64. data/.autotest +0 -23
  65. data/.gemtest +0 -0
  66. data/ext/nmatrix/cblas.c +0 -150
  67. data/ext/nmatrix/dense/blas_header.template.c +0 -52
  68. data/ext/nmatrix/dense/elementwise.template.c +0 -107
  69. data/ext/nmatrix/dense/gemm.template.c +0 -159
  70. data/ext/nmatrix/dense/gemv.template.c +0 -130
  71. data/ext/nmatrix/dense/rationalmath.template.c +0 -68
  72. data/ext/nmatrix/dense.c +0 -307
  73. data/ext/nmatrix/depend +0 -18
  74. data/ext/nmatrix/generator/syntax_tree.rb +0 -481
  75. data/ext/nmatrix/generator.rb +0 -594
  76. data/ext/nmatrix/list.c +0 -774
  77. data/ext/nmatrix/nmatrix.c +0 -1977
  78. data/ext/nmatrix/rational.c +0 -98
  79. data/ext/nmatrix/yale/complexmath.template.c +0 -71
  80. data/ext/nmatrix/yale/elementwise.template.c +0 -46
  81. data/ext/nmatrix/yale/elementwise_op.template.c +0 -73
  82. data/ext/nmatrix/yale/numbmm.template.c +0 -94
  83. data/ext/nmatrix/yale/smmp1.template.c +0 -21
  84. data/ext/nmatrix/yale/smmp2.template.c +0 -43
  85. data/ext/nmatrix/yale/smmp2_header.template.c +0 -46
  86. data/ext/nmatrix/yale/sort_columns.template.c +0 -56
  87. data/ext/nmatrix/yale/symbmm.template.c +0 -54
  88. data/ext/nmatrix/yale/transp.template.c +0 -68
  89. data/ext/nmatrix/yale.c +0 -726
  90. data/lib/array.rb +0 -67
  91. data/spec/syntax_tree_spec.rb +0 -46
@@ -0,0 +1,1609 @@
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 - 2012, Ruby Science Foundation
13
+ // NMatrix is Copyright (c) 2012, 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
+ // == nmatrix.cpp
25
+ //
26
+ // Main C++ source file for NMatrix. Contains Init_nmatrix and most Ruby instance and
27
+ // class methods for NMatrix. Also responsible for calling Init methods on related
28
+ // modules.
29
+
30
+ /*
31
+ * Standard Includes
32
+ */
33
+
34
+ #ifdef HAVE_CLAPACK_H
35
+ extern "C" {
36
+ #include <clapack.h>
37
+ }
38
+ #endif
39
+
40
+ #include <ruby.h>
41
+ #include <algorithm> // std::min
42
+
43
+ /*
44
+ * Project Includes
45
+ */
46
+ #include "types.h"
47
+ #include "data/data.h"
48
+ #include "util/math.h"
49
+ #include "util/io.h"
50
+ #include "storage/storage.h"
51
+
52
+ #include "nmatrix.h"
53
+
54
+ #include "ruby_constants.h"
55
+
56
+ /*
57
+ * Macros
58
+ */
59
+
60
+ /*
61
+ * If no block is given, return an enumerator. This copied straight out of ruby's include/ruby/intern.h.
62
+ *
63
+ * rb_enumeratorize is located in enumerator.c.
64
+ *
65
+ * VALUE rb_enumeratorize(VALUE obj, VALUE meth, int argc, VALUE *argv) {
66
+ * return enumerator_init(enumerator_allocate(rb_cEnumerator), obj, meth, argc, argv);
67
+ * }
68
+ */
69
+ #define RETURN_ENUMERATOR(obj, argc, argv) do { \
70
+ if (!rb_block_given_p()) \
71
+ return rb_enumeratorize((obj), ID2SYM(rb_frame_this_func()), \
72
+ (argc), (argv)); \
73
+ } while (0)
74
+
75
+ /*
76
+ * Global Variables
77
+ */
78
+
79
+
80
+ extern "C" {
81
+
82
+ /*
83
+ * Forward Declarations
84
+ */
85
+
86
+ static VALUE nm_init(int argc, VALUE* argv, VALUE nm);
87
+ static VALUE nm_init_copy(VALUE copy, VALUE original);
88
+ static VALUE nm_init_transposed(VALUE self);
89
+ static VALUE nm_init_cast_copy(VALUE self, VALUE new_stype_symbol, VALUE new_dtype_symbol);
90
+ static VALUE nm_to_hash(VALUE self);
91
+ static VALUE nm_init_yale_from_old_yale(VALUE shape, VALUE dtype, VALUE ia, VALUE ja, VALUE a, VALUE from_dtype, VALUE nm);
92
+ static VALUE nm_alloc(VALUE klass);
93
+ static void nm_delete(NMATRIX* mat);
94
+ static void nm_delete_ref(NMATRIX* mat);
95
+ static VALUE nm_dtype(VALUE self);
96
+ static VALUE nm_itype(VALUE self);
97
+ static VALUE nm_stype(VALUE self);
98
+ static VALUE nm_dim(VALUE self);
99
+ static VALUE nm_shape(VALUE self);
100
+ static VALUE nm_capacity(VALUE self);
101
+ static VALUE nm_each(VALUE nmatrix);
102
+
103
+ static SLICE* get_slice(size_t dim, VALUE* c, VALUE self);
104
+ static VALUE nm_xslice(int argc, VALUE* argv, void* (*slice_func)(STORAGE*, SLICE*), void (*delete_func)(NMATRIX*), VALUE self);
105
+ static VALUE nm_mset(int argc, VALUE* argv, VALUE self);
106
+ static VALUE nm_mget(int argc, VALUE* argv, VALUE self);
107
+ static VALUE nm_mref(int argc, VALUE* argv, VALUE self);
108
+ static VALUE nm_is_ref(VALUE self);
109
+
110
+ static VALUE is_symmetric(VALUE self, bool hermitian);
111
+
112
+ /*
113
+ * Macro defines an element-wise accessor function for some operation.
114
+ *
115
+ * This is only responsible for the Ruby accessor! You still have to write the actual functions, obviously.
116
+ */
117
+ #define DEF_ELEMENTWISE_RUBY_ACCESSOR(oper, name) \
118
+ static VALUE nm_ew_##name(VALUE left_val, VALUE right_val) { \
119
+ return elementwise_op(nm::EW_##oper, left_val, right_val); \
120
+ }
121
+
122
+ /*
123
+ * Macro declares a corresponding accessor function prototype for some element-wise operation.
124
+ */
125
+ #define DECL_ELEMENTWISE_RUBY_ACCESSOR(name) static VALUE nm_ew_##name(VALUE left_val, VALUE right_val);
126
+
127
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(add)
128
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(subtract)
129
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(multiply)
130
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(divide)
131
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(eqeq)
132
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(neq)
133
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(lt)
134
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(gt)
135
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(leq)
136
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(geq)
137
+
138
+ static VALUE elementwise_op(nm::ewop_t op, VALUE left_val, VALUE right_val);
139
+
140
+ static VALUE nm_symmetric(VALUE self);
141
+ static VALUE nm_hermitian(VALUE self);
142
+
143
+ static VALUE nm_eqeq(VALUE left, VALUE right);
144
+
145
+ static VALUE matrix_multiply_scalar(NMATRIX* left, VALUE scalar);
146
+ static VALUE matrix_multiply(NMATRIX* left, NMATRIX* right);
147
+ static VALUE nm_multiply(VALUE left_v, VALUE right_v);
148
+ static VALUE nm_factorize_lu(VALUE self);
149
+ static VALUE nm_det_exact(VALUE self);
150
+ static VALUE nm_complex_conjugate_bang(VALUE self);
151
+
152
+ static dtype_t dtype_guess(VALUE v);
153
+ static dtype_t interpret_dtype(int argc, VALUE* argv, stype_t stype);
154
+ static void* interpret_initial_value(VALUE arg, dtype_t dtype);
155
+ static size_t* interpret_shape(VALUE arg, size_t* dim);
156
+ static stype_t interpret_stype(VALUE arg);
157
+
158
+ /* Singleton methods */
159
+ static VALUE nm_itype_by_shape(VALUE self, VALUE shape_arg);
160
+ static VALUE nm_upcast(VALUE self, VALUE t1, VALUE t2);
161
+
162
+
163
+ #ifdef BENCHMARK
164
+ static double get_time(void);
165
+ #endif
166
+
167
+ /*
168
+ * Functions
169
+ */
170
+
171
+ ///////////////////
172
+ // Ruby Bindings //
173
+ ///////////////////
174
+
175
+ void Init_nmatrix() {
176
+
177
+ ///////////////////////
178
+ // Class Definitions //
179
+ ///////////////////////
180
+
181
+ cNMatrix = rb_define_class("NMatrix", rb_cObject);
182
+ cNVector = rb_define_class("NVector", cNMatrix);
183
+
184
+ // Special exceptions
185
+ nm_eDataTypeError = rb_define_class("DataTypeError", rb_eStandardError);
186
+ nm_eStorageTypeError = rb_define_class("StorageTypeError", rb_eStandardError);
187
+
188
+ ///////////////////
189
+ // Class Methods //
190
+ ///////////////////
191
+
192
+ rb_define_alloc_func(cNMatrix, nm_alloc);
193
+
194
+ ///////////////////////
195
+ // Singleton Methods //
196
+ ///////////////////////
197
+
198
+ rb_define_singleton_method(cNMatrix, "upcast", (METHOD)nm_upcast, 2);
199
+ rb_define_singleton_method(cNMatrix, "itype_by_shape", (METHOD)nm_itype_by_shape, 1);
200
+
201
+ //////////////////////
202
+ // Instance Methods //
203
+ //////////////////////
204
+
205
+ rb_define_method(cNMatrix, "initialize", (METHOD)nm_init, -1);
206
+ rb_define_method(cNMatrix, "initialize_copy", (METHOD)nm_init_copy, 1);
207
+
208
+ // Technically, the following function is a copy constructor.
209
+ rb_define_method(cNMatrix, "transpose", (METHOD)nm_init_transposed, 0);
210
+
211
+ rb_define_method(cNMatrix, "dtype", (METHOD)nm_dtype, 0);
212
+ rb_define_method(cNMatrix, "itype", (METHOD)nm_itype, 0);
213
+ rb_define_method(cNMatrix, "stype", (METHOD)nm_stype, 0);
214
+ rb_define_method(cNMatrix, "cast", (METHOD)nm_init_cast_copy, 2);
215
+
216
+ rb_define_method(cNMatrix, "[]", (METHOD)nm_mref, -1);
217
+ rb_define_method(cNMatrix, "slice", (METHOD)nm_mget, -1);
218
+ rb_define_method(cNMatrix, "[]=", (METHOD)nm_mset, -1);
219
+ rb_define_method(cNMatrix, "is_ref?", (METHOD)nm_is_ref, 0);
220
+ rb_define_method(cNMatrix, "dimensions", (METHOD)nm_dim, 0);
221
+
222
+ rb_define_method(cNMatrix, "to_hash", (METHOD)nm_to_hash, 0);
223
+ rb_define_alias(cNMatrix, "to_h", "to_hash");
224
+
225
+ rb_define_method(cNMatrix, "shape", (METHOD)nm_shape, 0);
226
+ rb_define_method(cNMatrix, "det_exact", (METHOD)nm_det_exact, 0);
227
+ //rb_define_method(cNMatrix, "transpose!", (METHOD)nm_transpose_self, 0);
228
+ rb_define_method(cNMatrix, "complex_conjugate!", (METHOD)nm_complex_conjugate_bang, 0);
229
+
230
+ rb_define_method(cNMatrix, "each", (METHOD)nm_each, 0);
231
+
232
+ rb_define_method(cNMatrix, "==", (METHOD)nm_eqeq, 1);
233
+
234
+ rb_define_method(cNMatrix, "+", (METHOD)nm_ew_add, 1);
235
+ rb_define_method(cNMatrix, "-", (METHOD)nm_ew_subtract, 1);
236
+ rb_define_method(cNMatrix, "*", (METHOD)nm_ew_multiply, 1);
237
+ rb_define_method(cNMatrix, "/", (METHOD)nm_ew_divide, 1);
238
+ //rb_define_method(cNMatrix, "%", (METHOD)nm_ew_mod, 1);
239
+
240
+ rb_define_method(cNMatrix, "=~", (METHOD)nm_ew_eqeq, 1);
241
+ rb_define_method(cNMatrix, "!~", (METHOD)nm_ew_neq, 1);
242
+ rb_define_method(cNMatrix, "<=", (METHOD)nm_ew_leq, 1);
243
+ rb_define_method(cNMatrix, ">=", (METHOD)nm_ew_geq, 1);
244
+ rb_define_method(cNMatrix, "<", (METHOD)nm_ew_lt, 1);
245
+ rb_define_method(cNMatrix, ">", (METHOD)nm_ew_gt, 1);
246
+
247
+ /////////////////////////
248
+ // Matrix Math Methods //
249
+ /////////////////////////
250
+ rb_define_method(cNMatrix, "dot", (METHOD)nm_multiply, 1);
251
+ rb_define_method(cNMatrix, "factorize_lu", (METHOD)nm_factorize_lu, 0);
252
+
253
+
254
+ rb_define_method(cNMatrix, "symmetric?", (METHOD)nm_symmetric, 0);
255
+ rb_define_method(cNMatrix, "hermitian?", (METHOD)nm_hermitian, 0);
256
+
257
+ rb_define_method(cNMatrix, "capacity", (METHOD)nm_capacity, 0);
258
+
259
+ /////////////
260
+ // Aliases //
261
+ /////////////
262
+
263
+ rb_define_alias(cNMatrix, "dim", "dimensions");
264
+ rb_define_alias(cNMatrix, "equal?", "eql?");
265
+
266
+ ///////////////////////
267
+ // Symbol Generation //
268
+ ///////////////////////
269
+
270
+ nm_init_ruby_constants();
271
+
272
+ //////////////////////////
273
+ // YaleFunctions module //
274
+ //////////////////////////
275
+
276
+ nm_init_yale_functions();
277
+
278
+ /////////////////
279
+ // BLAS module //
280
+ /////////////////
281
+
282
+ nm_math_init_blas();
283
+
284
+ ///////////////
285
+ // IO module //
286
+ ///////////////
287
+ nm_init_io();
288
+ }
289
+
290
+
291
+ //////////////////
292
+ // Ruby Methods //
293
+ //////////////////
294
+
295
+ /*
296
+ * Allocator.
297
+ */
298
+ static VALUE nm_alloc(VALUE klass) {
299
+ NMATRIX* mat = ALLOC(NMATRIX);
300
+ mat->storage = NULL;
301
+ // FIXME: mark_table[mat->stype] should be passed to Data_Wrap_Struct, but can't be done without stype. Also, nm_delete depends on this.
302
+ // mat->stype = nm::NUM_STYPES;
303
+
304
+ //STYPE_MARK_TABLE(mark_table);
305
+
306
+ return Data_Wrap_Struct(klass, NULL, nm_delete, mat);
307
+ }
308
+
309
+
310
+
311
+ /*
312
+ * Find the capacity of an NMatrix. The capacity only differs from the size for
313
+ * Yale matrices, which occasionally allocate more space than they need. For
314
+ * list and dense, capacity gives the number of elements in the matrix.
315
+ */
316
+ static VALUE nm_capacity(VALUE self) {
317
+ VALUE cap;
318
+
319
+ switch(NM_STYPE(self)) {
320
+ case YALE_STORE:
321
+ cap = UINT2NUM(((YALE_STORAGE*)(NM_STORAGE(self)))->capacity);
322
+ break;
323
+
324
+ case DENSE_STORE:
325
+ cap = UINT2NUM(nm_storage_count_max_elements( NM_STORAGE_DENSE(self) ));
326
+ break;
327
+
328
+ case LIST_STORE:
329
+ cap = UINT2NUM(nm_list_storage_count_elements( NM_STORAGE_LIST(self) ));
330
+ break;
331
+
332
+ default:
333
+ rb_raise(nm_eStorageTypeError, "unrecognized stype in nm_capacity()");
334
+ }
335
+
336
+ return cap;
337
+ }
338
+
339
+ /*
340
+ * Destructor.
341
+ */
342
+ static void nm_delete(NMATRIX* mat) {
343
+ static void (*ttable[nm::NUM_STYPES])(STORAGE*) = {
344
+ nm_dense_storage_delete,
345
+ nm_list_storage_delete,
346
+ nm_yale_storage_delete
347
+ };
348
+ ttable[mat->stype](mat->storage);
349
+ }
350
+
351
+ /*
352
+ * Slicing destructor.
353
+ */
354
+ static void nm_delete_ref(NMATRIX* mat) {
355
+ static void (*ttable[nm::NUM_STYPES])(STORAGE*) = {
356
+ nm_dense_storage_delete_ref,
357
+ nm_list_storage_delete_ref,
358
+ nm_yale_storage_delete
359
+ };
360
+ ttable[mat->stype](mat->storage);
361
+ }
362
+
363
+ /*
364
+ * Get the data type (dtype) of a matrix, e.g., :byte, :int8, :int16, :int32,
365
+ * :int64, :float32, :float64, :complex64, :complex128, :rational32,
366
+ * :rational64, :rational128, or :object (the last is a Ruby object).
367
+ */
368
+ static VALUE nm_dtype(VALUE self) {
369
+ ID dtype = rb_intern(DTYPE_NAMES[NM_DTYPE(self)]);
370
+ return ID2SYM(dtype);
371
+ }
372
+
373
+
374
+ /*
375
+ * Get the index data type (dtype) of a matrix. Defined only for yale; others return nil.
376
+ */
377
+ static VALUE nm_itype(VALUE self) {
378
+ if (NM_STYPE(self) == YALE_STORE) {
379
+ ID itype = rb_intern(ITYPE_NAMES[NM_ITYPE(self)]);
380
+ return ID2SYM(itype);
381
+ }
382
+ return Qnil;
383
+ }
384
+
385
+
386
+ /*
387
+ * Get the index data type (dtype) of a matrix. Defined only for yale; others return nil.
388
+ */
389
+ static VALUE nm_itype_by_shape(VALUE self, VALUE shape_arg) {
390
+
391
+ size_t dim;
392
+ size_t* shape = interpret_shape(shape_arg, &dim);
393
+
394
+ itype_t itype = nm_yale_storage_itype_by_shape(shape);
395
+ ID itype_id = rb_intern(ITYPE_NAMES[itype]);
396
+
397
+ return ID2SYM(itype_id);
398
+ }
399
+
400
+
401
+ /*
402
+ * Given a binary operation between types t1 and t2, what type will be returned?
403
+ *
404
+ * This is a singleton method on NMatrix, e.g., NMatrix.upcast(:int32, :int64)
405
+ */
406
+ static VALUE nm_upcast(VALUE self, VALUE t1, VALUE t2) {
407
+
408
+ dtype_t d1 = nm_dtype_from_rbsymbol(t1),
409
+ d2 = nm_dtype_from_rbsymbol(t2);
410
+
411
+ return ID2SYM(rb_intern( DTYPE_NAMES[ Upcast[d1][d2] ] ));
412
+ }
413
+
414
+
415
+ /*
416
+ * Each: Yield objects directly (suitable only for a dense matrix of Ruby objects).
417
+ */
418
+ static VALUE nm_dense_each_direct(VALUE nm) {
419
+ DENSE_STORAGE* s = NM_STORAGE_DENSE(nm);
420
+
421
+ RETURN_ENUMERATOR(nm, 0, 0);
422
+
423
+ for (size_t i = 0; i < nm_storage_count_max_elements(s); ++i)
424
+ rb_yield( reinterpret_cast<VALUE*>(s->elements)[i] );
425
+
426
+ return nm;
427
+ }
428
+
429
+ /*
430
+ * Each: Copy matrix elements into Ruby VALUEs before operating on them (suitable for a dense matrix).
431
+ */
432
+ static VALUE nm_dense_each_indirect(VALUE nm) {
433
+ DENSE_STORAGE* s = NM_STORAGE_DENSE(nm);
434
+
435
+ RETURN_ENUMERATOR(nm, 0, 0);
436
+
437
+ for (size_t i = 0; i < nm_storage_count_max_elements(s); ++i) {
438
+ VALUE v = rubyobj_from_cval((char*)(s->elements) + i*DTYPE_SIZES[NM_DTYPE(nm)], NM_DTYPE(nm)).rval;
439
+ rb_yield( v ); // yield to the copy we made
440
+ }
441
+
442
+ return nm;
443
+ }
444
+
445
+
446
+ /*
447
+ * Borrowed this function from NArray. Handles 'each' iteration on a dense
448
+ * matrix.
449
+ *
450
+ * Additionally, handles separately matrices containing VALUEs and matrices
451
+ * containing other types of data.
452
+ */
453
+ static VALUE nm_dense_each(VALUE nmatrix) {
454
+ volatile VALUE nm = nmatrix; // Not sure this actually does anything.
455
+
456
+ if (NM_DTYPE(nm) == RUBYOBJ) {
457
+
458
+ // matrix of Ruby objects -- yield those objects directly
459
+ return nm_dense_each_direct(nm);
460
+
461
+ } else {
462
+
463
+ // We're going to copy the matrix element into a Ruby VALUE and then operate on it. This way user can't accidentally
464
+ // modify it and cause a seg fault.
465
+ return nm_dense_each_indirect(nm);
466
+ }
467
+ }
468
+
469
+
470
+ /*
471
+ * Iterate over the matrix as you would an Enumerable (e.g., Array).
472
+ *
473
+ * Currently only works for dense.
474
+ */
475
+ static VALUE nm_each(VALUE nmatrix) {
476
+ volatile VALUE nm = nmatrix; // not sure why we do this, but it gets done in ruby's array.c.
477
+
478
+ switch(NM_STYPE(nm)) {
479
+ case DENSE_STORE:
480
+ return nm_dense_each(nm);
481
+ default:
482
+ rb_raise(rb_eNotImpError, "only dense matrix's each method works right now");
483
+ }
484
+ }
485
+
486
+
487
+
488
+ /*
489
+ * Equality operator. Returns a single true or false value indicating whether
490
+ * the matrices are equivalent.
491
+ *
492
+ * For elementwise, use =~ instead.
493
+ *
494
+ * This method will raise an exception if dimensions do not match.
495
+ */
496
+ static VALUE nm_eqeq(VALUE left, VALUE right) {
497
+ NMATRIX *l, *r;
498
+
499
+ CheckNMatrixType(left);
500
+ CheckNMatrixType(right);
501
+
502
+ UnwrapNMatrix(left, l);
503
+ UnwrapNMatrix(right, r);
504
+
505
+ if (l->stype != r->stype)
506
+ rb_raise(rb_eNotImpError, "comparison between different matrix stypes not yet implemented");
507
+
508
+ bool result = false;
509
+
510
+ switch(l->stype) {
511
+ case DENSE_STORE:
512
+ result = nm_dense_storage_eqeq(l->storage, r->storage);
513
+ break;
514
+ case LIST_STORE:
515
+ result = nm_list_storage_eqeq(l->storage, r->storage);
516
+ break;
517
+ case YALE_STORE:
518
+ result = nm_yale_storage_eqeq(l->storage, r->storage);
519
+ break;
520
+ }
521
+
522
+ return result ? Qtrue : Qfalse;
523
+ }
524
+
525
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(ADD, add)
526
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(SUB, subtract)
527
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(MUL, multiply)
528
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(DIV, divide)
529
+ //DEF_ELEMENTWISE_RUBY_ACCESSOR(MOD, mod)
530
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(EQEQ, eqeq)
531
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(NEQ, neq)
532
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(LEQ, leq)
533
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(GEQ, geq)
534
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(LT, lt)
535
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(GT, gt)
536
+
537
+ /*
538
+ * Is this matrix hermitian?
539
+ *
540
+ * Definition: http://en.wikipedia.org/wiki/Hermitian_matrix
541
+ *
542
+ * For non-complex matrices, this function should return the same result as symmetric?.
543
+ */
544
+ static VALUE nm_hermitian(VALUE self) {
545
+ return is_symmetric(self, true);
546
+ }
547
+
548
+ /*
549
+ * Transform the matrix (in-place) to its complex conjugate. Only works on complex matrices.
550
+ *
551
+ * FIXME: For non-complex matrices, someone needs to implement a non-in-place complex conjugate (which doesn't use a bang).
552
+ * Bang should imply that no copy is being made, even temporarily.
553
+ */
554
+ static VALUE nm_complex_conjugate_bang(VALUE self) {
555
+ NMATRIX* m;
556
+ void* elem;
557
+ size_t size, p;
558
+
559
+ UnwrapNMatrix(self, m);
560
+
561
+ if (m->stype == DENSE_STORE) {
562
+
563
+ size = nm_storage_count_max_elements(NM_STORAGE(self));
564
+ elem = NM_STORAGE_DENSE(self)->elements;
565
+
566
+ } else if (m->stype == YALE_STORE) {
567
+
568
+ size = nm_yale_storage_get_size(NM_STORAGE_YALE(self));
569
+ elem = NM_STORAGE_YALE(self)->a;
570
+
571
+ } else {
572
+ rb_raise(rb_eNotImpError, "please cast to yale or dense (complex) first");
573
+ }
574
+
575
+ // Walk through and negate the imaginary component
576
+ if (NM_DTYPE(self) == COMPLEX64) {
577
+
578
+ for (p = 0; p < size; ++p) {
579
+ reinterpret_cast<nm::Complex64*>(elem)[p].i = -reinterpret_cast<nm::Complex64*>(elem)[p].i;
580
+ }
581
+
582
+ } else if (NM_DTYPE(self) == COMPLEX128) {
583
+
584
+ for (p = 0; p < size; ++p) {
585
+ reinterpret_cast<nm::Complex128*>(elem)[p].i = -reinterpret_cast<nm::Complex128*>(elem)[p].i;
586
+ }
587
+
588
+ } else {
589
+ rb_raise(nm_eDataTypeError, "can only calculate in-place complex conjugate on matrices of type :complex64 or :complex128");
590
+ }
591
+
592
+ return self;
593
+ }
594
+
595
+
596
+ /*
597
+ * Helper function for creating a matrix. You have to create the storage and pass it in, but you don't
598
+ * need to worry about deleting it.
599
+ */
600
+ NMATRIX* nm_create(stype_t stype, STORAGE* storage) {
601
+ NMATRIX* mat = ALLOC(NMATRIX);
602
+
603
+ mat->stype = stype;
604
+ mat->storage = storage;
605
+
606
+ return mat;
607
+ }
608
+
609
+ /*
610
+ * Create a new NMatrix.
611
+ *
612
+ * There are several ways to do this. At a minimum, dimensions and either a dtype or initial values are needed, e.g.,
613
+ *
614
+ * NMatrix.new(3, :int64) # square 3x3 dense matrix
615
+ * NMatrix.new([3,4], :float32) # 3x4 matrix
616
+ * NMatrix.new(3, 0) # 3x3 dense matrix initialized to all zeros
617
+ * NMatrix.new([3,3], [1,2,3]) # [[1,2,3],[1,2,3],[1,2,3]]
618
+ *
619
+ * NMatrix will try to guess the dtype from the first value in the initial values array.
620
+ *
621
+ * You can also provide the stype prior to the dimensions. However, non-dense matrices cannot take initial values, and
622
+ * require a dtype (e.g., :int64):
623
+ *
624
+ * NMatrix.new(:yale, [4,3], :int64)
625
+ * NMatrix.new(:list, 5, :rational128)
626
+ *
627
+ * For Yale, you can also give an initial size for the non-diagonal component of the matrix:
628
+ *
629
+ * NMatrix.new(:yale, [4,3], 2, :int64)
630
+ *
631
+ * Finally, you can be extremely specific, and define a matrix very exactly:
632
+ *
633
+ * NMatrix.new(:dense, [2,2,2], [0,1,2,3,4,5,6,7], :int8)
634
+ *
635
+ * There is one additional constructor for advanced users, which takes seven arguments and is only for creating Yale matrices
636
+ * with known IA, JA, and A arrays. This is used primarily internally for IO, e.g., reading Matlab matrices, which are
637
+ * stored in old Yale format.
638
+ *
639
+ * Just be careful! There are no overflow warnings in NMatrix.
640
+ */
641
+ static VALUE nm_init(int argc, VALUE* argv, VALUE nm) {
642
+
643
+ if (argc < 2) {
644
+ rb_raise(rb_eArgError, "Expected 2-4 arguments (or 7 for internal Yale creation)");
645
+ return Qnil;
646
+ }
647
+
648
+ /* First, determine stype (dense by default) */
649
+ stype_t stype;
650
+ size_t offset = 0;
651
+
652
+ if (!SYMBOL_P(argv[0]) && TYPE(argv[0]) != T_STRING) {
653
+ stype = DENSE_STORE;
654
+
655
+ } else {
656
+ // 0: String or Symbol
657
+ stype = interpret_stype(argv[0]);
658
+ offset = 1;
659
+ }
660
+
661
+ // If there are 7 arguments and Yale, refer to a different init function with fewer sanity checks.
662
+ if (argc == 7) {
663
+ if (stype == YALE_STORE) {
664
+ return nm_init_yale_from_old_yale(argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], nm);
665
+
666
+ } else {
667
+ rb_raise(rb_eArgError, "Expected 2-4 arguments (or 7 for internal Yale creation)");
668
+ }
669
+ }
670
+
671
+ // 1: Array or Fixnum
672
+ size_t dim;
673
+ size_t* shape = interpret_shape(argv[offset], &dim);
674
+
675
+ // 2-3: dtype
676
+ dtype_t dtype = interpret_dtype(argc-1-offset, argv+offset+1, stype);
677
+
678
+ size_t init_cap = 0, init_val_len = 0;
679
+ void* init_val = NULL;
680
+ if (NM_RUBYVAL_IS_NUMERIC(argv[1+offset]) || TYPE(argv[1+offset]) == T_ARRAY) {
681
+ // Initial value provided (could also be initial capacity, if yale).
682
+
683
+ if (stype == YALE_STORE) {
684
+ init_cap = FIX2UINT(argv[1+offset]);
685
+
686
+ } else {
687
+ // 4: initial value / dtype
688
+ init_val = interpret_initial_value(argv[1+offset], dtype);
689
+
690
+ if (TYPE(argv[1+offset]) == T_ARRAY) init_val_len = RARRAY_LEN(argv[1+offset]);
691
+ else init_val_len = 1;
692
+ }
693
+
694
+ } else {
695
+ // DType is RUBYOBJ.
696
+
697
+ if (stype == DENSE_STORE) {
698
+ /*
699
+ * No need to initialize dense with any kind of default value unless it's
700
+ * an RUBYOBJ matrix.
701
+ */
702
+ if (dtype == RUBYOBJ) {
703
+ // Pretend [nil] was passed for RUBYOBJ.
704
+ init_val = ALLOC(VALUE);
705
+ *(VALUE*)init_val = Qnil;
706
+
707
+ init_val_len = 1;
708
+
709
+ } else {
710
+ init_val = NULL;
711
+ }
712
+ } else if (stype == LIST_STORE) {
713
+ init_val = ALLOC_N(char, DTYPE_SIZES[dtype]);
714
+ std::memset(init_val, 0, DTYPE_SIZES[dtype]);
715
+ }
716
+ }
717
+
718
+ // TODO: Update to allow an array as the initial value.
719
+ NMATRIX* nmatrix;
720
+ UnwrapNMatrix(nm, nmatrix);
721
+
722
+ nmatrix->stype = stype;
723
+
724
+ switch (stype) {
725
+ case DENSE_STORE:
726
+ nmatrix->storage = (STORAGE*)nm_dense_storage_create(dtype, shape, dim, init_val, init_val_len);
727
+ break;
728
+
729
+ case LIST_STORE:
730
+ nmatrix->storage = (STORAGE*)nm_list_storage_create(dtype, shape, dim, init_val);
731
+ break;
732
+
733
+ case YALE_STORE:
734
+ nmatrix->storage = (STORAGE*)nm_yale_storage_create(dtype, shape, dim, init_cap);
735
+ nm_yale_storage_init((YALE_STORAGE*)(nmatrix->storage));
736
+ break;
737
+ }
738
+
739
+ return nm;
740
+ }
741
+
742
+
743
+ /*
744
+ * Create a Ruby Hash from an NMatrix.
745
+ *
746
+ * Currently only works for list storage.
747
+ */
748
+ static VALUE nm_to_hash(VALUE self) {
749
+ if (NM_STYPE(self) != LIST_STORE) {
750
+ rb_raise(rb_eNotImpError, "please cast to :list first");
751
+ }
752
+
753
+ return nm_list_storage_to_hash(NM_STORAGE_LIST(self), NM_DTYPE(self));
754
+ }
755
+
756
+
757
+ /*
758
+ * Copy constructor for changing dtypes and stypes.
759
+ */
760
+ static VALUE nm_init_cast_copy(VALUE self, VALUE new_stype_symbol, VALUE new_dtype_symbol) {
761
+ dtype_t new_dtype = nm_dtype_from_rbsymbol(new_dtype_symbol);
762
+ stype_t new_stype = nm_stype_from_rbsymbol(new_stype_symbol);
763
+
764
+ CheckNMatrixType(self);
765
+
766
+ NMATRIX *lhs = ALLOC(NMATRIX),
767
+ *rhs;
768
+ lhs->stype = new_stype;
769
+
770
+ UnwrapNMatrix( self, rhs );
771
+
772
+ // Copy the storage
773
+ STYPE_CAST_COPY_TABLE(cast_copy);
774
+ lhs->storage = cast_copy[lhs->stype][rhs->stype](rhs->storage, new_dtype);
775
+
776
+ STYPE_MARK_TABLE(mark);
777
+
778
+ return Data_Wrap_Struct(CLASS_OF(self), mark[lhs->stype], nm_delete, lhs);
779
+ }
780
+
781
+
782
+ /*
783
+ * Copy constructor for transposing.
784
+ */
785
+ static VALUE nm_init_transposed(VALUE self) {
786
+ static STORAGE* (*storage_copy_transposed[nm::NUM_STYPES])(const STORAGE* rhs_base) = {
787
+ nm_dense_storage_copy_transposed,
788
+ nm_list_storage_copy_transposed,
789
+ nm_yale_storage_copy_transposed
790
+ };
791
+
792
+ NMATRIX* lhs = nm_create( NM_STYPE(self),
793
+ storage_copy_transposed[NM_STYPE(self)]( NM_STORAGE(self) )
794
+ );
795
+
796
+ STYPE_MARK_TABLE(mark);
797
+
798
+ return Data_Wrap_Struct(CLASS_OF(self), mark[lhs->stype], nm_delete, lhs);
799
+ }
800
+
801
+
802
+ /*
803
+ * Copy constructor for no change of dtype or stype (used for #initialize_copy hook).
804
+ */
805
+ static VALUE nm_init_copy(VALUE copy, VALUE original) {
806
+ NMATRIX *lhs, *rhs;
807
+
808
+ CheckNMatrixType(original);
809
+
810
+ if (copy == original) return copy;
811
+
812
+ UnwrapNMatrix( original, rhs );
813
+ UnwrapNMatrix( copy, lhs );
814
+
815
+ lhs->stype = rhs->stype;
816
+
817
+ // Copy the storage
818
+ STYPE_CAST_COPY_TABLE(ttable);
819
+ lhs->storage = ttable[lhs->stype][rhs->stype](rhs->storage, rhs->storage->dtype);
820
+
821
+ return copy;
822
+ }
823
+
824
+
825
+ /*
826
+ * Create a new NMatrix helper for handling internal ia, ja, and a arguments.
827
+ *
828
+ * This constructor is only called by Ruby code, so we can skip most of the
829
+ * checks.
830
+ */
831
+ static VALUE nm_init_yale_from_old_yale(VALUE shape, VALUE dtype, VALUE ia, VALUE ja, VALUE a, VALUE from_dtype, VALUE nm) {
832
+ size_t dim = 2;
833
+ size_t* shape_ = interpret_shape(shape, &dim);
834
+ dtype_t dtype_ = nm_dtype_from_rbsymbol(dtype);
835
+ char *ia_ = RSTRING_PTR(ia),
836
+ *ja_ = RSTRING_PTR(ja),
837
+ *a_ = RSTRING_PTR(a);
838
+ dtype_t from_dtype_ = nm_dtype_from_rbsymbol(from_dtype);
839
+ NMATRIX* nmatrix;
840
+
841
+ UnwrapNMatrix( nm, nmatrix );
842
+
843
+ nmatrix->stype = YALE_STORE;
844
+ nmatrix->storage = (STORAGE*)nm_yale_storage_create_from_old_yale(dtype_, shape_, ia_, ja_, a_, from_dtype_);
845
+
846
+ return nm;
847
+ }
848
+
849
+ /*
850
+ * Check to determine whether matrix is a reference to another matrix.
851
+ */
852
+ static VALUE nm_is_ref(VALUE self) {
853
+ // Refs only allowed for dense and list matrices.
854
+ if (NM_STYPE(self) == DENSE_STORE) {
855
+ return (NM_DENSE_SRC(self) == NM_STORAGE(self)) ? Qfalse : Qtrue;
856
+ }
857
+
858
+ if (NM_STYPE(self) == LIST_STORE) {
859
+ return (NM_LIST_SRC(self) == NM_STORAGE(self)) ? Qfalse : Qtrue;
860
+ }
861
+
862
+ return Qfalse;
863
+ }
864
+
865
+
866
+
867
+ /*
868
+ * Access the contents of an NMatrix at given coordinates, using copying.
869
+ *
870
+ * n.slice(3,3) # => 5.0
871
+ * n.slice(0..1,0..1) #=> matrix [2,2]
872
+ *
873
+ */
874
+ static VALUE nm_mget(int argc, VALUE* argv, VALUE self) {
875
+ static void* (*ttable[nm::NUM_STYPES])(STORAGE*, SLICE*) = {
876
+ nm_dense_storage_get,
877
+ nm_list_storage_get,
878
+ nm_yale_storage_get
879
+ };
880
+
881
+ return nm_xslice(argc, argv, ttable[NM_STYPE(self)], nm_delete, self);
882
+ }
883
+
884
+ /*
885
+ * Access the contents of an NMatrix at given coordinates by reference.
886
+ *
887
+ * n[3,3] # => 5.0
888
+ * n[0..1,0..1] #=> matrix [2,2]
889
+ *
890
+ */
891
+ static VALUE nm_mref(int argc, VALUE* argv, VALUE self) {
892
+ static void* (*ttable[nm::NUM_STYPES])(STORAGE*, SLICE*) = {
893
+ nm_dense_storage_ref,
894
+ nm_list_storage_ref,
895
+ nm_yale_storage_ref
896
+ };
897
+ return nm_xslice(argc, argv, ttable[NM_STYPE(self)], nm_delete_ref, self);
898
+ }
899
+
900
+ /*
901
+ * Modify the contents of an NMatrix in the given cell
902
+ *
903
+ * n[3,3] = 5.0
904
+ *
905
+ * Also returns the new contents, so you can chain:
906
+ *
907
+ * n[3,3] = n[2,3] = 5.0
908
+ */
909
+ static VALUE nm_mset(int argc, VALUE* argv, VALUE self) {
910
+ size_t dim = argc - 1; // last arg is the value
911
+
912
+ if (argc <= 1) {
913
+ rb_raise(rb_eArgError, "Expected coordinates and r-value");
914
+
915
+ } else if (NM_DIM(self) == dim) {
916
+
917
+ SLICE* slice = get_slice(dim, argv, self);
918
+
919
+ void* value = rubyobj_to_cval(argv[dim], NM_DTYPE(self));
920
+
921
+ // FIXME: Can't use a function pointer table here currently because these functions have different
922
+ // signatures (namely the return type).
923
+ switch(NM_STYPE(self)) {
924
+ case DENSE_STORE:
925
+ nm_dense_storage_set(NM_STORAGE(self), slice, value);
926
+ break;
927
+ case LIST_STORE:
928
+ // Remove if it's a zero, insert otherwise
929
+ if (!std::memcmp(value, NM_STORAGE_LIST(self)->default_val, DTYPE_SIZES[NM_DTYPE(self)])) {
930
+ free(value);
931
+ value = nm_list_storage_remove(NM_STORAGE(self), slice);
932
+ free(value);
933
+ } else {
934
+ nm_list_storage_insert(NM_STORAGE(self), slice, value);
935
+ }
936
+ break;
937
+ case YALE_STORE:
938
+ nm_yale_storage_set(NM_STORAGE(self), slice, value);
939
+ break;
940
+ }
941
+
942
+ return argv[dim];
943
+
944
+ } else if (NM_DIM(self) < dim) {
945
+ rb_raise(rb_eArgError, "Coordinates given exceed number of matrix dimensions");
946
+ } else {
947
+ rb_raise(rb_eNotImpError, "Slicing not supported yet");
948
+ }
949
+ return Qnil;
950
+ }
951
+
952
+ /*
953
+ * Matrix multiply (dot product): against another matrix or a vector.
954
+ *
955
+ * For elementwise, use * instead.
956
+ *
957
+ * The two matrices must be of the same stype (for now). If dtype differs, an upcast will occur.
958
+ */
959
+ static VALUE nm_multiply(VALUE left_v, VALUE right_v) {
960
+ NMATRIX *left, *right;
961
+
962
+ // left has to be of type NMatrix.
963
+ CheckNMatrixType(left_v);
964
+
965
+ UnwrapNMatrix( left_v, left );
966
+
967
+ if (NM_RUBYVAL_IS_NUMERIC(right_v))
968
+ return matrix_multiply_scalar(left, right_v);
969
+
970
+ else if (TYPE(right_v) == T_ARRAY)
971
+ rb_raise(rb_eNotImpError, "for matrix-vector multiplication, please use an NVector instead of an Array for now");
972
+
973
+ //if (RDATA(right_v)->dfree != (RUBY_DATA_FUNC)nm_delete) {
974
+ else { // both are matrices
975
+ CheckNMatrixType(right_v);
976
+ UnwrapNMatrix( right_v, right );
977
+
978
+ if (left->storage->shape[1] != right->storage->shape[0])
979
+ rb_raise(rb_eArgError, "incompatible dimensions");
980
+
981
+ if (left->stype != right->stype)
982
+ rb_raise(rb_eNotImpError, "matrices must have same stype");
983
+
984
+ return matrix_multiply(left, right);
985
+
986
+ }
987
+
988
+ return Qnil;
989
+ }
990
+
991
+ /*
992
+ * LU factorization of a matrix.
993
+ *
994
+ * FIXME: For some reason, getrf seems to require that the matrix be transposed first -- and then you have to transpose the
995
+ * FIXME: result again. Ideally, this would be an in-place factorize instead, and would be called nm_factorize_lu_bang.
996
+ */
997
+ static VALUE nm_factorize_lu(VALUE self) {
998
+ if (NM_STYPE(self) != DENSE_STORE) {
999
+ rb_raise(rb_eNotImpError, "only implemented for dense storage");
1000
+ }
1001
+
1002
+ if (NM_DIM(self) != 2) {
1003
+ rb_raise(rb_eNotImpError, "matrix is not 2-dimensional");
1004
+ }
1005
+
1006
+ VALUE copy = nm_init_transposed(self);
1007
+
1008
+ static int (*ttable[nm::NUM_DTYPES])(const enum CBLAS_ORDER, const int m, const int n, void* a, const int lda, int* ipiv) = {
1009
+ NULL, NULL, NULL, NULL, NULL, // integers not allowed due to division
1010
+ nm::math::clapack_getrf<float>,
1011
+ nm::math::clapack_getrf<double>,
1012
+ #ifdef HAVE_CLAPACK_H
1013
+ clapack_cgetrf, clapack_zgetrf, // call directly, same function signature!
1014
+ #else
1015
+ nm::math::clapack_getrf<nm::Complex64>,
1016
+ nm::math::clapack_getrf<nm::Complex128>,
1017
+ #endif
1018
+ nm::math::clapack_getrf<nm::Rational32>,
1019
+ nm::math::clapack_getrf<nm::Rational64>,
1020
+ nm::math::clapack_getrf<nm::Rational128>,
1021
+ nm::math::clapack_getrf<nm::RubyObject>
1022
+ };
1023
+
1024
+ int* ipiv = ALLOCA_N(int, std::min(NM_SHAPE0(copy), NM_SHAPE1(copy)));
1025
+
1026
+ // In-place factorize
1027
+ ttable[NM_DTYPE(copy)](CblasRowMajor, NM_SHAPE0(copy), NM_SHAPE1(copy), NM_STORAGE_DENSE(copy)->elements, NM_SHAPE1(copy), ipiv);
1028
+
1029
+ // Transpose the result
1030
+ return nm_init_transposed(copy);
1031
+ }
1032
+
1033
+ /*
1034
+ * Get the number of dimensions of a matrix.
1035
+ *
1036
+ * In other words, if you set your matrix to be 3x4, the dim is 2. If the
1037
+ * matrix was initialized as 3x4x3, the dim is 3.
1038
+ *
1039
+ * This function may lie slightly for NVectors, which are internally stored as
1040
+ * dim 2 (and have an orientation), but act as if they're dim 1.
1041
+ */
1042
+ static VALUE nm_dim(VALUE self) {
1043
+ return INT2FIX(NM_STORAGE(self)->dim);
1044
+ }
1045
+
1046
+ /*
1047
+ * Get the shape (dimensions) of a matrix.
1048
+ */
1049
+ static VALUE nm_shape(VALUE self) {
1050
+ STORAGE* s = NM_STORAGE(self);
1051
+ size_t index;
1052
+
1053
+ // Copy elements into a VALUE array and then use those to create a Ruby array with rb_ary_new4.
1054
+ VALUE* shape = ALLOCA_N(VALUE, s->dim);
1055
+ for (index = 0; index < s->dim; ++index)
1056
+ shape[index] = INT2FIX(s->shape[index]);
1057
+
1058
+ return rb_ary_new4(s->dim, shape);
1059
+ }
1060
+
1061
+ /*
1062
+ * Get the storage type (stype) of a matrix, e.g., :yale, :dense, or :list.
1063
+ */
1064
+ static VALUE nm_stype(VALUE self) {
1065
+ ID stype = rb_intern(STYPE_NAMES[NM_STYPE(self)]);
1066
+ return ID2SYM(stype);
1067
+ }
1068
+
1069
+ /*
1070
+ * Is this matrix symmetric?
1071
+ */
1072
+ static VALUE nm_symmetric(VALUE self) {
1073
+ return is_symmetric(self, false);
1074
+ }
1075
+
1076
+ /*
1077
+ * Get a slice of an NMatrix.
1078
+ */
1079
+ static VALUE nm_xslice(int argc, VALUE* argv, void* (*slice_func)(STORAGE*, SLICE*), void (*delete_func)(NMATRIX*), VALUE self) {
1080
+ VALUE result = Qnil;
1081
+
1082
+ if (NM_DIM(self) == (size_t)(argc)) {
1083
+ SLICE* slice = get_slice((size_t)(argc), argv, self);
1084
+
1085
+ if (slice->single) {
1086
+ static void* (*ttable[nm::NUM_STYPES])(STORAGE*, SLICE*) = {
1087
+ nm_dense_storage_ref,
1088
+ nm_list_storage_ref,
1089
+ nm_yale_storage_ref
1090
+ };
1091
+
1092
+ if (NM_DTYPE(self) == RUBYOBJ) result = *reinterpret_cast<VALUE*>( ttable[NM_STYPE(self)](NM_STORAGE(self), slice) );
1093
+ else result = rubyobj_from_cval( ttable[NM_STYPE(self)](NM_STORAGE(self), slice), NM_DTYPE(self) ).rval;
1094
+
1095
+ } else {
1096
+ STYPE_MARK_TABLE(mark_table);
1097
+
1098
+ NMATRIX* mat = ALLOC(NMATRIX);
1099
+ mat->stype = NM_STYPE(self);
1100
+ mat->storage = (STORAGE*)((*slice_func)( NM_STORAGE(self), slice ));
1101
+ result = Data_Wrap_Struct(cNMatrix, mark_table[mat->stype], delete_func, mat);
1102
+ }
1103
+
1104
+ free(slice);
1105
+
1106
+ } else if (NM_DIM(self) < (size_t)(argc)) {
1107
+ rb_raise(rb_eArgError, "Coordinates given exceed number of matrix dimensions");
1108
+ } else {
1109
+ rb_raise(rb_eNotImpError, "This type slicing not supported yet");
1110
+ }
1111
+
1112
+ return result;
1113
+ }
1114
+
1115
+ //////////////////////
1116
+ // Helper Functions //
1117
+ //////////////////////
1118
+
1119
+ static VALUE elementwise_op(nm::ewop_t op, VALUE left_val, VALUE right_val) {
1120
+ STYPE_MARK_TABLE(mark);
1121
+
1122
+ static STORAGE* (*ew_op[nm::NUM_STYPES])(nm::ewop_t, const STORAGE*, const STORAGE*) = {
1123
+ nm_dense_storage_ew_op,
1124
+ nm_list_storage_ew_op,
1125
+ nm_yale_storage_ew_op
1126
+ // NULL
1127
+ };
1128
+
1129
+ NMATRIX* result = ALLOC(NMATRIX);
1130
+
1131
+ CheckNMatrixType(left_val);
1132
+ CheckNMatrixType(right_val);
1133
+
1134
+ // Check that the left- and right-hand sides have the same dimensionality.
1135
+ if (NM_DIM(left_val) != NM_DIM(right_val)) {
1136
+ rb_raise(rb_eArgError, "The left- and right-hand sides of the operation must have the same dimensionality.");
1137
+ }
1138
+
1139
+ // Check that the left- and right-hand sides have the same shape.
1140
+ if (memcmp(&NM_SHAPE(left_val, 0), &NM_SHAPE(right_val, 0), sizeof(size_t) * NM_DIM(left_val)) != 0) {
1141
+ rb_raise(rb_eArgError, "The left- and right-hand sides of the operation must have the same shape.");
1142
+ }
1143
+
1144
+ NMATRIX* left, * right;
1145
+ UnwrapNMatrix(left_val, left);
1146
+ UnwrapNMatrix(right_val, right);
1147
+
1148
+ if (left->stype == right->stype) {
1149
+
1150
+ if (ew_op[left->stype] == NULL) {
1151
+ rb_raise(rb_eArgError, "Element-wise operations are not currently supported for this data type.");
1152
+ }
1153
+
1154
+ result->storage = ew_op[left->stype](op, reinterpret_cast<STORAGE*>(left->storage), reinterpret_cast<STORAGE*>(right->storage));
1155
+ result->stype = left->stype;
1156
+
1157
+ } else {
1158
+ rb_raise(rb_eArgError, "Element-wise operations are not currently supported between matrices with differing stypes.");
1159
+ }
1160
+
1161
+ return Data_Wrap_Struct(cNMatrix, mark[result->stype], nm_delete, result);
1162
+ }
1163
+
1164
+ /*
1165
+ * Check to determine whether matrix is a reference to another matrix.
1166
+ */
1167
+ bool is_ref(const NMATRIX* matrix) {
1168
+ // FIXME: Needs to work for other types
1169
+ if (matrix->stype != DENSE_STORE) {
1170
+ return false;
1171
+ }
1172
+
1173
+ return ((DENSE_STORAGE*)(matrix->storage))->src != matrix->storage;
1174
+ }
1175
+
1176
+ /*
1177
+ * Helper function for nm_symmetric and nm_hermitian.
1178
+ */
1179
+ static VALUE is_symmetric(VALUE self, bool hermitian) {
1180
+ NMATRIX* m;
1181
+ UnwrapNMatrix(self, m);
1182
+
1183
+ if (m->storage->shape[0] == m->storage->shape[1] and m->storage->dim == 2) {
1184
+ if (NM_STYPE(self) == DENSE_STORE) {
1185
+ if (hermitian) {
1186
+ nm_dense_storage_is_hermitian((DENSE_STORAGE*)(m->storage), m->storage->shape[0]);
1187
+
1188
+ } else {
1189
+ nm_dense_storage_is_symmetric((DENSE_STORAGE*)(m->storage), m->storage->shape[0]);
1190
+ }
1191
+
1192
+ } else {
1193
+ // TODO: Implement, at the very least, yale_is_symmetric. Model it after yale/transp.template.c.
1194
+ rb_raise(rb_eNotImpError, "symmetric? and hermitian? only implemented for dense currently");
1195
+ }
1196
+
1197
+ }
1198
+
1199
+ return Qfalse;
1200
+ }
1201
+
1202
+
1203
+ ///////////////////////
1204
+ // Utility Functions //
1205
+ ///////////////////////
1206
+
1207
+ /*
1208
+ * Guess the data type given a value.
1209
+ *
1210
+ * TODO: Probably needs some work for Bignum.
1211
+ */
1212
+ static dtype_t dtype_guess(VALUE v) {
1213
+ switch(TYPE(v)) {
1214
+ case T_TRUE:
1215
+ case T_FALSE:
1216
+ return BYTE;
1217
+
1218
+ case T_STRING:
1219
+ if (RSTRING_LEN(v) == 1) {
1220
+ return BYTE;
1221
+
1222
+ } else {
1223
+ rb_raise(rb_eArgError, "Strings of length > 1 may not be stored in a matrix.");
1224
+ }
1225
+
1226
+ #if SIZEOF_INT == 8
1227
+ case T_FIXNUM:
1228
+ return INT64;
1229
+
1230
+ case T_RATIONAL:
1231
+ return RATIONAL128;
1232
+
1233
+ #else
1234
+ # if SIZEOF_INT == 4
1235
+ case T_FIXNUM:
1236
+ return INT32;
1237
+
1238
+ case T_RATIONAL:
1239
+ return RATIONAL64;
1240
+
1241
+ #else
1242
+ case T_FIXNUM:
1243
+ return INT16;
1244
+
1245
+ case T_RATIONAL:
1246
+ return RATIONAL32;
1247
+ # endif
1248
+ #endif
1249
+
1250
+ case T_BIGNUM:
1251
+ return INT64;
1252
+
1253
+ #if SIZEOF_FLOAT == 4
1254
+ case T_COMPLEX:
1255
+ return COMPLEX128;
1256
+
1257
+ case T_FLOAT:
1258
+ return FLOAT64;
1259
+
1260
+ #else
1261
+ # if SIZEOF_FLOAT == 2
1262
+ case T_COMPLEX:
1263
+ return COMPLEX64;
1264
+
1265
+ case T_FLOAT:
1266
+ return FLOAT32;
1267
+ # endif
1268
+ #endif
1269
+
1270
+ case T_ARRAY:
1271
+ /*
1272
+ * May be passed for dense -- for now, just look at the first element.
1273
+ *
1274
+ * TODO: Look at entire array for most specific type.
1275
+ */
1276
+
1277
+ return dtype_guess(RARRAY_PTR(v)[0]);
1278
+
1279
+ case T_NIL:
1280
+ default:
1281
+ rb_raise(rb_eArgError, "Unable to guess a data type from provided parameters; data type must be specified manually.");
1282
+ }
1283
+ }
1284
+
1285
+ /*
1286
+ * Documentation goes here.
1287
+ */
1288
+ static SLICE* get_slice(size_t dim, VALUE* c, VALUE self) {
1289
+ size_t r;
1290
+ VALUE beg, end;
1291
+ int exl;
1292
+
1293
+ SLICE* slice = ALLOC(SLICE);
1294
+ slice->coords = ALLOC_N(size_t,dim);
1295
+ slice->lengths = ALLOC_N(size_t, dim);
1296
+ slice->single = true;
1297
+
1298
+ for (r = 0; r < dim; ++r) {
1299
+
1300
+ if (FIXNUM_P(c[r])) { // this used CLASS_OF before, which is inefficient for fixnum
1301
+
1302
+ slice->coords[r] = FIX2UINT(c[r]);
1303
+ slice->lengths[r] = 1;
1304
+
1305
+ } else if (CLASS_OF(c[r]) == rb_cRange) {
1306
+ rb_range_values(c[r], &beg, &end, &exl);
1307
+ slice->coords[r] = FIX2UINT(beg);
1308
+ slice->lengths[r] = FIX2UINT(end) - slice->coords[r] + 1;
1309
+
1310
+ // Exclude last element for a...b range
1311
+ if (exl)
1312
+ slice->lengths[r] -= 1;
1313
+
1314
+ slice->single = false;
1315
+
1316
+ } else {
1317
+ rb_raise(rb_eArgError, "cannot slice using class %s, needs a number or range or something", rb_obj_classname(c[r]));
1318
+ }
1319
+
1320
+ if (slice->coords[r] + slice->lengths[r] > NM_SHAPE(self,r))
1321
+ rb_raise(rb_eArgError, "out of range");
1322
+ }
1323
+
1324
+ return slice;
1325
+ }
1326
+
1327
+ #ifdef BENCHMARK
1328
+ /*
1329
+ * A simple function used when benchmarking NMatrix.
1330
+ */
1331
+ static double get_time(void) {
1332
+ struct timeval t;
1333
+ struct timezone tzp;
1334
+
1335
+ gettimeofday(&t, &tzp);
1336
+
1337
+ return t.tv_sec + t.tv_usec*1e-6;
1338
+ }
1339
+ #endif
1340
+
1341
+ /*
1342
+ * The argv parameter will be either 1 or 2 elements. If 1, could be either
1343
+ * initial or dtype. If 2, is initial and dtype. This function returns the
1344
+ * dtype.
1345
+ */
1346
+ static dtype_t interpret_dtype(int argc, VALUE* argv, stype_t stype) {
1347
+ int offset;
1348
+
1349
+ switch (argc) {
1350
+ case 1:
1351
+ offset = 0;
1352
+ break;
1353
+
1354
+ case 2:
1355
+ offset = 1;
1356
+ break;
1357
+
1358
+ default:
1359
+ rb_raise(rb_eArgError, "Need an initial value or a dtype.");
1360
+ break;
1361
+ }
1362
+
1363
+ if (SYMBOL_P(argv[offset])) {
1364
+ return nm_dtype_from_rbsymbol(argv[offset]);
1365
+
1366
+ } else if (TYPE(argv[offset]) == T_STRING) {
1367
+ return nm_dtype_from_rbstring(StringValue(argv[offset]));
1368
+
1369
+ } else if (stype == YALE_STORE) {
1370
+ rb_raise(rb_eArgError, "Yale storage class requires a dtype.");
1371
+
1372
+ } else {
1373
+ return dtype_guess(argv[0]);
1374
+ }
1375
+ }
1376
+
1377
+ /*
1378
+ * Convert an Ruby value or an array of Ruby values into initial C values.
1379
+ */
1380
+ static void* interpret_initial_value(VALUE arg, dtype_t dtype) {
1381
+ unsigned int index;
1382
+ void* init_val;
1383
+
1384
+ if (TYPE(arg) == T_ARRAY) {
1385
+ // Array
1386
+
1387
+ init_val = ALLOC_N(int8_t, DTYPE_SIZES[dtype] * RARRAY_LEN(arg));
1388
+ for (index = 0; index < RARRAY_LEN(arg); ++index) {
1389
+ rubyval_to_cval(RARRAY_PTR(arg)[index], dtype, (char*)init_val + (index * DTYPE_SIZES[dtype]));
1390
+ }
1391
+
1392
+ } else {
1393
+ // Single value
1394
+
1395
+ init_val = rubyobj_to_cval(arg, dtype);
1396
+ }
1397
+
1398
+ return init_val;
1399
+ }
1400
+
1401
+ /*
1402
+ * Convert the shape argument, which may be either a Ruby value or an array of
1403
+ * Ruby values, into C values. The second argument is where the dimensionality
1404
+ * of the matrix will be stored. The function itself returns a pointer to the
1405
+ * array describing the shape, which must be freed manually.
1406
+ */
1407
+ static size_t* interpret_shape(VALUE arg, size_t* dim) {
1408
+ size_t* shape;
1409
+
1410
+ if (TYPE(arg) == T_ARRAY) {
1411
+ *dim = RARRAY_LEN(arg);
1412
+ shape = ALLOC_N(size_t, *dim);
1413
+
1414
+ for (size_t index = 0; index < *dim; ++index) {
1415
+ shape[index] = FIX2UINT( RARRAY_PTR(arg)[index] );
1416
+ }
1417
+
1418
+ } else if (FIXNUM_P(arg)) {
1419
+ *dim = 2;
1420
+ shape = ALLOC_N(size_t, *dim);
1421
+
1422
+ shape[0] = FIX2UINT(arg);
1423
+ shape[1] = FIX2UINT(arg);
1424
+
1425
+ } else {
1426
+ rb_raise(rb_eArgError, "Expected an array of numbers or a single Fixnum for matrix shape");
1427
+ }
1428
+
1429
+ return shape;
1430
+ }
1431
+
1432
+ /*
1433
+ * Convert a Ruby symbol or string into an storage type.
1434
+ */
1435
+ static stype_t interpret_stype(VALUE arg) {
1436
+ if (SYMBOL_P(arg)) {
1437
+ return nm_stype_from_rbsymbol(arg);
1438
+
1439
+ } else if (TYPE(arg) == T_STRING) {
1440
+ return nm_stype_from_rbstring(StringValue(arg));
1441
+
1442
+ } else {
1443
+ rb_raise(rb_eArgError, "Expected storage type");
1444
+ }
1445
+ }
1446
+
1447
+
1448
+ //////////////////
1449
+ // Math Helpers //
1450
+ //////////////////
1451
+
1452
+
1453
+ STORAGE* matrix_storage_cast_alloc(NMATRIX* matrix, dtype_t new_dtype) {
1454
+ if (matrix->storage->dtype == new_dtype && !is_ref(matrix))
1455
+ return matrix->storage;
1456
+
1457
+ STYPE_CAST_COPY_TABLE(cast_copy_storage);
1458
+ return cast_copy_storage[matrix->stype][matrix->stype](matrix->storage, new_dtype);
1459
+ }
1460
+
1461
+
1462
+ STORAGE_PAIR binary_storage_cast_alloc(NMATRIX* left_matrix, NMATRIX* right_matrix) {
1463
+ STORAGE_PAIR casted;
1464
+ dtype_t new_dtype = Upcast[left_matrix->storage->dtype][right_matrix->storage->dtype];
1465
+
1466
+ casted.left = matrix_storage_cast_alloc(left_matrix, new_dtype);
1467
+ casted.right = matrix_storage_cast_alloc(right_matrix, new_dtype);
1468
+
1469
+ return casted;
1470
+ }
1471
+
1472
+
1473
+ static VALUE matrix_multiply_scalar(NMATRIX* left, VALUE scalar) {
1474
+ rb_raise(rb_eNotImpError, "matrix-scalar multiplication not implemented yet");
1475
+ return Qnil;
1476
+ }
1477
+
1478
+ static VALUE matrix_multiply(NMATRIX* left, NMATRIX* right) {
1479
+ ///TODO: multiplication for non-dense and/or non-decimal matrices
1480
+
1481
+ // Make sure both of our matrices are of the correct type.
1482
+ STORAGE_PAIR casted = binary_storage_cast_alloc(left, right);
1483
+
1484
+ size_t* resulting_shape = ALLOC_N(size_t, 2);
1485
+ resulting_shape[0] = left->storage->shape[0];
1486
+ resulting_shape[1] = right->storage->shape[1];
1487
+
1488
+ // Sometimes we only need to use matrix-vector multiplication (e.g., GEMM versus GEMV). Find out.
1489
+ bool vector = false;
1490
+ if (resulting_shape[1] == 1) vector = true;
1491
+
1492
+ static STORAGE* (*storage_matrix_multiply[nm::NUM_STYPES])(const STORAGE_PAIR&, size_t*, bool) = {
1493
+ nm_dense_storage_matrix_multiply,
1494
+ nm_list_storage_matrix_multiply,
1495
+ nm_yale_storage_matrix_multiply
1496
+ };
1497
+
1498
+ STORAGE* resulting_storage = storage_matrix_multiply[left->stype](casted, resulting_shape, vector);
1499
+ NMATRIX* result = nm_create(left->stype, resulting_storage);
1500
+
1501
+ // Free any casted-storage we created for the multiplication.
1502
+ // TODO: Can we make the Ruby GC take care of this stuff now that we're using it?
1503
+ // If we did that, we night not have to re-create these every time, right? Or wrong? Need to do
1504
+ // more research.
1505
+ static void (*free_storage[nm::NUM_STYPES])(STORAGE*) = {
1506
+ nm_dense_storage_delete,
1507
+ nm_list_storage_delete,
1508
+ nm_yale_storage_delete
1509
+ };
1510
+
1511
+ if (left->storage != casted.left) free_storage[result->stype](casted.left);
1512
+ if (right->storage != casted.right) free_storage[result->stype](casted.right);
1513
+
1514
+
1515
+ STYPE_MARK_TABLE(mark_table);
1516
+
1517
+ if (result) return Data_Wrap_Struct(cNMatrix, mark_table[result->stype], nm_delete, result);
1518
+ return Qnil; // Only if we try to multiply list matrices should we return Qnil.
1519
+ }
1520
+
1521
+
1522
+
1523
+
1524
+
1525
+ /*
1526
+ * Calculate the exact determinant of a dense matrix.
1527
+ *
1528
+ * Returns nil for dense matrices which are not square or number of dimensions other than 2.
1529
+ *
1530
+ * Note: Currently only implemented for 2x2 and 3x3 matrices.
1531
+ */
1532
+ static VALUE nm_det_exact(VALUE self) {
1533
+ if (NM_STYPE(self) != DENSE_STORE) rb_raise(nm_eStorageTypeError, "can only calculate exact determinant for dense matrices");
1534
+
1535
+ if (NM_DIM(self) != 2 || NM_SHAPE0(self) != NM_SHAPE1(self)) return Qnil;
1536
+
1537
+ // Calculate the determinant and then assign it to the return value
1538
+ void* result = ALLOCA_N(char, DTYPE_SIZES[NM_DTYPE(self)]);
1539
+ nm_math_det_exact(NM_SHAPE0(self), NM_STORAGE_DENSE(self)->elements, NM_SHAPE0(self), NM_DTYPE(self), result);
1540
+
1541
+ return rubyobj_from_cval(result, NM_DTYPE(self)).rval;
1542
+ }
1543
+
1544
+
1545
+
1546
+
1547
+ /////////////////
1548
+ // Exposed API //
1549
+ /////////////////
1550
+
1551
+
1552
+ /*
1553
+ * Create a dense matrix. Used by the NMatrix GSL fork. Unlike nm_create, this one copies all of the
1554
+ * arrays and such passed in -- so you don't have to allocate and pass a new shape object for every
1555
+ * matrix you want to create, for example. Same goes for elements.
1556
+ *
1557
+ * Returns a properly-wrapped Ruby object as a VALUE.
1558
+ *
1559
+ * TODO: Add a column-major option for libraries that use column-major matrices.
1560
+ */
1561
+ VALUE rb_nmatrix_dense_create(dtype_t dtype, size_t* shape, size_t dim, void* elements, size_t length) {
1562
+ NMATRIX* nm;
1563
+ VALUE klass;
1564
+ size_t nm_dim;
1565
+ size_t* shape_copy;
1566
+
1567
+ // Do not allow a dim of 1; if dim == 1, this should probably be an NVector instead, but that still has dim 2.
1568
+ if (dim == 1) {
1569
+ klass = cNVector;
1570
+ nm_dim = 2;
1571
+ shape_copy = ALLOC_N(size_t, nm_dim);
1572
+ shape_copy[0] = shape[0];
1573
+ shape_copy[1] = 1;
1574
+
1575
+ } else {
1576
+ klass = cNMatrix;
1577
+ nm_dim = dim;
1578
+ shape_copy = ALLOC_N(size_t, nm_dim);
1579
+ memcpy(shape_copy, shape, sizeof(size_t)*nm_dim);
1580
+ }
1581
+
1582
+ // Copy elements
1583
+ void* elements_copy = ALLOC_N(char, DTYPE_SIZES[dtype]*length);
1584
+ memcpy(elements_copy, elements, DTYPE_SIZES[dtype]*length);
1585
+
1586
+ // allocate and create the matrix and its storage
1587
+ nm = nm_create(DENSE_STORE, nm_dense_storage_create(dtype, shape_copy, dim, elements_copy, length));
1588
+
1589
+ // tell Ruby about the matrix and its storage, particularly how to garbage collect it.
1590
+ return Data_Wrap_Struct(klass, nm_dense_storage_mark, nm_dense_storage_delete, nm);
1591
+ }
1592
+
1593
+
1594
+ /*
1595
+ * Create a dense vector. Used by the NMatrix GSL fork.
1596
+ *
1597
+ * Basically just a convenience wrapper for rb_nmatrix_dense_create().
1598
+ *
1599
+ * Returns a properly-wrapped Ruby NVector object as a VALUE.
1600
+ *
1601
+ * TODO: Add a transpose option for setting the orientation of the vector?
1602
+ */
1603
+ VALUE rb_nvector_dense_create(dtype_t dtype, void* elements, size_t length) {
1604
+ size_t dim = 1, shape = length;
1605
+ return rb_nmatrix_dense_create(dtype, &shape, dim, elements, length);
1606
+ }
1607
+
1608
+ } // end of extern "C"
1609
+