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,3130 @@
1
+ /////////////////////////////////////////////////////////////////////
2
+ // = NMatrix
3
+ //
4
+ // A linear algebra library for scientific computation in Ruby.
5
+ // NMatrix is part of SciRuby.
6
+ //
7
+ // NMatrix was originally inspired by and derived from NArray, by
8
+ // Masahiro Tanaka: http://narray.rubyforge.org
9
+ //
10
+ // == Copyright Information
11
+ //
12
+ // SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
13
+ // NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
14
+ //
15
+ // Please see LICENSE.txt for additional copyright notices.
16
+ //
17
+ // == Contributing
18
+ //
19
+ // By contributing source code to SciRuby, you agree to be bound by
20
+ // our Contributor Agreement:
21
+ //
22
+ // * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
23
+ //
24
+ // == ruby_nmatrix.c
25
+ //
26
+ // Ruby-facing NMatrix C functions. Not compiled directly -- included
27
+ // into nmatrix.cpp.
28
+ //
29
+
30
+ /*
31
+ * Forward Declarations
32
+ */
33
+
34
+ static VALUE nm_init(int argc, VALUE* argv, VALUE nm);
35
+ static VALUE nm_init_copy(VALUE copy, VALUE original);
36
+ static VALUE nm_init_transposed(VALUE self);
37
+ static VALUE nm_read(int argc, VALUE* argv, VALUE self);
38
+ static VALUE nm_write(int argc, VALUE* argv, VALUE self);
39
+ static VALUE nm_init_yale_from_old_yale(VALUE shape, VALUE dtype, VALUE ia, VALUE ja, VALUE a, VALUE from_dtype, VALUE nm);
40
+ static VALUE nm_alloc(VALUE klass);
41
+ static VALUE nm_dtype(VALUE self);
42
+ static VALUE nm_stype(VALUE self);
43
+ static VALUE nm_default_value(VALUE self);
44
+ static size_t effective_dim(STORAGE* s);
45
+ static VALUE nm_effective_dim(VALUE self);
46
+ static VALUE nm_dim(VALUE self);
47
+ static VALUE nm_offset(VALUE self);
48
+ static VALUE nm_shape(VALUE self);
49
+ static VALUE nm_supershape(VALUE self);
50
+ static VALUE nm_capacity(VALUE self);
51
+ static VALUE nm_each_with_indices(VALUE nmatrix);
52
+ static VALUE nm_each_stored_with_indices(VALUE nmatrix);
53
+ static VALUE nm_each_ordered_stored_with_indices(VALUE nmatrix);
54
+ static VALUE nm_map_stored(VALUE nmatrix);
55
+
56
+ static void init_slice_no_alloc(SLICE* slice, size_t dim, int argc, VALUE* arg, size_t* shape);
57
+ static VALUE nm_xslice(int argc, VALUE* argv, void* (*slice_func)(const STORAGE*, SLICE*), void (*delete_func)(NMATRIX*), VALUE self);
58
+ static VALUE nm_mset(int argc, VALUE* argv, VALUE self);
59
+ static VALUE nm_mget(int argc, VALUE* argv, VALUE self);
60
+ static VALUE nm_mref(int argc, VALUE* argv, VALUE self);
61
+ static VALUE nm_is_ref(VALUE self);
62
+
63
+ static VALUE is_symmetric(VALUE self, bool hermitian);
64
+
65
+ static VALUE nm_guess_dtype(VALUE self, VALUE v);
66
+ static VALUE nm_min_dtype(VALUE self, VALUE v);
67
+
68
+ static VALUE nm_data_pointer(VALUE self);
69
+
70
+ /*
71
+ * Macro defines an element-wise accessor function for some operation.
72
+ *
73
+ * This is only responsible for the Ruby accessor! You still have to write the actual functions, obviously.
74
+ */
75
+ #define DEF_ELEMENTWISE_RUBY_ACCESSOR(oper, name) \
76
+ static VALUE nm_ew_##name(VALUE left_val, VALUE right_val) { \
77
+ return elementwise_op(nm::EW_##oper, left_val, right_val); \
78
+ }
79
+
80
+ #define DEF_UNARY_RUBY_ACCESSOR(oper, name) \
81
+ static VALUE nm_unary_##name(VALUE self) { \
82
+ return unary_op(nm::UNARY_##oper, self); \
83
+ }
84
+
85
+ #define DEF_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(oper, name) \
86
+ static VALUE nm_noncom_ew_##name(int argc, VALUE* argv, VALUE self) { \
87
+ if (argc > 1) { \
88
+ return noncom_elementwise_op(nm::NONCOM_EW_##oper, self, argv[0], argv[1]); \
89
+ } else { \
90
+ return noncom_elementwise_op(nm::NONCOM_EW_##oper, self, argv[0], Qfalse); \
91
+ } \
92
+ }
93
+
94
+
95
+ /*
96
+ * Macro declares a corresponding accessor function prototype for some element-wise operation.
97
+ */
98
+ #define DECL_ELEMENTWISE_RUBY_ACCESSOR(name) static VALUE nm_ew_##name(VALUE left_val, VALUE right_val);
99
+ #define DECL_UNARY_RUBY_ACCESSOR(name) static VALUE nm_unary_##name(VALUE self);
100
+ #define DECL_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(name) static VALUE nm_noncom_ew_##name(int argc, VALUE* argv, VALUE self);
101
+
102
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(add)
103
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(subtract)
104
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(multiply)
105
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(divide)
106
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(power)
107
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(mod)
108
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(eqeq)
109
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(neq)
110
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(lt)
111
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(gt)
112
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(leq)
113
+ DECL_ELEMENTWISE_RUBY_ACCESSOR(geq)
114
+ DECL_UNARY_RUBY_ACCESSOR(sin)
115
+ DECL_UNARY_RUBY_ACCESSOR(cos)
116
+ DECL_UNARY_RUBY_ACCESSOR(tan)
117
+ DECL_UNARY_RUBY_ACCESSOR(asin)
118
+ DECL_UNARY_RUBY_ACCESSOR(acos)
119
+ DECL_UNARY_RUBY_ACCESSOR(atan)
120
+ DECL_UNARY_RUBY_ACCESSOR(sinh)
121
+ DECL_UNARY_RUBY_ACCESSOR(cosh)
122
+ DECL_UNARY_RUBY_ACCESSOR(tanh)
123
+ DECL_UNARY_RUBY_ACCESSOR(asinh)
124
+ DECL_UNARY_RUBY_ACCESSOR(acosh)
125
+ DECL_UNARY_RUBY_ACCESSOR(atanh)
126
+ DECL_UNARY_RUBY_ACCESSOR(exp)
127
+ DECL_UNARY_RUBY_ACCESSOR(log2)
128
+ DECL_UNARY_RUBY_ACCESSOR(log10)
129
+ DECL_UNARY_RUBY_ACCESSOR(sqrt)
130
+ DECL_UNARY_RUBY_ACCESSOR(erf)
131
+ DECL_UNARY_RUBY_ACCESSOR(erfc)
132
+ DECL_UNARY_RUBY_ACCESSOR(cbrt)
133
+ DECL_UNARY_RUBY_ACCESSOR(gamma)
134
+ DECL_UNARY_RUBY_ACCESSOR(negate)
135
+ DECL_UNARY_RUBY_ACCESSOR(floor)
136
+ DECL_UNARY_RUBY_ACCESSOR(ceil)
137
+ DECL_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(atan2)
138
+ DECL_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(ldexp)
139
+ DECL_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(hypot)
140
+
141
+ //log/round can be unary, but also take a base argument, as with Math.log
142
+ static VALUE nm_unary_log(int argc, VALUE* argv, VALUE self);
143
+ static VALUE nm_unary_round(int argc, VALUE* argv, VALUE self);
144
+
145
+ static VALUE elementwise_op(nm::ewop_t op, VALUE left_val, VALUE right_val);
146
+ static VALUE unary_op(nm::unaryop_t op, VALUE self);
147
+ static VALUE noncom_elementwise_op(nm::noncom_ewop_t op, VALUE self, VALUE other, VALUE orderflip);
148
+
149
+ static VALUE nm_symmetric(VALUE self);
150
+ static VALUE nm_hermitian(VALUE self);
151
+
152
+ static VALUE nm_eqeq(VALUE left, VALUE right);
153
+
154
+ static VALUE matrix_multiply_scalar(NMATRIX* left, VALUE scalar);
155
+ static VALUE matrix_multiply(NMATRIX* left, NMATRIX* right);
156
+ static VALUE nm_multiply(VALUE left_v, VALUE right_v);
157
+ static VALUE nm_det_exact(VALUE self);
158
+ static VALUE nm_hessenberg(VALUE self, VALUE a);
159
+ static VALUE nm_inverse(VALUE self, VALUE inverse, VALUE bang);
160
+ static VALUE nm_inverse_exact(VALUE self, VALUE inverse, VALUE lda, VALUE ldb);
161
+ static VALUE nm_complex_conjugate_bang(VALUE self);
162
+ static VALUE nm_reshape_bang(VALUE self, VALUE arg);
163
+
164
+ static nm::dtype_t interpret_dtype(int argc, VALUE* argv, nm::stype_t stype);
165
+ static void* interpret_initial_value(VALUE arg, nm::dtype_t dtype);
166
+ static size_t* interpret_shape(VALUE arg, size_t* dim);
167
+ static nm::stype_t interpret_stype(VALUE arg);
168
+
169
+ /* Singleton methods */
170
+ static VALUE nm_upcast(VALUE self, VALUE t1, VALUE t2);
171
+
172
+
173
+ #ifdef BENCHMARK
174
+ static double get_time(void);
175
+ #endif
176
+
177
+ ///////////////////
178
+ // Ruby Bindings //
179
+ ///////////////////
180
+
181
+ void Init_nmatrix() {
182
+
183
+
184
+ ///////////////////////
185
+ // Class Definitions //
186
+ ///////////////////////
187
+
188
+ cNMatrix = rb_define_class("NMatrix", rb_cObject);
189
+
190
+ // Special exceptions
191
+
192
+ /*
193
+ * Exception raised when there's a problem with data.
194
+ */
195
+ nm_eDataTypeError = rb_define_class("DataTypeError", rb_eStandardError);
196
+
197
+ /*
198
+ * Exception raised when something goes wrong with the storage of a matrix.
199
+ */
200
+ nm_eStorageTypeError = rb_define_class("StorageTypeError", rb_eStandardError);
201
+
202
+ /*
203
+ * Exception raise when the matrix shape is not appropriate for a given operation.
204
+ */
205
+ nm_eShapeError = rb_define_class("ShapeError", rb_eStandardError);
206
+
207
+ /*
208
+ * Exception raise when an inverse is requested but the matrix is not invertible.
209
+ */
210
+ nm_eNotInvertibleError = rb_define_class("NotInvertibleError", rb_eStandardError);
211
+
212
+ /*
213
+ * :nodoc:
214
+ * Class that holds values in use by the C code.
215
+ */
216
+ cNMatrix_GC_holder = rb_define_class("NMGCHolder", rb_cObject);
217
+
218
+
219
+ ///////////////////
220
+ // Class Methods //
221
+ ///////////////////
222
+
223
+ rb_define_alloc_func(cNMatrix, nm_alloc);
224
+
225
+ ///////////////////////
226
+ // Singleton Methods //
227
+ ///////////////////////
228
+
229
+ rb_define_singleton_method(cNMatrix, "upcast", (METHOD)nm_upcast, 2); /* in ext/nmatrix/nmatrix.cpp */
230
+ rb_define_singleton_method(cNMatrix, "guess_dtype", (METHOD)nm_guess_dtype, 1);
231
+ rb_define_singleton_method(cNMatrix, "min_dtype", (METHOD)nm_min_dtype, 1);
232
+
233
+ //////////////////////
234
+ // Instance Methods //
235
+ //////////////////////
236
+
237
+ rb_define_method(cNMatrix, "initialize", (METHOD)nm_init, -1);
238
+ rb_define_method(cNMatrix, "initialize_copy", (METHOD)nm_init_copy, 1);
239
+ rb_define_singleton_method(cNMatrix, "read", (METHOD)nm_read, -1);
240
+
241
+ rb_define_method(cNMatrix, "write", (METHOD)nm_write, -1);
242
+
243
+ // Technically, the following function is a copy constructor.
244
+ rb_define_protected_method(cNMatrix, "clone_transpose", (METHOD)nm_init_transposed, 0);
245
+
246
+ rb_define_method(cNMatrix, "dtype", (METHOD)nm_dtype, 0);
247
+ rb_define_method(cNMatrix, "stype", (METHOD)nm_stype, 0);
248
+ rb_define_method(cNMatrix, "cast_full", (METHOD)nm_cast, 3);
249
+ rb_define_method(cNMatrix, "default_value", (METHOD)nm_default_value, 0);
250
+ rb_define_protected_method(cNMatrix, "__list_default_value__", (METHOD)nm_list_default_value, 0);
251
+ rb_define_protected_method(cNMatrix, "__yale_default_value__", (METHOD)nm_yale_default_value, 0);
252
+
253
+ rb_define_method(cNMatrix, "[]", (METHOD)nm_mref, -1);
254
+ rb_define_method(cNMatrix, "slice", (METHOD)nm_mget, -1);
255
+ rb_define_method(cNMatrix, "[]=", (METHOD)nm_mset, -1);
256
+ rb_define_method(cNMatrix, "is_ref?", (METHOD)nm_is_ref, 0);
257
+ rb_define_method(cNMatrix, "dimensions", (METHOD)nm_dim, 0);
258
+ rb_define_method(cNMatrix, "effective_dimensions", (METHOD)nm_effective_dim, 0);
259
+
260
+ rb_define_protected_method(cNMatrix, "__list_to_hash__", (METHOD)nm_to_hash, 0); // handles list and dense, which are n-dimensional
261
+
262
+ rb_define_method(cNMatrix, "shape", (METHOD)nm_shape, 0);
263
+ rb_define_method(cNMatrix, "supershape", (METHOD)nm_supershape, 0);
264
+ rb_define_method(cNMatrix, "offset", (METHOD)nm_offset, 0);
265
+ rb_define_method(cNMatrix, "det_exact", (METHOD)nm_det_exact, 0);
266
+ rb_define_method(cNMatrix, "complex_conjugate!", (METHOD)nm_complex_conjugate_bang, 0);
267
+
268
+ rb_define_protected_method(cNMatrix, "reshape_bang", (METHOD)nm_reshape_bang, 1);
269
+
270
+ // Iterators public methods
271
+ rb_define_method(cNMatrix, "each_with_indices", (METHOD)nm_each_with_indices, 0);
272
+ rb_define_method(cNMatrix, "each_stored_with_indices", (METHOD)nm_each_stored_with_indices, 0);
273
+ rb_define_method(cNMatrix, "map_stored", (METHOD)nm_map_stored, 0);
274
+ rb_define_method(cNMatrix, "each_ordered_stored_with_indices", (METHOD)nm_each_ordered_stored_with_indices, 0);
275
+
276
+ // Iterators protected methods
277
+ rb_define_protected_method(cNMatrix, "__dense_each__", (METHOD)nm_dense_each, 0);
278
+ rb_define_protected_method(cNMatrix, "__dense_map__", (METHOD)nm_dense_map, 0);
279
+ rb_define_protected_method(cNMatrix, "__dense_map_pair__", (METHOD)nm_dense_map_pair, 1);
280
+ rb_define_protected_method(cNMatrix, "__list_map_merged_stored__", (METHOD)nm_list_map_merged_stored, 2);
281
+ rb_define_protected_method(cNMatrix, "__list_map_stored__", (METHOD)nm_list_map_stored, 1);
282
+ rb_define_protected_method(cNMatrix, "__yale_map_merged_stored__", (METHOD)nm_yale_map_merged_stored, 2);
283
+ rb_define_protected_method(cNMatrix, "__yale_map_stored__", (METHOD)nm_yale_map_stored, 0);
284
+ rb_define_protected_method(cNMatrix, "__yale_stored_diagonal_each_with_indices__", (METHOD)nm_yale_stored_diagonal_each_with_indices, 0);
285
+ rb_define_protected_method(cNMatrix, "__yale_stored_nondiagonal_each_with_indices__", (METHOD)nm_yale_stored_nondiagonal_each_with_indices, 0);
286
+
287
+ rb_define_method(cNMatrix, "==", (METHOD)nm_eqeq, 1);
288
+
289
+ rb_define_method(cNMatrix, "+", (METHOD)nm_ew_add, 1);
290
+ rb_define_method(cNMatrix, "-", (METHOD)nm_ew_subtract, 1);
291
+ rb_define_method(cNMatrix, "*", (METHOD)nm_ew_multiply, 1);
292
+ rb_define_method(cNMatrix, "/", (METHOD)nm_ew_divide, 1);
293
+ rb_define_method(cNMatrix, "**", (METHOD)nm_ew_power, 1);
294
+ rb_define_method(cNMatrix, "%", (METHOD)nm_ew_mod, 1);
295
+
296
+ rb_define_method(cNMatrix, "atan2", (METHOD)nm_noncom_ew_atan2, -1);
297
+ rb_define_method(cNMatrix, "ldexp", (METHOD)nm_noncom_ew_ldexp, -1);
298
+ rb_define_method(cNMatrix, "hypot", (METHOD)nm_noncom_ew_hypot, -1);
299
+
300
+ rb_define_method(cNMatrix, "sin", (METHOD)nm_unary_sin, 0);
301
+ rb_define_method(cNMatrix, "cos", (METHOD)nm_unary_cos, 0);
302
+ rb_define_method(cNMatrix, "tan", (METHOD)nm_unary_tan, 0);
303
+ rb_define_method(cNMatrix, "asin", (METHOD)nm_unary_asin, 0);
304
+ rb_define_method(cNMatrix, "acos", (METHOD)nm_unary_acos, 0);
305
+ rb_define_method(cNMatrix, "atan", (METHOD)nm_unary_atan, 0);
306
+ rb_define_method(cNMatrix, "sinh", (METHOD)nm_unary_sinh, 0);
307
+ rb_define_method(cNMatrix, "cosh", (METHOD)nm_unary_cosh, 0);
308
+ rb_define_method(cNMatrix, "tanh", (METHOD)nm_unary_tanh, 0);
309
+ rb_define_method(cNMatrix, "asinh", (METHOD)nm_unary_asinh, 0);
310
+ rb_define_method(cNMatrix, "acosh", (METHOD)nm_unary_acosh, 0);
311
+ rb_define_method(cNMatrix, "atanh", (METHOD)nm_unary_atanh, 0);
312
+ rb_define_method(cNMatrix, "exp", (METHOD)nm_unary_exp, 0);
313
+ rb_define_method(cNMatrix, "log2", (METHOD)nm_unary_log2, 0);
314
+ rb_define_method(cNMatrix, "log10", (METHOD)nm_unary_log10, 0);
315
+ rb_define_method(cNMatrix, "sqrt", (METHOD)nm_unary_sqrt, 0);
316
+ rb_define_method(cNMatrix, "erf", (METHOD)nm_unary_erf, 0);
317
+ rb_define_method(cNMatrix, "erfc", (METHOD)nm_unary_erfc, 0);
318
+ rb_define_method(cNMatrix, "cbrt", (METHOD)nm_unary_cbrt, 0);
319
+ rb_define_method(cNMatrix, "gamma", (METHOD)nm_unary_gamma, 0);
320
+ rb_define_method(cNMatrix, "log", (METHOD)nm_unary_log, -1);
321
+ rb_define_method(cNMatrix, "-@", (METHOD)nm_unary_negate,0);
322
+ rb_define_method(cNMatrix, "floor", (METHOD)nm_unary_floor, 0);
323
+ rb_define_method(cNMatrix, "ceil", (METHOD)nm_unary_ceil, 0);
324
+ rb_define_method(cNMatrix, "round", (METHOD)nm_unary_round, -1);
325
+
326
+
327
+ rb_define_method(cNMatrix, "=~", (METHOD)nm_ew_eqeq, 1);
328
+ rb_define_method(cNMatrix, "!~", (METHOD)nm_ew_neq, 1);
329
+ rb_define_method(cNMatrix, "<=", (METHOD)nm_ew_leq, 1);
330
+ rb_define_method(cNMatrix, ">=", (METHOD)nm_ew_geq, 1);
331
+ rb_define_method(cNMatrix, "<", (METHOD)nm_ew_lt, 1);
332
+ rb_define_method(cNMatrix, ">", (METHOD)nm_ew_gt, 1);
333
+
334
+ /////////////////////////////
335
+ // Helper Instance Methods //
336
+ /////////////////////////////
337
+ rb_define_protected_method(cNMatrix, "__yale_vector_set__", (METHOD)nm_vector_set, -1);
338
+
339
+ /////////////////////////
340
+ // Matrix Math Methods //
341
+ /////////////////////////
342
+ rb_define_method(cNMatrix, "dot", (METHOD)nm_multiply, 1);
343
+ rb_define_method(cNMatrix, "symmetric?", (METHOD)nm_symmetric, 0);
344
+ rb_define_method(cNMatrix, "hermitian?", (METHOD)nm_hermitian, 0);
345
+ rb_define_method(cNMatrix, "capacity", (METHOD)nm_capacity, 0);
346
+
347
+ // protected methods
348
+ rb_define_protected_method(cNMatrix, "__inverse__", (METHOD)nm_inverse, 2);
349
+ rb_define_protected_method(cNMatrix, "__inverse_exact__", (METHOD)nm_inverse_exact, 3);
350
+
351
+ // private methods
352
+ rb_define_private_method(cNMatrix, "__hessenberg__", (METHOD)nm_hessenberg, 1);
353
+
354
+ /////////////////
355
+ // FFI Methods //
356
+ /////////////////
357
+ rb_define_method(cNMatrix, "data_pointer", (METHOD)nm_data_pointer, 0);
358
+
359
+ /////////////
360
+ // Aliases //
361
+ /////////////
362
+
363
+ rb_define_alias(cNMatrix, "dim", "dimensions");
364
+ rb_define_alias(cNMatrix, "effective_dim", "effective_dimensions");
365
+ rb_define_alias(cNMatrix, "equal?", "eql?");
366
+
367
+ ////////////
368
+ //Epsilons//
369
+ ////////////
370
+ rb_define_const(cNMatrix, "FLOAT64_EPSILON", rb_const_get(rb_cFloat, rb_intern("EPSILON")));
371
+ rb_define_const(cNMatrix, "FLOAT32_EPSILON", DBL2NUM(FLT_EPSILON));
372
+
373
+ ///////////////////////
374
+ // Symbol Generation //
375
+ ///////////////////////
376
+
377
+ nm_init_ruby_constants();
378
+
379
+ //////////////////////////
380
+ // YaleFunctions module //
381
+ //////////////////////////
382
+
383
+ nm_init_yale_functions();
384
+
385
+ /////////////////
386
+ // BLAS module //
387
+ /////////////////
388
+
389
+ nm_math_init_blas();
390
+
391
+ ///////////////
392
+ // IO module //
393
+ ///////////////
394
+ nm_init_io();
395
+
396
+ /////////////////////////////////////////////////
397
+ // Force compilation of necessary constructors //
398
+ /////////////////////////////////////////////////
399
+ nm_init_data();
400
+ }
401
+
402
+
403
+ //////////////////
404
+ // Ruby Methods //
405
+ //////////////////
406
+
407
+ /*
408
+ * Allocator.
409
+ */
410
+ static VALUE nm_alloc(VALUE klass) {
411
+ NMATRIX* mat = NM_ALLOC(NMATRIX);
412
+ mat->storage = NULL;
413
+
414
+ // DO NOT MARK This STRUCT. It has no storage allocated, and no stype, so mark will do an invalid something.
415
+ return Data_Wrap_Struct(klass, NULL, nm_delete, mat);
416
+ }
417
+
418
+ /*
419
+ * Find the capacity of an NMatrix. The capacity only differs from the size for
420
+ * Yale matrices, which occasionally allocate more space than they need. For
421
+ * list and dense, capacity gives the number of elements in the matrix.
422
+ *
423
+ * If you call this on a slice, it may behave unpredictably. Most likely it'll
424
+ * just return the original matrix's capacity.
425
+ */
426
+ static VALUE nm_capacity(VALUE self) {
427
+ NM_CONSERVATIVE(nm_register_value(&self));
428
+ VALUE cap;
429
+
430
+ switch(NM_STYPE(self)) {
431
+ case nm::YALE_STORE:
432
+ cap = UINT2NUM(reinterpret_cast<YALE_STORAGE*>(NM_STORAGE_YALE(self)->src)->capacity);
433
+ break;
434
+
435
+ case nm::DENSE_STORE:
436
+ cap = UINT2NUM(nm_storage_count_max_elements( NM_STORAGE_DENSE(self) ));
437
+ break;
438
+
439
+ case nm::LIST_STORE:
440
+ cap = UINT2NUM(nm_list_storage_count_elements( NM_STORAGE_LIST(self) ));
441
+ break;
442
+
443
+ default:
444
+ NM_CONSERVATIVE(nm_unregister_value(&self));
445
+ rb_raise(nm_eStorageTypeError, "unrecognized stype in nm_capacity()");
446
+ }
447
+
448
+ NM_CONSERVATIVE(nm_unregister_value(&self));
449
+ return cap;
450
+ }
451
+
452
+
453
+ /*
454
+ * Mark function.
455
+ */
456
+ void nm_mark(NMATRIX* mat) {
457
+ STYPE_MARK_TABLE(mark)
458
+ mark[mat->stype](mat->storage);
459
+ }
460
+
461
+
462
+ /*
463
+ * Destructor.
464
+ */
465
+ void nm_delete(NMATRIX* mat) {
466
+ static void (*ttable[nm::NUM_STYPES])(STORAGE*) = {
467
+ nm_dense_storage_delete,
468
+ nm_list_storage_delete,
469
+ nm_yale_storage_delete
470
+ };
471
+ ttable[mat->stype](mat->storage);
472
+
473
+ NM_FREE(mat);
474
+ }
475
+
476
+ /*
477
+ * Slicing destructor.
478
+ */
479
+ void nm_delete_ref(NMATRIX* mat) {
480
+ static void (*ttable[nm::NUM_STYPES])(STORAGE*) = {
481
+ nm_dense_storage_delete_ref,
482
+ nm_list_storage_delete_ref,
483
+ nm_yale_storage_delete_ref
484
+ };
485
+ ttable[mat->stype](mat->storage);
486
+
487
+ NM_FREE(mat);
488
+ }
489
+
490
+
491
+ /**
492
+ * These variables hold a linked list of VALUEs that are registered to be in
493
+ * use by nmatrix so that they can be marked when GC runs.
494
+ */
495
+ static VALUE* gc_value_holder = NULL;
496
+ static NM_GC_HOLDER* gc_value_holder_struct = NULL;
497
+ static NM_GC_HOLDER* allocated_pool = NULL; // an object pool for linked list nodes; using pooling is in some cases a substantial performance improvement
498
+
499
+ /**
500
+ * GC Marking function for the values that have been registered.
501
+ */
502
+ static void __nm_mark_value_container(NM_GC_HOLDER* gc_value_holder_struct) {
503
+ if (gc_value_holder_struct && gc_value_holder_struct->start) {
504
+ NM_GC_LL_NODE* curr = gc_value_holder_struct->start;
505
+ while (curr) {
506
+ rb_gc_mark_locations(curr->val, curr->val + curr->n);
507
+ curr = curr->next;
508
+ }
509
+ }
510
+ }
511
+
512
+ /**
513
+ * Initilalizes the linked list of in-use VALUEs if it hasn't been done
514
+ * already.
515
+ */
516
+ static void __nm_initialize_value_container() {
517
+ if (gc_value_holder == NULL) {
518
+ gc_value_holder_struct = NM_ALLOC_NONRUBY(NM_GC_HOLDER);
519
+ allocated_pool = NM_ALLOC_NONRUBY(NM_GC_HOLDER);
520
+ gc_value_holder = NM_ALLOC_NONRUBY(VALUE);
521
+ gc_value_holder_struct->start = NULL;
522
+ allocated_pool->start = NULL;
523
+ *gc_value_holder = Data_Wrap_Struct(cNMatrix_GC_holder, __nm_mark_value_container, NULL, gc_value_holder_struct);
524
+ rb_global_variable(gc_value_holder);
525
+ }
526
+ }
527
+
528
+ /*
529
+ * Register an array of VALUEs to avoid their collection
530
+ * while using them internally.
531
+ */
532
+ void nm_register_values(VALUE* values, size_t n) {
533
+ if (!gc_value_holder_struct)
534
+ __nm_initialize_value_container();
535
+ if (values) {
536
+ NM_GC_LL_NODE* to_insert = NULL;
537
+ if (allocated_pool->start) {
538
+ to_insert = allocated_pool->start;
539
+ allocated_pool->start = to_insert->next;
540
+ } else {
541
+ to_insert = NM_ALLOC_NONRUBY(NM_GC_LL_NODE);
542
+ }
543
+ to_insert->val = values;
544
+ to_insert->n = n;
545
+ to_insert->next = gc_value_holder_struct->start;
546
+ gc_value_holder_struct->start = to_insert;
547
+ }
548
+ }
549
+
550
+ /*
551
+ * Unregister an array of VALUEs with the gc to allow normal
552
+ * garbage collection to occur again.
553
+ */
554
+ void nm_unregister_values(VALUE* values, size_t n) {
555
+ if (values) {
556
+ if (gc_value_holder_struct) {
557
+ NM_GC_LL_NODE* curr = gc_value_holder_struct->start;
558
+ NM_GC_LL_NODE* last = NULL;
559
+ while (curr) {
560
+ if (curr->val == values) {
561
+ if (last) {
562
+ last->next = curr->next;
563
+ } else {
564
+ gc_value_holder_struct->start = curr->next;
565
+ }
566
+ curr->next = allocated_pool->start;
567
+ curr->val = NULL;
568
+ curr->n = 0;
569
+ allocated_pool->start = curr;
570
+ break;
571
+ }
572
+ last = curr;
573
+ curr = curr->next;
574
+ }
575
+ }
576
+ }
577
+ }
578
+
579
+
580
+ /**
581
+ * Register a single VALUE as in use to avoid garbage collection.
582
+ */
583
+ void nm_register_value(VALUE* val) {
584
+ nm_register_values(val, 1);
585
+ }
586
+
587
+ /**
588
+ * Unregister a single VALUE to allow normal garbage collection.
589
+ */
590
+ void nm_unregister_value(VALUE* val) {
591
+ nm_unregister_values(val, 1);
592
+ }
593
+
594
+ /**
595
+ * Removes all instances of a single VALUE in the gc list. This can be
596
+ * dangerous. Primarily used when something is about to be
597
+ * freed and replaced so that and residual registrations won't access after
598
+ * free.
599
+ **/
600
+ void nm_completely_unregister_value(VALUE* val) {
601
+ if (gc_value_holder_struct) {
602
+ NM_GC_LL_NODE* curr = gc_value_holder_struct->start;
603
+ NM_GC_LL_NODE* last = NULL;
604
+ while (curr) {
605
+ if (curr->val == val) {
606
+ if (last) {
607
+ last->next = curr->next;
608
+ } else {
609
+ gc_value_holder_struct->start = curr->next;
610
+ }
611
+ NM_GC_LL_NODE* temp_next = curr->next;
612
+ curr->next = allocated_pool->start;
613
+ curr->val = NULL;
614
+ curr->n = 0;
615
+ allocated_pool->start = curr;
616
+ curr = temp_next;
617
+ } else {
618
+ last = curr;
619
+ curr = curr->next;
620
+ }
621
+ }
622
+ }
623
+ }
624
+
625
+
626
+
627
+ /**
628
+ * Register a STORAGE struct of the supplied stype to avoid garbage collection
629
+ * of its internals.
630
+ *
631
+ * Delegates to the storage-specific methods. They will check dtype and ignore
632
+ * non-rubyobject dtypes, so it's safe to pass any storage in.
633
+ */
634
+ void nm_register_storage(nm::stype_t stype, const STORAGE* storage) {
635
+ STYPE_REGISTER_TABLE(ttable);
636
+ ttable[stype](storage);
637
+ }
638
+
639
+ /**
640
+ * Unregister a STORAGE struct of the supplied stype to allow normal garbage collection
641
+ * of its internals.
642
+ *
643
+ * Delegates to the storage-specific methods. They will check dtype and ignore
644
+ * non-rubyobject dtypes, so it's safe to pass any storage in.
645
+ *
646
+ */
647
+ void nm_unregister_storage(nm::stype_t stype, const STORAGE* storage) {
648
+ STYPE_UNREGISTER_TABLE(ttable);
649
+ ttable[stype](storage);
650
+ }
651
+
652
+ /**
653
+ * Registers an NMATRIX struct to avoid garbage collection of its internals.
654
+ */
655
+ void nm_register_nmatrix(NMATRIX* nmatrix) {
656
+ if (nmatrix)
657
+ nm_register_storage(nmatrix->stype, nmatrix->storage);
658
+ }
659
+
660
+ /**
661
+ * Unregisters an NMATRIX struct to avoid garbage collection of its internals.
662
+ */
663
+ void nm_unregister_nmatrix(NMATRIX* nmatrix) {
664
+ if (nmatrix)
665
+ nm_unregister_storage(nmatrix->stype, nmatrix->storage);
666
+ }
667
+
668
+ /*
669
+ * call-seq:
670
+ * dtype -> Symbol
671
+ *
672
+ * Get the data type (dtype) of a matrix, e.g., :byte, :int8, :int16, :int32,
673
+ * :int64, :float32, :float64, :complex64, :complex128,
674
+ * or :object (the last is a Ruby object).
675
+ */
676
+ static VALUE nm_dtype(VALUE self) {
677
+ ID dtype = rb_intern(DTYPE_NAMES[NM_DTYPE(self)]);
678
+ return ID2SYM(dtype);
679
+ }
680
+
681
+
682
+ /*
683
+ * call-seq:
684
+ * upcast(first_dtype, second_dtype) -> Symbol
685
+ *
686
+ * Given a binary operation between types t1 and t2, what type will be returned?
687
+ *
688
+ * This is a singleton method on NMatrix, e.g., NMatrix.upcast(:int32, :int64)
689
+ */
690
+ static VALUE nm_upcast(VALUE self, VALUE t1, VALUE t2) {
691
+ nm::dtype_t d1 = nm_dtype_from_rbsymbol(t1),
692
+ d2 = nm_dtype_from_rbsymbol(t2);
693
+
694
+ return ID2SYM(rb_intern( DTYPE_NAMES[ Upcast[d1][d2] ] ));
695
+ }
696
+
697
+
698
+ /*
699
+ * call-seq:
700
+ default_value -> ...
701
+ *
702
+ * Get the default value for the matrix. For dense, this is undefined and will return Qnil. For list, it is user-defined.
703
+ * For yale, it's going to be some variation on zero, but may be Qfalse or Qnil.
704
+ */
705
+ static VALUE nm_default_value(VALUE self) {
706
+ switch(NM_STYPE(self)) {
707
+ case nm::YALE_STORE:
708
+ return nm_yale_default_value(self);
709
+ case nm::LIST_STORE:
710
+ return nm_list_default_value(self);
711
+ case nm::DENSE_STORE:
712
+ default:
713
+ return Qnil;
714
+ }
715
+ }
716
+
717
+
718
+ /*
719
+ * call-seq:
720
+ * each_with_indices -> Enumerator
721
+ *
722
+ * Iterate over all entries of any matrix in standard storage order (as with #each), and include the indices.
723
+ */
724
+ static VALUE nm_each_with_indices(VALUE nmatrix) {
725
+ NM_CONSERVATIVE(nm_register_value(&nmatrix));
726
+ VALUE to_return = Qnil;
727
+
728
+ switch(NM_STYPE(nmatrix)) {
729
+ case nm::YALE_STORE:
730
+ to_return = nm_yale_each_with_indices(nmatrix);
731
+ break;
732
+ case nm::DENSE_STORE:
733
+ to_return = nm_dense_each_with_indices(nmatrix);
734
+ break;
735
+ case nm::LIST_STORE:
736
+ to_return = nm_list_each_with_indices(nmatrix, false);
737
+ break;
738
+ default:
739
+ NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
740
+ rb_raise(nm_eDataTypeError, "Not a proper storage type");
741
+ }
742
+
743
+ NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
744
+ return to_return;
745
+ }
746
+
747
+ /*
748
+ * call-seq:
749
+ * each_stored_with_indices -> Enumerator
750
+ *
751
+ * Iterate over the stored entries of any matrix. For dense and yale, this iterates over non-zero
752
+ * entries; for list, this iterates over non-default entries. Yields dim+1 values for each entry:
753
+ * i, j, ..., and the entry itself.
754
+ */
755
+ static VALUE nm_each_stored_with_indices(VALUE nmatrix) {
756
+ NM_CONSERVATIVE(nm_register_value(&nmatrix));
757
+ VALUE to_return = Qnil;
758
+
759
+ switch(NM_STYPE(nmatrix)) {
760
+ case nm::YALE_STORE:
761
+ to_return = nm_yale_each_stored_with_indices(nmatrix);
762
+ break;
763
+ case nm::DENSE_STORE:
764
+ to_return = nm_dense_each_with_indices(nmatrix);
765
+ break;
766
+ case nm::LIST_STORE:
767
+ to_return = nm_list_each_with_indices(nmatrix, true);
768
+ break;
769
+ default:
770
+ NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
771
+ rb_raise(nm_eDataTypeError, "Not a proper storage type");
772
+ }
773
+
774
+ NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
775
+ return to_return;
776
+ }
777
+
778
+
779
+ /*
780
+ * call-seq:
781
+ * map_stored -> Enumerator
782
+ *
783
+ * Iterate over the stored entries of any matrix. For dense and yale, this iterates over non-zero
784
+ * entries; for list, this iterates over non-default entries. Yields dim+1 values for each entry:
785
+ * i, j, ..., and the entry itself.
786
+ */
787
+ static VALUE nm_map_stored(VALUE nmatrix) {
788
+ NM_CONSERVATIVE(nm_register_value(&nmatrix));
789
+ VALUE to_return = Qnil;
790
+
791
+ switch(NM_STYPE(nmatrix)) {
792
+ case nm::YALE_STORE:
793
+ to_return = nm_yale_map_stored(nmatrix);
794
+ break;
795
+ case nm::DENSE_STORE:
796
+ to_return = nm_dense_map(nmatrix);
797
+ break;
798
+ case nm::LIST_STORE:
799
+ to_return = nm_list_map_stored(nmatrix, Qnil);
800
+ break;
801
+ default:
802
+ NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
803
+ rb_raise(nm_eDataTypeError, "Not a proper storage type");
804
+ }
805
+
806
+ NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
807
+ return to_return;
808
+ }
809
+
810
+ /*
811
+ * call-seq:
812
+ * each_ordered_stored_with_indices -> Enumerator
813
+ *
814
+ * Very similar to #each_stored_with_indices. The key difference is that it enforces matrix ordering rather
815
+ * than storage ordering, which only matters if your matrix is Yale.
816
+ */
817
+ static VALUE nm_each_ordered_stored_with_indices(VALUE nmatrix) {
818
+ NM_CONSERVATIVE(nm_register_value(&nmatrix));
819
+ VALUE to_return = Qnil;
820
+
821
+ switch(NM_STYPE(nmatrix)) {
822
+ case nm::YALE_STORE:
823
+ to_return = nm_yale_each_ordered_stored_with_indices(nmatrix);
824
+ break;
825
+ case nm::DENSE_STORE:
826
+ to_return = nm_dense_each_with_indices(nmatrix);
827
+ break;
828
+ case nm::LIST_STORE:
829
+ to_return = nm_list_each_with_indices(nmatrix, true);
830
+ break;
831
+ default:
832
+ NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
833
+ rb_raise(nm_eDataTypeError, "Not a proper storage type");
834
+ }
835
+
836
+ NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
837
+ return to_return;
838
+ }
839
+
840
+
841
+ /*
842
+ * Equality operator. Returns a single true or false value indicating whether
843
+ * the matrices are equivalent.
844
+ *
845
+ * For elementwise, use =~ instead.
846
+ *
847
+ * This method will raise an exception if dimensions do not match.
848
+ *
849
+ * When stypes differ, this function calls a protected Ruby method.
850
+ */
851
+ static VALUE nm_eqeq(VALUE left, VALUE right) {
852
+ NM_CONSERVATIVE(nm_register_value(&left));
853
+ NM_CONSERVATIVE(nm_register_value(&right));
854
+
855
+ NMATRIX *l, *r;
856
+
857
+ CheckNMatrixType(left);
858
+ CheckNMatrixType(right);
859
+
860
+ UnwrapNMatrix(left, l);
861
+ UnwrapNMatrix(right, r);
862
+
863
+ bool result = false;
864
+
865
+ // Check that the shapes match before going any further.
866
+ if (l->storage->dim != r->storage->dim) {
867
+ NM_CONSERVATIVE(nm_unregister_value(&left));
868
+ NM_CONSERVATIVE(nm_unregister_value(&right));
869
+ rb_raise(nm_eShapeError, "cannot compare matrices with different dimension");
870
+ }
871
+
872
+ size_t dim = l->storage->dim;
873
+ for (size_t i=0; i<dim; i++) {
874
+ if (l->storage->shape[i] != r->storage->shape[i]) {
875
+ NM_CONSERVATIVE(nm_unregister_value(&left));
876
+ NM_CONSERVATIVE(nm_unregister_value(&right));
877
+ rb_raise(nm_eShapeError, "cannot compare matrices with different shapes");
878
+ }
879
+ }
880
+
881
+ if (l->stype != r->stype) { // DIFFERENT STYPES
882
+
883
+ if (l->stype == nm::DENSE_STORE)
884
+ result = rb_funcall(left, rb_intern("dense_eql_sparse?"), 1, right);
885
+ else if (r->stype == nm::DENSE_STORE)
886
+ result = rb_funcall(right, rb_intern("dense_eql_sparse?"), 1, left);
887
+ else
888
+ result = rb_funcall(left, rb_intern("sparse_eql_sparse?"), 1, right);
889
+
890
+ } else {
891
+
892
+ switch(l->stype) { // SAME STYPES
893
+ case nm::DENSE_STORE:
894
+ result = nm_dense_storage_eqeq(l->storage, r->storage);
895
+ break;
896
+ case nm::LIST_STORE:
897
+ result = nm_list_storage_eqeq(l->storage, r->storage);
898
+ break;
899
+ case nm::YALE_STORE:
900
+ result = nm_yale_storage_eqeq(l->storage, r->storage);
901
+ break;
902
+ }
903
+ }
904
+
905
+ NM_CONSERVATIVE(nm_unregister_value(&left));
906
+ NM_CONSERVATIVE(nm_unregister_value(&right));
907
+
908
+ return result ? Qtrue : Qfalse;
909
+ }
910
+
911
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(ADD, add)
912
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(SUB, subtract)
913
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(MUL, multiply)
914
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(DIV, divide)
915
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(POW, power)
916
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(MOD, mod)
917
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(EQEQ, eqeq)
918
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(NEQ, neq)
919
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(LEQ, leq)
920
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(GEQ, geq)
921
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(LT, lt)
922
+ DEF_ELEMENTWISE_RUBY_ACCESSOR(GT, gt)
923
+
924
+ DEF_UNARY_RUBY_ACCESSOR(SIN, sin)
925
+ DEF_UNARY_RUBY_ACCESSOR(COS, cos)
926
+ DEF_UNARY_RUBY_ACCESSOR(TAN, tan)
927
+ DEF_UNARY_RUBY_ACCESSOR(ASIN, asin)
928
+ DEF_UNARY_RUBY_ACCESSOR(ACOS, acos)
929
+ DEF_UNARY_RUBY_ACCESSOR(ATAN, atan)
930
+ DEF_UNARY_RUBY_ACCESSOR(SINH, sinh)
931
+ DEF_UNARY_RUBY_ACCESSOR(COSH, cosh)
932
+ DEF_UNARY_RUBY_ACCESSOR(TANH, tanh)
933
+ DEF_UNARY_RUBY_ACCESSOR(ASINH, asinh)
934
+ DEF_UNARY_RUBY_ACCESSOR(ACOSH, acosh)
935
+ DEF_UNARY_RUBY_ACCESSOR(ATANH, atanh)
936
+ DEF_UNARY_RUBY_ACCESSOR(EXP, exp)
937
+ DEF_UNARY_RUBY_ACCESSOR(LOG2, log2)
938
+ DEF_UNARY_RUBY_ACCESSOR(LOG10, log10)
939
+ DEF_UNARY_RUBY_ACCESSOR(SQRT, sqrt)
940
+ DEF_UNARY_RUBY_ACCESSOR(ERF, erf)
941
+ DEF_UNARY_RUBY_ACCESSOR(ERFC, erfc)
942
+ DEF_UNARY_RUBY_ACCESSOR(CBRT, cbrt)
943
+ DEF_UNARY_RUBY_ACCESSOR(GAMMA, gamma)
944
+ DEF_UNARY_RUBY_ACCESSOR(NEGATE, negate)
945
+ DEF_UNARY_RUBY_ACCESSOR(FLOOR, floor)
946
+ DEF_UNARY_RUBY_ACCESSOR(CEIL, ceil)
947
+
948
+ DEF_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(ATAN2, atan2)
949
+ DEF_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(LDEXP, ldexp)
950
+ DEF_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(HYPOT, hypot)
951
+
952
+ static VALUE nm_unary_log(int argc, VALUE* argv, VALUE self) {
953
+ NM_CONSERVATIVE(nm_register_values(argv, argc));
954
+ const double default_log_base = exp(1.0);
955
+ NMATRIX* left;
956
+ UnwrapNMatrix(self, left);
957
+ std::string sym;
958
+
959
+ switch(left->stype) {
960
+ case nm::DENSE_STORE:
961
+ sym = "__dense_unary_log__";
962
+ break;
963
+ case nm::YALE_STORE:
964
+ sym = "__yale_unary_log__";
965
+ break;
966
+ case nm::LIST_STORE:
967
+ sym = "__list_unary_log__";
968
+ break;
969
+ }
970
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
971
+ if (argc > 0) { //supplied a base
972
+ return rb_funcall(self, rb_intern(sym.c_str()), 1, argv[0]);
973
+ }
974
+ return rb_funcall(self, rb_intern(sym.c_str()), 1, nm::RubyObject(default_log_base).rval);
975
+ }
976
+
977
+ static VALUE nm_unary_round(int argc, VALUE* argv, VALUE self) {
978
+ NM_CONSERVATIVE(nm_register_values(argv, argc));
979
+ const int default_precision = 0;
980
+ NMATRIX* left;
981
+ UnwrapNMatrix(self, left);
982
+ std::string sym;
983
+
984
+ switch(left->stype) {
985
+ case nm::DENSE_STORE:
986
+ sym = "__dense_unary_round__";
987
+ break;
988
+ case nm::YALE_STORE:
989
+ sym = "__yale_unary_round__";
990
+ break;
991
+ case nm::LIST_STORE:
992
+ sym = "__list_unary_round__";
993
+ break;
994
+ }
995
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
996
+ if (argc > 0) { //supplied precision
997
+ return rb_funcall(self, rb_intern(sym.c_str()), 1, argv[0]);
998
+ }
999
+ return rb_funcall(self, rb_intern(sym.c_str()), 1, nm::RubyObject(default_precision).rval);
1000
+ }
1001
+
1002
+ //DEF_ELEMENTWISE_RUBY_ACCESSOR(ATAN2, atan2)
1003
+ //DEF_ELEMENTWISE_RUBY_ACCESSOR(LDEXP, ldexp)
1004
+ //DEF_ELEMENTWISE_RUBY_ACCESSOR(HYPOT, hypot)
1005
+
1006
+ /*
1007
+ * call-seq:
1008
+ * hermitian? -> Boolean
1009
+ *
1010
+ * Is this matrix hermitian?
1011
+ *
1012
+ * Definition: http://en.wikipedia.org/wiki/Hermitian_matrix
1013
+ *
1014
+ * For non-complex matrices, this function should return the same result as symmetric?.
1015
+ */
1016
+ static VALUE nm_hermitian(VALUE self) {
1017
+ return is_symmetric(self, true);
1018
+ }
1019
+
1020
+
1021
+ /*
1022
+ * call-seq:
1023
+ * complex_conjugate_bang -> NMatrix
1024
+ *
1025
+ * Transform the matrix (in-place) to its complex conjugate. Only works on complex matrices.
1026
+ *
1027
+ * Bang should imply that no copy is being made, even temporarily.
1028
+ */
1029
+ static VALUE nm_complex_conjugate_bang(VALUE self) {
1030
+
1031
+ NMATRIX* m;
1032
+ void* elem;
1033
+ size_t size, p;
1034
+
1035
+ UnwrapNMatrix(self, m);
1036
+
1037
+ if (m->stype == nm::DENSE_STORE) {
1038
+
1039
+ size = nm_storage_count_max_elements(NM_STORAGE(self));
1040
+ elem = NM_STORAGE_DENSE(self)->elements;
1041
+
1042
+ } else if (m->stype == nm::YALE_STORE) {
1043
+
1044
+ size = nm_yale_storage_get_size(NM_STORAGE_YALE(self));
1045
+ elem = NM_STORAGE_YALE(self)->a;
1046
+
1047
+ } else {
1048
+ rb_raise(rb_eNotImpError, "please cast to yale or dense (complex) first");
1049
+ }
1050
+
1051
+ // Walk through and negate the imaginary component
1052
+ if (NM_DTYPE(self) == nm::COMPLEX64) {
1053
+
1054
+ for (p = 0; p < size; ++p) {
1055
+ reinterpret_cast<nm::Complex64*>(elem)[p].i = -reinterpret_cast<nm::Complex64*>(elem)[p].i;
1056
+ }
1057
+
1058
+ } else if (NM_DTYPE(self) == nm::COMPLEX128) {
1059
+
1060
+ for (p = 0; p < size; ++p) {
1061
+ reinterpret_cast<nm::Complex128*>(elem)[p].i = -reinterpret_cast<nm::Complex128*>(elem)[p].i;
1062
+ }
1063
+
1064
+ }
1065
+ return self;
1066
+ }
1067
+
1068
+ /*
1069
+ * call-seq:
1070
+ * __reshape!__ -> NMatrix
1071
+ *
1072
+ * Reshapes the matrix (in-place) to the desired shape. Note that this function does not do a resize; the product of
1073
+ * the new and old shapes' components must be equal.
1074
+ *
1075
+ */
1076
+ static VALUE nm_reshape_bang(VALUE self, VALUE arg){
1077
+ NMATRIX* m;
1078
+ UnwrapNMatrix(self, m);
1079
+ if(m->stype == nm::DENSE_STORE){
1080
+ DENSE_STORAGE* s = NM_STORAGE_DENSE(self);
1081
+ VALUE shape_ary = arg;
1082
+ size_t dim;
1083
+ size_t size = nm_storage_count_max_elements(s);
1084
+ size_t new_size = 1;
1085
+ size_t* shape = interpret_shape(shape_ary, &dim);
1086
+ for (size_t index = 0; index < dim; ++index){
1087
+ new_size *= shape[index];}
1088
+
1089
+ if (size == new_size){
1090
+ s->shape = shape;
1091
+ s->dim = dim;
1092
+ NM_FREE(s->offset);
1093
+ s->offset = NM_ALLOC_N(size_t, dim);
1094
+ memset(s->offset, 0, sizeof(size_t)*dim);
1095
+ size_t i, j;
1096
+ size_t* stride = NM_ALLOC_N(size_t, dim);
1097
+ for (i = 0; i < dim; ++i) {
1098
+ stride[i] = 1;
1099
+ for (j = i+1; j < dim; ++j) {
1100
+ stride[i] *= shape[j];
1101
+ }
1102
+ }
1103
+ NM_FREE(s->stride);
1104
+ s->stride = stride;
1105
+ return self;
1106
+ }
1107
+ else
1108
+ rb_raise(rb_eArgError, "reshape cannot resize; size of new and old matrices must match");
1109
+ }
1110
+ else {
1111
+ rb_raise(rb_eNotImpError, "reshape in place only for dense stype");
1112
+ }
1113
+ }
1114
+
1115
+ /*
1116
+ * Helper function for creating a matrix. You have to create the storage and pass it in, but you don't
1117
+ * need to worry about deleting it.
1118
+ */
1119
+ NMATRIX* nm_create(nm::stype_t stype, STORAGE* storage) {
1120
+ nm_register_storage(stype, storage);
1121
+ NMATRIX* mat = NM_ALLOC(NMATRIX);
1122
+
1123
+ mat->stype = stype;
1124
+ mat->storage = storage;
1125
+
1126
+ nm_unregister_storage(stype, storage);
1127
+ return mat;
1128
+ }
1129
+
1130
+ /*
1131
+ * @see nm_init
1132
+ */
1133
+ static VALUE nm_init_new_version(int argc, VALUE* argv, VALUE self) {
1134
+ NM_CONSERVATIVE(nm_register_values(argv, argc));
1135
+ NM_CONSERVATIVE(nm_register_value(&self));
1136
+ VALUE shape_ary, initial_ary, hash;
1137
+ //VALUE shape_ary, default_val, capacity, initial_ary, dtype_sym, stype_sym;
1138
+ // Mandatory args: shape, dtype, stype
1139
+ rb_scan_args(argc, argv, "11:", &shape_ary, &initial_ary, &hash); // &stype_sym, &dtype_sym, &default_val, &capacity);
1140
+
1141
+ NM_CONSERVATIVE(nm_register_value(&shape_ary));
1142
+ NM_CONSERVATIVE(nm_register_value(&initial_ary));
1143
+ NM_CONSERVATIVE(nm_register_value(&hash));
1144
+ // Get the shape.
1145
+ size_t dim;
1146
+ size_t* shape = interpret_shape(shape_ary, &dim);
1147
+ void* init;
1148
+ void* v = NULL;
1149
+ size_t v_size = 0;
1150
+
1151
+ nm::stype_t stype = nm::DENSE_STORE;
1152
+ nm::dtype_t dtype = nm::RUBYOBJ;
1153
+ VALUE dtype_sym = Qnil, stype_sym = Qnil, default_val_num = Qnil, capacity_num = Qnil;
1154
+ size_t capacity = 0;
1155
+ if (!NIL_P(hash)) {
1156
+ dtype_sym = rb_hash_aref(hash, ID2SYM(nm_rb_dtype));
1157
+ stype_sym = rb_hash_aref(hash, ID2SYM(nm_rb_stype));
1158
+ capacity_num = rb_hash_aref(hash, ID2SYM(nm_rb_capacity));
1159
+ NM_CONSERVATIVE(nm_register_value(&capacity_num));
1160
+ default_val_num = rb_hash_aref(hash, ID2SYM(nm_rb_default));
1161
+ NM_CONSERVATIVE(nm_register_value(&default_val_num));
1162
+ }
1163
+
1164
+ // stype ||= :dense
1165
+ stype = !NIL_P(stype_sym) ? nm_stype_from_rbsymbol(stype_sym) : nm::DENSE_STORE;
1166
+
1167
+ // dtype ||= h[:dtype] || guess_dtype(initial_ary) || :object
1168
+ if (NIL_P(initial_ary) && NIL_P(dtype_sym))
1169
+ dtype = nm::RUBYOBJ;
1170
+ else if (NIL_P(dtype_sym))
1171
+ dtype = nm_dtype_guess(initial_ary);
1172
+ else
1173
+ dtype = nm_dtype_from_rbsymbol(dtype_sym);
1174
+
1175
+ // if stype != :dense
1176
+ // if initial_ary.nil?
1177
+ // init = h[:default] || 0
1178
+ // elsif initial_ary.is_a?(Array)
1179
+ // init = initial_ary.size > 1 ? (h[:default] || 0) : initial_ary[0]
1180
+ // else
1181
+ // init = initial_ary # not an array, just a value
1182
+ // end
1183
+ // end
1184
+ if (stype != nm::DENSE_STORE) {
1185
+ if (!NIL_P(default_val_num))
1186
+ init = rubyobj_to_cval(default_val_num, dtype);
1187
+ else if (NIL_P(initial_ary))
1188
+ init = NULL;
1189
+ else if (RB_TYPE_P(initial_ary, T_ARRAY))
1190
+ init = RARRAY_LEN(initial_ary) == 1 ? rubyobj_to_cval(rb_ary_entry(initial_ary, 0), dtype) : NULL;
1191
+ else
1192
+ init = rubyobj_to_cval(initial_ary, dtype);
1193
+
1194
+ if (dtype == nm::RUBYOBJ) {
1195
+ nm_register_values(reinterpret_cast<VALUE*>(init), 1);
1196
+ }
1197
+ }
1198
+
1199
+ // capacity = h[:capacity] || 0
1200
+ if (stype == nm::YALE_STORE) {
1201
+ if (!NIL_P(capacity_num)) capacity = FIX2INT(capacity_num);
1202
+ }
1203
+
1204
+ if (!NIL_P(initial_ary)) {
1205
+
1206
+ if (RB_TYPE_P(initial_ary, T_ARRAY)) v_size = RARRAY_LEN(initial_ary);
1207
+ else v_size = 1;
1208
+
1209
+ v = interpret_initial_value(initial_ary, dtype);
1210
+
1211
+ if (dtype == nm::RUBYOBJ) {
1212
+ nm_register_values(reinterpret_cast<VALUE*>(v), v_size);
1213
+ }
1214
+ }
1215
+
1216
+ // :object matrices MUST be initialized.
1217
+ else if (stype == nm::DENSE_STORE && dtype == nm::RUBYOBJ) {
1218
+ // Pretend [nil] was passed for RUBYOBJ.
1219
+ v = NM_ALLOC(VALUE);
1220
+ *(VALUE*)v = Qnil;
1221
+
1222
+ v_size = 1;
1223
+
1224
+ }
1225
+
1226
+ NMATRIX* nmatrix;
1227
+ UnwrapNMatrix(self, nmatrix);
1228
+
1229
+ nmatrix->stype = stype;
1230
+
1231
+ switch (stype) {
1232
+ case nm::DENSE_STORE:
1233
+ nmatrix->storage = (STORAGE*)nm_dense_storage_create(dtype, shape, dim, v, v_size);
1234
+ break;
1235
+
1236
+ case nm::LIST_STORE:
1237
+ nmatrix->storage = (STORAGE*)nm_list_storage_create(dtype, shape, dim, init);
1238
+ break;
1239
+
1240
+ case nm::YALE_STORE:
1241
+ nmatrix->storage = (STORAGE*)nm_yale_storage_create(dtype, shape, dim, capacity);
1242
+ nm_yale_storage_init((YALE_STORAGE*)(nmatrix->storage), init);
1243
+ break;
1244
+ }
1245
+
1246
+ nm_register_storage(stype, nmatrix->storage);
1247
+
1248
+ // If we're not creating a dense, and an initial array was provided, use that and multi-slice-set
1249
+ // to set the contents of the matrix right now.
1250
+ if (stype != nm::DENSE_STORE && v_size > 1) {
1251
+ VALUE* slice_argv = NM_ALLOCA_N(VALUE, dim);
1252
+ nm_register_values(slice_argv, dim);
1253
+ size_t* tmp_shape = NM_ALLOC_N(size_t, dim);
1254
+ for (size_t m = 0; m < dim; ++m) {
1255
+ slice_argv[m] = ID2SYM(nm_rb_mul); // :* -- full range
1256
+ tmp_shape[m] = shape[m];
1257
+ }
1258
+
1259
+ SLICE slice_s;
1260
+ SLICE* slice = &slice_s;
1261
+ slice->coords = NM_ALLOCA_N(size_t, dim);
1262
+ slice->lengths = NM_ALLOCA_N(size_t, dim);
1263
+ init_slice_no_alloc(slice, dim, dim, slice_argv, shape);
1264
+
1265
+ // Create a temporary dense matrix and use it to do a slice assignment on self.
1266
+ NMATRIX* tmp = nm_create(nm::DENSE_STORE, (STORAGE*)nm_dense_storage_create(dtype, tmp_shape, dim, v, v_size));
1267
+ nm_register_nmatrix(tmp);
1268
+ VALUE rb_tmp = Data_Wrap_Struct(CLASS_OF(self), nm_mark, nm_delete, tmp);
1269
+ nm_unregister_nmatrix(tmp);
1270
+ nm_register_value(&rb_tmp);
1271
+ if (stype == nm::YALE_STORE) nm_yale_storage_set(self, slice, rb_tmp);
1272
+ else nm_list_storage_set(self, slice, rb_tmp);
1273
+
1274
+ // We need to free v if it's not the same size as tmp -- because tmp will have made a copy instead.
1275
+ //if (nm_storage_count_max_elements(tmp->storage) != v_size)
1276
+ // NM_FREE(v);
1277
+
1278
+ // nm_delete(tmp); // This seems to enrage the garbage collector (because rb_tmp is still available). It'd be better if we could force it to free immediately, but no sweat.
1279
+
1280
+ nm_unregister_value(&rb_tmp);
1281
+ nm_unregister_values(slice_argv, dim);
1282
+ }
1283
+
1284
+ if (!NIL_P(initial_ary) && dtype == nm::RUBYOBJ) {
1285
+ nm_unregister_values(reinterpret_cast<VALUE*>(v), v_size);
1286
+ }
1287
+
1288
+ if (stype != nm::DENSE_STORE && dtype == nm::RUBYOBJ) {
1289
+ nm_unregister_values(reinterpret_cast<VALUE*>(init), 1);
1290
+ }
1291
+
1292
+ if (!NIL_P(hash)) {
1293
+ NM_CONSERVATIVE(nm_unregister_value(&capacity_num));
1294
+ NM_CONSERVATIVE(nm_unregister_value(&default_val_num));
1295
+ }
1296
+
1297
+ NM_CONSERVATIVE(nm_unregister_value(&shape_ary));
1298
+ NM_CONSERVATIVE(nm_unregister_value(&initial_ary));
1299
+ NM_CONSERVATIVE(nm_unregister_value(&hash));
1300
+
1301
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1302
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
1303
+ nm_unregister_storage(stype, nmatrix->storage);
1304
+
1305
+ return self;
1306
+ }
1307
+
1308
+ /*
1309
+ * call-seq:
1310
+ * new(shape) -> NMatrix
1311
+ * new(shape, initial_value) -> NMatrix
1312
+ * new(shape, initial_array) -> NMatrix
1313
+ * new(shape, initial_value, options) -> NMatrix
1314
+ * new(shape, initial_array, options) -> NMatrix
1315
+ *
1316
+ * Create a new NMatrix.
1317
+ *
1318
+ * The only mandatory argument is shape, which may be a positive integer or an array of positive integers.
1319
+ *
1320
+ * It is recommended that you supply an initialization value or array of values. Without one, Yale and List matrices will
1321
+ * be initialized to 0; and dense matrices will be undefined.
1322
+ *
1323
+ * Additional options may be provided using keyword arguments. The keywords are +:dtype, +:stype+, +:capacity+, and
1324
+ * +:default+. Only Yale uses a capacity argument, which is used to reserve the initial size of its storage vectors.
1325
+ * List and Yale both accept a default value (which itself defaults to 0). This default is taken from the initial value
1326
+ * if such a value is given; it is more likely to be required when an initial array is provided.
1327
+ *
1328
+ * The storage type, or stype, is used to specify whether we want a +:dense+, +:list+, or +:yale+ matrix; dense is the
1329
+ * default.
1330
+ *
1331
+ * The data type, or dtype, can be one of: :byte, :int8, :int16, :int32, :int64, :float32, :float64, :complex64,
1332
+ * :complex128, or :object. The constructor will attempt to guess it from the initial value/array/default
1333
+ * provided, if any. Otherwise, the default is :object, which stores any type of Ruby object.
1334
+ *
1335
+ * In addition to the above, there is a legacy constructor from the alpha version. To use that version, you must be
1336
+ * providing exactly four arguments. It is now deprecated.
1337
+ *
1338
+ * There is one additional constructor for advanced users, which takes seven arguments and is only for creating Yale
1339
+ * matrices with known IA, JA, and A arrays. This is used primarily internally for IO, e.g., reading Matlab matrices,
1340
+ * which are stored in old Yale (not our Yale) format. But be careful; there are no overflow warnings. All of these
1341
+ * constructors are defined for power-users. Everyone else should probably resort to the shortcut functions defined in
1342
+ * shortcuts.rb.
1343
+ */
1344
+ static VALUE nm_init(int argc, VALUE* argv, VALUE nm) {
1345
+ NM_CONSERVATIVE(nm_register_value(&nm));
1346
+ NM_CONSERVATIVE(nm_register_values(argv, argc));
1347
+
1348
+ if (argc <= 3) { // Call the new constructor unless all four arguments are given (or the 7-arg version is given)
1349
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
1350
+ NM_CONSERVATIVE(nm_unregister_value(&nm));
1351
+ return nm_init_new_version(argc, argv, nm);
1352
+ }
1353
+
1354
+ /* First, determine stype (dense by default) */
1355
+ nm::stype_t stype;
1356
+ size_t offset = 0;
1357
+
1358
+ if (!SYMBOL_P(argv[0]) && !RB_TYPE_P(argv[0], T_STRING)) {
1359
+ stype = nm::DENSE_STORE;
1360
+
1361
+ } else {
1362
+ // 0: String or Symbol
1363
+ stype = interpret_stype(argv[0]);
1364
+ offset = 1;
1365
+ }
1366
+
1367
+ // If there are 7 arguments and Yale, refer to a different init function with fewer sanity checks.
1368
+ if (argc == 7) {
1369
+ if (stype == nm::YALE_STORE) {
1370
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
1371
+ NM_CONSERVATIVE(nm_unregister_value(&nm));
1372
+ return nm_init_yale_from_old_yale(argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], nm);
1373
+
1374
+ } else {
1375
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
1376
+ NM_CONSERVATIVE(nm_unregister_value(&nm));
1377
+ rb_raise(rb_eArgError, "Expected 2-4 arguments (or 7 for internal Yale creation)");
1378
+ }
1379
+ }
1380
+
1381
+ // 1: Array or Fixnum
1382
+ size_t dim;
1383
+ size_t* shape = interpret_shape(argv[offset], &dim);
1384
+
1385
+ // 2-3: dtype
1386
+ nm::dtype_t dtype = interpret_dtype(argc-1-offset, argv+offset+1, stype);
1387
+
1388
+ size_t init_cap = 0, init_val_len = 0;
1389
+ void* init_val = NULL;
1390
+ if (!SYMBOL_P(argv[1+offset]) || RB_TYPE_P(argv[1+offset], T_ARRAY)) {
1391
+ // Initial value provided (could also be initial capacity, if yale).
1392
+
1393
+ if (stype == nm::YALE_STORE && NM_RUBYVAL_IS_NUMERIC(argv[1+offset])) {
1394
+ init_cap = FIX2UINT(argv[1+offset]);
1395
+
1396
+ } else {
1397
+ // 4: initial value / dtype
1398
+ init_val = interpret_initial_value(argv[1+offset], dtype);
1399
+
1400
+ if (RB_TYPE_P(argv[1+offset], T_ARRAY)) init_val_len = RARRAY_LEN(argv[1+offset]);
1401
+ else init_val_len = 1;
1402
+ }
1403
+
1404
+ } else {
1405
+ // DType is RUBYOBJ.
1406
+
1407
+ if (stype == nm::DENSE_STORE) {
1408
+ /*
1409
+ * No need to initialize dense with any kind of default value unless it's
1410
+ * an RUBYOBJ matrix.
1411
+ */
1412
+ if (dtype == nm::RUBYOBJ) {
1413
+ // Pretend [nil] was passed for RUBYOBJ.
1414
+ init_val = NM_ALLOC(VALUE);
1415
+ *(VALUE*)init_val = Qnil;
1416
+
1417
+ init_val_len = 1;
1418
+
1419
+ } else {
1420
+ init_val = NULL;
1421
+ }
1422
+ } else if (stype == nm::LIST_STORE) {
1423
+ init_val = NM_ALLOC_N(char, DTYPE_SIZES[dtype]);
1424
+ std::memset(init_val, 0, DTYPE_SIZES[dtype]);
1425
+ }
1426
+ }
1427
+
1428
+ if (dtype == nm::RUBYOBJ) {
1429
+ nm_register_values(reinterpret_cast<VALUE*>(init_val), init_val_len);
1430
+ }
1431
+
1432
+ // TODO: Update to allow an array as the initial value.
1433
+ NMATRIX* nmatrix;
1434
+ UnwrapNMatrix(nm, nmatrix);
1435
+
1436
+ nmatrix->stype = stype;
1437
+
1438
+ switch (stype) {
1439
+ case nm::DENSE_STORE:
1440
+ nmatrix->storage = (STORAGE*)nm_dense_storage_create(dtype, shape, dim, init_val, init_val_len);
1441
+ break;
1442
+
1443
+ case nm::LIST_STORE:
1444
+ nmatrix->storage = (STORAGE*)nm_list_storage_create(dtype, shape, dim, init_val);
1445
+ break;
1446
+
1447
+ case nm::YALE_STORE:
1448
+ nmatrix->storage = (STORAGE*)nm_yale_storage_create(dtype, shape, dim, init_cap);
1449
+ nm_yale_storage_init((YALE_STORAGE*)(nmatrix->storage), NULL);
1450
+ break;
1451
+ }
1452
+
1453
+ if (dtype == nm::RUBYOBJ) {
1454
+ nm_unregister_values(reinterpret_cast<VALUE*>(init_val), init_val_len);
1455
+ }
1456
+
1457
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
1458
+ NM_CONSERVATIVE(nm_unregister_value(&nm));
1459
+
1460
+ return nm;
1461
+ }
1462
+
1463
+
1464
+ /*
1465
+ * Helper for nm_cast_with_types which uses the C types instead of the Ruby objects.
1466
+ * Called by nm_cast_with_types.
1467
+ */
1468
+ NMATRIX* nm_cast_with_ctype_args(NMATRIX* self, nm::stype_t new_stype, nm::dtype_t new_dtype, void* init_ptr) {
1469
+
1470
+ nm_register_nmatrix(self);
1471
+
1472
+ NMATRIX* lhs = NM_ALLOC(NMATRIX);
1473
+ lhs->stype = new_stype;
1474
+
1475
+ // Copy the storage
1476
+ CAST_TABLE(cast_copy);
1477
+ lhs->storage = cast_copy[lhs->stype][self->stype](self->storage, new_dtype, init_ptr);
1478
+
1479
+ nm_unregister_nmatrix(self);
1480
+
1481
+ return lhs;
1482
+ }
1483
+
1484
+ /*
1485
+ * Cast NMatrix with given new_stype and new_dtype. Called by nm_cast.
1486
+ */
1487
+ VALUE nm_cast_with_types(VALUE self, nm::stype_t new_stype, nm::dtype_t new_dtype,
1488
+ void* init_ptr) {
1489
+ NMATRIX *rhs;
1490
+
1491
+ UnwrapNMatrix( self, rhs );
1492
+
1493
+ NMATRIX* m = nm_cast_with_ctype_args(rhs, new_stype, new_dtype, init_ptr);
1494
+ nm_register_nmatrix(m);
1495
+
1496
+ VALUE to_return = Data_Wrap_Struct(CLASS_OF(self), nm_mark, nm_delete, m);
1497
+
1498
+ nm_unregister_nmatrix(m);
1499
+ return to_return;
1500
+ }
1501
+
1502
+ /*
1503
+ * call-seq:
1504
+ * cast_full(stype) -> NMatrix
1505
+ * cast_full(stype, dtype, sparse_basis) -> NMatrix
1506
+ *
1507
+ * Copy constructor for changing dtypes and stypes.
1508
+ */
1509
+ VALUE nm_cast(VALUE self, VALUE new_stype_symbol, VALUE new_dtype_symbol, VALUE init) {
1510
+ NM_CONSERVATIVE(nm_register_value(&self));
1511
+ NM_CONSERVATIVE(nm_register_value(&init));
1512
+
1513
+ nm::dtype_t new_dtype = nm_dtype_from_rbsymbol(new_dtype_symbol);
1514
+ nm::stype_t new_stype = nm_stype_from_rbsymbol(new_stype_symbol);
1515
+
1516
+ CheckNMatrixType(self);
1517
+ void* init_ptr = NM_ALLOCA_N(char, DTYPE_SIZES[new_dtype]);
1518
+ rubyval_to_cval(init, new_dtype, init_ptr);
1519
+
1520
+ VALUE to_return = nm_cast_with_types(self, new_stype, new_dtype, init_ptr);
1521
+
1522
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1523
+ NM_CONSERVATIVE(nm_unregister_value(&init));
1524
+ return to_return;
1525
+
1526
+ }
1527
+
1528
+ /*
1529
+ * Copy constructor for transposing.
1530
+ */
1531
+ static VALUE nm_init_transposed(VALUE self) {
1532
+ NM_CONSERVATIVE(nm_register_value(&self));
1533
+
1534
+ static STORAGE* (*storage_copy_transposed[nm::NUM_STYPES])(const STORAGE* rhs_base) = {
1535
+ nm_dense_storage_copy_transposed,
1536
+ nm_list_storage_copy_transposed,
1537
+ nm_yale_storage_copy_transposed
1538
+ };
1539
+
1540
+ NMATRIX* lhs = nm_create( NM_STYPE(self),
1541
+ storage_copy_transposed[NM_STYPE(self)]( NM_STORAGE(self) )
1542
+ );
1543
+ nm_register_nmatrix(lhs);
1544
+ VALUE to_return = Data_Wrap_Struct(CLASS_OF(self), nm_mark, nm_delete, lhs);
1545
+
1546
+ nm_unregister_nmatrix(lhs);
1547
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1548
+ return to_return;
1549
+ }
1550
+
1551
+ /*
1552
+ * Copy constructor for no change of dtype or stype (used for #initialize_copy hook).
1553
+ */
1554
+ static VALUE nm_init_copy(VALUE copy, VALUE original) {
1555
+ NM_CONSERVATIVE(nm_register_value(&copy));
1556
+ NM_CONSERVATIVE(nm_register_value(&original));
1557
+
1558
+ NMATRIX *lhs, *rhs;
1559
+
1560
+ CheckNMatrixType(original);
1561
+
1562
+ if (copy == original) {
1563
+ NM_CONSERVATIVE(nm_unregister_value(&copy));
1564
+ NM_CONSERVATIVE(nm_unregister_value(&original));
1565
+ return copy;
1566
+ }
1567
+
1568
+ UnwrapNMatrix( original, rhs );
1569
+ UnwrapNMatrix( copy, lhs );
1570
+
1571
+ lhs->stype = rhs->stype;
1572
+
1573
+ // Copy the storage
1574
+ CAST_TABLE(ttable);
1575
+ lhs->storage = ttable[lhs->stype][rhs->stype](rhs->storage, rhs->storage->dtype, NULL);
1576
+
1577
+ NM_CONSERVATIVE(nm_unregister_value(&copy));
1578
+ NM_CONSERVATIVE(nm_unregister_value(&original));
1579
+
1580
+ return copy;
1581
+ }
1582
+
1583
+ /*
1584
+ * Get major, minor, and release components of NMatrix::VERSION. Store in function parameters. Doesn't get
1585
+ * the "pre" field currently (beta1/rc1/etc).
1586
+ */
1587
+ static void get_version_info(uint16_t& major, uint16_t& minor, uint16_t& release) {
1588
+ // Get VERSION and split it on periods. Result is an Array.
1589
+ VALUE cVersion = rb_const_get(cNMatrix, rb_intern("VERSION"));
1590
+
1591
+ // Convert each to an integer
1592
+ major = FIX2INT(rb_const_get(cVersion, rb_intern("MAJOR")));
1593
+ minor = FIX2INT(rb_const_get(cVersion, rb_intern("MINOR")));
1594
+ release = FIX2INT(rb_const_get(cVersion, rb_intern("TINY")));
1595
+ }
1596
+
1597
+
1598
+ /*
1599
+ * Interpret the NMatrix::write symmetry argument (which should be nil or a symbol). Return a symm_t (enum).
1600
+ */
1601
+ static nm::symm_t interpret_symm(VALUE symm) {
1602
+ if (symm == Qnil) return nm::NONSYMM;
1603
+
1604
+ ID rb_symm = rb_intern("symmetric"),
1605
+ rb_skew = rb_intern("skew"),
1606
+ rb_herm = rb_intern("hermitian");
1607
+ // nm_rb_upper, nm_rb_lower already set
1608
+
1609
+ ID symm_id = rb_to_id(symm);
1610
+
1611
+ if (symm_id == rb_symm) return nm::SYMM;
1612
+ else if (symm_id == rb_skew) return nm::SKEW;
1613
+ else if (symm_id == rb_herm) return nm::HERM;
1614
+ else if (symm_id == nm_rb_upper) return nm::UPPER;
1615
+ else if (symm_id == nm_rb_lower) return nm::LOWER;
1616
+ else rb_raise(rb_eArgError, "unrecognized symmetry argument");
1617
+
1618
+ return nm::NONSYMM;
1619
+ }
1620
+
1621
+
1622
+
1623
+ void read_padded_shape(std::ifstream& f, size_t dim, size_t* shape) {
1624
+ size_t bytes_read = 0;
1625
+
1626
+ // Read shape
1627
+ for (size_t i = 0; i < dim; ++i) {
1628
+ size_t s;
1629
+ f.read(reinterpret_cast<char*>(&s), sizeof(size_t));
1630
+ shape[i] = s;
1631
+
1632
+ bytes_read += sizeof(size_t);
1633
+ }
1634
+
1635
+ // Ignore padding
1636
+ f.ignore(bytes_read % 8);
1637
+ }
1638
+
1639
+
1640
+ void write_padded_shape(std::ofstream& f, size_t dim, size_t* shape) {
1641
+ size_t bytes_written = 0;
1642
+
1643
+ // Write shape
1644
+ for (size_t i = 0; i < dim; ++i) {
1645
+ size_t s = shape[i];
1646
+ f.write(reinterpret_cast<const char*>(&s), sizeof(size_t));
1647
+
1648
+ bytes_written += sizeof(size_t);
1649
+ }
1650
+
1651
+ // Pad with zeros
1652
+ size_t zero = 0;
1653
+ while (bytes_written % 8) {
1654
+ f.write(reinterpret_cast<const char*>(&zero), sizeof(size_t));
1655
+
1656
+ bytes_written += sizeof(IType);
1657
+ }
1658
+ }
1659
+
1660
+
1661
+ void read_padded_yale_elements(std::ifstream& f, YALE_STORAGE* storage, size_t length, nm::symm_t symm, nm::dtype_t dtype) {
1662
+ NAMED_DTYPE_TEMPLATE_TABLE_NO_ROBJ(ttable, nm::read_padded_yale_elements, void, std::ifstream&, YALE_STORAGE*, size_t, nm::symm_t)
1663
+
1664
+ ttable[dtype](f, storage, length, symm);
1665
+ }
1666
+
1667
+
1668
+ void write_padded_yale_elements(std::ofstream& f, YALE_STORAGE* storage, size_t length, nm::symm_t symm, nm::dtype_t dtype) {
1669
+ NAMED_DTYPE_TEMPLATE_TABLE_NO_ROBJ(ttable, nm::write_padded_yale_elements, void, std::ofstream& f, YALE_STORAGE*, size_t, nm::symm_t)
1670
+
1671
+ ttable[dtype](f, storage, length, symm);
1672
+ }
1673
+
1674
+
1675
+ void read_padded_dense_elements(std::ifstream& f, DENSE_STORAGE* storage, nm::symm_t symm, nm::dtype_t dtype) {
1676
+ NAMED_DTYPE_TEMPLATE_TABLE_NO_ROBJ(ttable, nm::read_padded_dense_elements, void, std::ifstream&, DENSE_STORAGE*, nm::symm_t)
1677
+
1678
+ ttable[dtype](f, storage, symm);
1679
+ }
1680
+
1681
+
1682
+ void write_padded_dense_elements(std::ofstream& f, DENSE_STORAGE* storage, nm::symm_t symm, nm::dtype_t dtype) {
1683
+ NAMED_DTYPE_TEMPLATE_TABLE_NO_ROBJ(ttable, nm::write_padded_dense_elements, void, std::ofstream& f, DENSE_STORAGE*, nm::symm_t)
1684
+
1685
+ ttable[dtype](f, storage, symm);
1686
+ }
1687
+
1688
+
1689
+ /*
1690
+ * Helper function to get exceptions in the module Errno (e.g., ENOENT). Example:
1691
+ *
1692
+ * rb_raise(rb_get_errno_exc("ENOENT"), RSTRING_PTR(filename));
1693
+ */
1694
+ static VALUE rb_get_errno_exc(const char* which) {
1695
+ return rb_const_get(rb_const_get(rb_cObject, rb_intern("Errno")), rb_intern(which));
1696
+ }
1697
+
1698
+
1699
+
1700
+ /*
1701
+ * Binary file writer for NMatrix standard format. file should be a path, which we aren't going to
1702
+ * check very carefully (in other words, this function should generally be called from a Ruby
1703
+ * helper method). Function also takes a symmetry argument, which allows us to specify that we only want to
1704
+ * save the upper triangular portion of the matrix (or if the matrix is a lower triangular matrix, only
1705
+ * the lower triangular portion). nil means regular storage.
1706
+ */
1707
+ static VALUE nm_write(int argc, VALUE* argv, VALUE self) {
1708
+ using std::ofstream;
1709
+
1710
+ if (argc < 1 || argc > 2) {
1711
+ rb_raise(rb_eArgError, "Expected one or two arguments");
1712
+ }
1713
+
1714
+ NM_CONSERVATIVE(nm_register_values(argv, argc));
1715
+ NM_CONSERVATIVE(nm_register_value(&self));
1716
+
1717
+ VALUE file = argv[0],
1718
+ symm = argc == 1 ? Qnil : argv[1];
1719
+
1720
+ NMATRIX* nmatrix;
1721
+ UnwrapNMatrix( self, nmatrix );
1722
+
1723
+ nm::symm_t symm_ = interpret_symm(symm);
1724
+
1725
+ if (nmatrix->storage->dtype == nm::RUBYOBJ) {
1726
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
1727
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1728
+ rb_raise(rb_eNotImpError, "Ruby Object writing is not implemented yet");
1729
+ }
1730
+
1731
+ // Get the dtype, stype, itype, and symm and ensure they're the correct number of bytes.
1732
+ uint8_t st = static_cast<uint8_t>(nmatrix->stype),
1733
+ dt = static_cast<uint8_t>(nmatrix->storage->dtype),
1734
+ sm = static_cast<uint8_t>(symm_);
1735
+ uint16_t dim = nmatrix->storage->dim;
1736
+
1737
+ //FIXME: Cast the matrix to the smallest possible index type. Write that in the place of IType.
1738
+
1739
+ // Check arguments before starting to write.
1740
+ if (nmatrix->stype == nm::LIST_STORE) {
1741
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
1742
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1743
+ rb_raise(nm_eStorageTypeError, "cannot save list matrix; cast to yale or dense first");
1744
+ }
1745
+ if (symm_ != nm::NONSYMM) {
1746
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
1747
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1748
+
1749
+ if (dim != 2) rb_raise(rb_eArgError, "symmetry/triangularity not defined for a non-2D matrix");
1750
+ if (nmatrix->storage->shape[0] != nmatrix->storage->shape[1])
1751
+ rb_raise(rb_eArgError, "symmetry/triangularity not defined for a non-square matrix");
1752
+ if (symm_ == nm::HERM &&
1753
+ dt != static_cast<uint8_t>(nm::COMPLEX64) && dt != static_cast<uint8_t>(nm::COMPLEX128) && dt != static_cast<uint8_t>(nm::RUBYOBJ))
1754
+ rb_raise(rb_eArgError, "cannot save a non-complex matrix as hermitian");
1755
+ }
1756
+
1757
+ ofstream f(RSTRING_PTR(file), std::ios::out | std::ios::binary);
1758
+
1759
+ // Get the NMatrix version information.
1760
+ uint16_t major, minor, release, null16 = 0;
1761
+ get_version_info(major, minor, release);
1762
+
1763
+ // WRITE FIRST 64-BIT BLOCK
1764
+ f.write(reinterpret_cast<const char*>(&major), sizeof(uint16_t));
1765
+ f.write(reinterpret_cast<const char*>(&minor), sizeof(uint16_t));
1766
+ f.write(reinterpret_cast<const char*>(&release), sizeof(uint16_t));
1767
+ f.write(reinterpret_cast<const char*>(&null16), sizeof(uint16_t));
1768
+
1769
+ uint8_t ZERO = 0;
1770
+ // WRITE SECOND 64-BIT BLOCK
1771
+ f.write(reinterpret_cast<const char*>(&dt), sizeof(uint8_t));
1772
+ f.write(reinterpret_cast<const char*>(&st), sizeof(uint8_t));
1773
+ f.write(reinterpret_cast<const char*>(&ZERO),sizeof(uint8_t));
1774
+ f.write(reinterpret_cast<const char*>(&sm), sizeof(uint8_t));
1775
+ f.write(reinterpret_cast<const char*>(&null16), sizeof(uint16_t));
1776
+ f.write(reinterpret_cast<const char*>(&dim), sizeof(uint16_t));
1777
+
1778
+ // Write shape (in 64-bit blocks)
1779
+ write_padded_shape(f, nmatrix->storage->dim, nmatrix->storage->shape);
1780
+
1781
+ if (nmatrix->stype == nm::DENSE_STORE) {
1782
+ write_padded_dense_elements(f, reinterpret_cast<DENSE_STORAGE*>(nmatrix->storage), symm_, nmatrix->storage->dtype);
1783
+ } else if (nmatrix->stype == nm::YALE_STORE) {
1784
+ YALE_STORAGE* s = reinterpret_cast<YALE_STORAGE*>(nmatrix->storage);
1785
+ uint32_t ndnz = s->ndnz,
1786
+ length = nm_yale_storage_get_size(s);
1787
+ f.write(reinterpret_cast<const char*>(&ndnz), sizeof(uint32_t));
1788
+ f.write(reinterpret_cast<const char*>(&length), sizeof(uint32_t));
1789
+
1790
+ write_padded_yale_elements(f, s, length, symm_, s->dtype);
1791
+ }
1792
+
1793
+ f.close();
1794
+
1795
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
1796
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1797
+
1798
+ return Qtrue;
1799
+ }
1800
+
1801
+
1802
+ /*
1803
+ * Binary file reader for NMatrix standard format. file should be a path, which we aren't going to
1804
+ * check very carefully (in other words, this function should generally be called from a Ruby
1805
+ * helper method).
1806
+ *
1807
+ * Note that currently, this function will by default refuse to read files that are newer than
1808
+ * your version of NMatrix. To force an override, set the second argument to anything other than nil.
1809
+ *
1810
+ * Returns an NMatrix Ruby object.
1811
+ */
1812
+ static VALUE nm_read(int argc, VALUE* argv, VALUE self) {
1813
+ using std::ifstream;
1814
+
1815
+ NM_CONSERVATIVE(nm_register_values(argv, argc));
1816
+ NM_CONSERVATIVE(nm_register_value(&self));
1817
+
1818
+ VALUE file, force_;
1819
+
1820
+ // Read the arguments
1821
+ rb_scan_args(argc, argv, "11", &file, &force_);
1822
+ bool force = (force_ != Qnil && force_ != Qfalse);
1823
+
1824
+
1825
+ if (!RB_FILE_EXISTS(file)) { // FIXME: Errno::ENOENT
1826
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
1827
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1828
+ rb_raise(rb_get_errno_exc("ENOENT"), "%s", RSTRING_PTR(file));
1829
+ }
1830
+
1831
+ // Open a file stream
1832
+ ifstream f(RSTRING_PTR(file), std::ios::in | std::ios::binary);
1833
+
1834
+ uint16_t major, minor, release;
1835
+ get_version_info(major, minor, release); // compare to NMatrix version
1836
+
1837
+ uint16_t fmajor, fminor, frelease, null16;
1838
+
1839
+ // READ FIRST 64-BIT BLOCK
1840
+ f.read(reinterpret_cast<char*>(&fmajor), sizeof(uint16_t));
1841
+ f.read(reinterpret_cast<char*>(&fminor), sizeof(uint16_t));
1842
+ f.read(reinterpret_cast<char*>(&frelease), sizeof(uint16_t));
1843
+ f.read(reinterpret_cast<char*>(&null16), sizeof(uint16_t));
1844
+
1845
+ int ver = major * 10000 + minor * 100 + release,
1846
+ fver = fmajor * 10000 + fminor * 100 + release;
1847
+ if (fver > ver && force == false) {
1848
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
1849
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1850
+ rb_raise(rb_eIOError, "File was created in newer version of NMatrix than current (%u.%u.%u)", fmajor, fminor, frelease);
1851
+ }
1852
+ if (null16 != 0) rb_warn("nm_read: Expected zero padding was not zero (0)\n");
1853
+
1854
+ uint8_t dt, st, it, sm;
1855
+ uint16_t dim;
1856
+
1857
+ // READ SECOND 64-BIT BLOCK
1858
+ f.read(reinterpret_cast<char*>(&dt), sizeof(uint8_t));
1859
+ f.read(reinterpret_cast<char*>(&st), sizeof(uint8_t));
1860
+ f.read(reinterpret_cast<char*>(&it), sizeof(uint8_t)); // FIXME: should tell how few bytes indices are stored as
1861
+ f.read(reinterpret_cast<char*>(&sm), sizeof(uint8_t));
1862
+ f.read(reinterpret_cast<char*>(&null16), sizeof(uint16_t));
1863
+ f.read(reinterpret_cast<char*>(&dim), sizeof(uint16_t));
1864
+
1865
+ if (null16 != 0) rb_warn("nm_read: Expected zero padding was not zero (1)");
1866
+ nm::stype_t stype = static_cast<nm::stype_t>(st);
1867
+ nm::dtype_t dtype = static_cast<nm::dtype_t>(dt);
1868
+ nm::symm_t symm = static_cast<nm::symm_t>(sm);
1869
+ //nm::itype_t itype = static_cast<nm::itype_t>(it);
1870
+
1871
+ // READ NEXT FEW 64-BIT BLOCKS
1872
+ size_t* shape = NM_ALLOC_N(size_t, dim);
1873
+ read_padded_shape(f, dim, shape);
1874
+
1875
+ STORAGE* s;
1876
+ if (stype == nm::DENSE_STORE) {
1877
+ s = nm_dense_storage_create(dtype, shape, dim, NULL, 0);
1878
+ nm_register_storage(stype, s);
1879
+
1880
+ read_padded_dense_elements(f, reinterpret_cast<DENSE_STORAGE*>(s), symm, dtype);
1881
+
1882
+ } else if (stype == nm::YALE_STORE) {
1883
+ uint32_t ndnz, length;
1884
+
1885
+ // READ YALE-SPECIFIC 64-BIT BLOCK
1886
+ f.read(reinterpret_cast<char*>(&ndnz), sizeof(uint32_t));
1887
+ f.read(reinterpret_cast<char*>(&length), sizeof(uint32_t));
1888
+
1889
+ s = nm_yale_storage_create(dtype, shape, dim, length); // set length as init capacity
1890
+
1891
+ nm_register_storage(stype, s);
1892
+
1893
+ read_padded_yale_elements(f, reinterpret_cast<YALE_STORAGE*>(s), length, symm, dtype);
1894
+ } else {
1895
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
1896
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1897
+ rb_raise(nm_eStorageTypeError, "please convert to yale or dense before saving");
1898
+ }
1899
+
1900
+ NMATRIX* nm = nm_create(stype, s);
1901
+
1902
+ // Return the appropriate matrix object (Ruby VALUE)
1903
+ // FIXME: This should probably return CLASS_OF(self) instead of cNMatrix, but I don't know how that works for
1904
+ // FIXME: class methods.
1905
+ nm_register_nmatrix(nm);
1906
+ VALUE to_return = Data_Wrap_Struct(cNMatrix, nm_mark, nm_delete, nm);
1907
+
1908
+ nm_unregister_nmatrix(nm);
1909
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
1910
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1911
+ nm_unregister_storage(stype, s);
1912
+
1913
+ switch(stype) {
1914
+ case nm::DENSE_STORE:
1915
+ case nm::YALE_STORE:
1916
+ return to_return;
1917
+ default: // this case never occurs (due to earlier rb_raise)
1918
+ return Qnil;
1919
+ }
1920
+
1921
+ }
1922
+
1923
+
1924
+
1925
+ /*
1926
+ * Create a new NMatrix helper for handling internal ia, ja, and a arguments.
1927
+ *
1928
+ * This constructor is only called by Ruby code, so we can skip most of the
1929
+ * checks.
1930
+ */
1931
+ static VALUE nm_init_yale_from_old_yale(VALUE shape, VALUE dtype, VALUE ia, VALUE ja, VALUE a, VALUE from_dtype, VALUE nm) {
1932
+ size_t dim = 2;
1933
+ size_t* shape_ = interpret_shape(shape, &dim);
1934
+ nm::dtype_t dtype_ = nm_dtype_from_rbsymbol(dtype);
1935
+ char *ia_ = RSTRING_PTR(ia),
1936
+ *ja_ = RSTRING_PTR(ja),
1937
+ *a_ = RSTRING_PTR(a);
1938
+ nm::dtype_t from_dtype_ = nm_dtype_from_rbsymbol(from_dtype);
1939
+ NMATRIX* nmatrix;
1940
+
1941
+ UnwrapNMatrix( nm, nmatrix );
1942
+
1943
+ nmatrix->stype = nm::YALE_STORE;
1944
+ nmatrix->storage = (STORAGE*)nm_yale_storage_create_from_old_yale(dtype_, shape_, ia_, ja_, a_, from_dtype_);
1945
+
1946
+ return nm;
1947
+ }
1948
+
1949
+ /*
1950
+ * Check to determine whether matrix is a reference to another matrix.
1951
+ */
1952
+ static VALUE nm_is_ref(VALUE self) {
1953
+ if (NM_SRC(self) == NM_STORAGE(self)) return Qfalse;
1954
+ return Qtrue;
1955
+ }
1956
+
1957
+ /*
1958
+ * call-seq:
1959
+ * slice -> ...
1960
+ *
1961
+ * Access the contents of an NMatrix at given coordinates, using copying.
1962
+ *
1963
+ * n.slice(3,3) # => 5.0
1964
+ * n.slice(0..1,0..1) #=> matrix [2,2]
1965
+ *
1966
+ */
1967
+ static VALUE nm_mget(int argc, VALUE* argv, VALUE self) {
1968
+ static void* (*ttable[nm::NUM_STYPES])(const STORAGE*, SLICE*) = {
1969
+ nm_dense_storage_get,
1970
+ nm_list_storage_get,
1971
+ nm_yale_storage_get
1972
+ };
1973
+ nm::stype_t stype = NM_STYPE(self);
1974
+ return nm_xslice(argc, argv, ttable[stype], nm_delete, self);
1975
+ }
1976
+
1977
+ /*
1978
+ * call-seq:
1979
+ * matrix[indices] -> ...
1980
+ *
1981
+ * Access the contents of an NMatrix at given coordinates by reference.
1982
+ *
1983
+ * n[3,3] # => 5.0
1984
+ * n[0..1,0..1] #=> matrix [2,2]
1985
+ *
1986
+ */
1987
+ static VALUE nm_mref(int argc, VALUE* argv, VALUE self) {
1988
+ static void* (*ttable[nm::NUM_STYPES])(const STORAGE*, SLICE*) = {
1989
+ nm_dense_storage_ref,
1990
+ nm_list_storage_ref,
1991
+ nm_yale_storage_ref
1992
+ };
1993
+ nm::stype_t stype = NM_STYPE(self);
1994
+ return nm_xslice(argc, argv, ttable[stype], nm_delete_ref, self);
1995
+ }
1996
+
1997
+ /*
1998
+ * Modify the contents of an NMatrix in the given cell
1999
+ *
2000
+ * n[3,3] = 5.0
2001
+ *
2002
+ * Also returns the new contents, so you can chain:
2003
+ *
2004
+ * n[3,3] = n[2,3] = 5.0
2005
+ */
2006
+ static VALUE nm_mset(int argc, VALUE* argv, VALUE self) {
2007
+
2008
+ size_t dim = NM_DIM(self); // last arg is the value
2009
+
2010
+ VALUE to_return = Qnil;
2011
+
2012
+ if ((size_t)(argc) > NM_DIM(self)+1) {
2013
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %lu)", argc, effective_dim(NM_STORAGE(self))+1);
2014
+ } else {
2015
+ NM_CONSERVATIVE(nm_register_value(&self));
2016
+ NM_CONSERVATIVE(nm_register_values(argv, argc));
2017
+
2018
+ SLICE slice_s;
2019
+ SLICE* slice = &slice_s;
2020
+ slice->coords = NM_ALLOCA_N(size_t, dim);
2021
+ slice->lengths = NM_ALLOCA_N(size_t, dim);
2022
+ init_slice_no_alloc(slice, dim, argc-1, argv, NM_STORAGE(self)->shape);
2023
+
2024
+ static void (*ttable[nm::NUM_STYPES])(VALUE, SLICE*, VALUE) = {
2025
+ nm_dense_storage_set,
2026
+ nm_list_storage_set,
2027
+ nm_yale_storage_set
2028
+ };
2029
+
2030
+ ttable[NM_STYPE(self)](self, slice, argv[argc-1]);
2031
+
2032
+ to_return = argv[argc-1];
2033
+
2034
+ NM_CONSERVATIVE(nm_unregister_value(&self));
2035
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
2036
+ }
2037
+
2038
+ return to_return;
2039
+ }
2040
+
2041
+ /*
2042
+ * Matrix multiply (dot product): against another matrix or a vector.
2043
+ *
2044
+ * For elementwise, use * instead.
2045
+ *
2046
+ * The two matrices must be of the same stype (for now). If dtype differs, an upcast will occur.
2047
+ */
2048
+ static VALUE nm_multiply(VALUE left_v, VALUE right_v) {
2049
+ NM_CONSERVATIVE(nm_register_value(&left_v));
2050
+ NM_CONSERVATIVE(nm_register_value(&right_v));
2051
+
2052
+ NMATRIX *left, *right;
2053
+
2054
+ UnwrapNMatrix( left_v, left );
2055
+
2056
+ if (NM_RUBYVAL_IS_NUMERIC(right_v)) {
2057
+ NM_CONSERVATIVE(nm_unregister_value(&left_v));
2058
+ NM_CONSERVATIVE(nm_unregister_value(&right_v));
2059
+ return matrix_multiply_scalar(left, right_v);
2060
+ }
2061
+
2062
+ else if (RB_TYPE_P(right_v, T_ARRAY)) {
2063
+ NM_CONSERVATIVE(nm_unregister_value(&left_v));
2064
+ NM_CONSERVATIVE(nm_unregister_value(&right_v));
2065
+ rb_raise(rb_eNotImpError, "please convert array to nx1 or 1xn NMatrix first");
2066
+ }
2067
+
2068
+ else { // both are matrices (probably)
2069
+ CheckNMatrixType(right_v);
2070
+ UnwrapNMatrix( right_v, right );
2071
+
2072
+ // work like vector dot product for 1dim
2073
+ if (left->storage->dim == 1 && right->storage->dim == 1) {
2074
+ if (left->storage->shape[0] != right->storage->shape[0]) {
2075
+ NM_CONSERVATIVE(nm_unregister_value(&left_v));
2076
+ NM_CONSERVATIVE(nm_unregister_value(&right_v));
2077
+ rb_raise(rb_eArgError, "The left- and right-hand sides of the operation must have the same dimensionality.");
2078
+ } else {
2079
+ VALUE result = elementwise_op(nm::EW_MUL, left_v, right_v);
2080
+ VALUE to_return = rb_funcall(result, rb_intern("sum"),0);
2081
+ NM_CONSERVATIVE(nm_unregister_value(&left_v));
2082
+ NM_CONSERVATIVE(nm_unregister_value(&right_v));
2083
+ return to_return;
2084
+ }
2085
+ }
2086
+
2087
+ if (left->storage->shape[1] != right->storage->shape[0]) {
2088
+ NM_CONSERVATIVE(nm_unregister_value(&left_v));
2089
+ NM_CONSERVATIVE(nm_unregister_value(&right_v));
2090
+ rb_raise(rb_eArgError, "incompatible dimensions");
2091
+ }
2092
+
2093
+ if (left->stype != right->stype) {
2094
+ NM_CONSERVATIVE(nm_unregister_value(&left_v));
2095
+ NM_CONSERVATIVE(nm_unregister_value(&right_v));
2096
+ rb_raise(rb_eNotImpError, "matrices must have same stype");
2097
+ }
2098
+
2099
+ NM_CONSERVATIVE(nm_unregister_value(&left_v));
2100
+ NM_CONSERVATIVE(nm_unregister_value(&right_v));
2101
+ return matrix_multiply(left, right);
2102
+
2103
+ }
2104
+
2105
+ NM_CONSERVATIVE(nm_unregister_value(&left_v));
2106
+ NM_CONSERVATIVE(nm_unregister_value(&right_v));
2107
+
2108
+ return Qnil;
2109
+ }
2110
+
2111
+
2112
+ /*
2113
+ * call-seq:
2114
+ * dim -> Integer
2115
+ *
2116
+ * Get the number of dimensions of a matrix.
2117
+ *
2118
+ * In other words, if you set your matrix to be 3x4, the dim is 2. If the
2119
+ * matrix was initialized as 3x4x3, the dim is 3.
2120
+ *
2121
+ * Use #effective_dim to get the dimension of an NMatrix which acts as a vector (e.g., a column or row).
2122
+ */
2123
+ static VALUE nm_dim(VALUE self) {
2124
+ return INT2FIX(NM_STORAGE(self)->dim);
2125
+ }
2126
+
2127
+ /*
2128
+ * call-seq:
2129
+ * shape -> Array
2130
+ *
2131
+ * Get the shape (dimensions) of a matrix.
2132
+ */
2133
+ static VALUE nm_shape(VALUE self) {
2134
+ NM_CONSERVATIVE(nm_register_value(&self));
2135
+ STORAGE* s = NM_STORAGE(self);
2136
+
2137
+ // Copy elements into a VALUE array and then use those to create a Ruby array with rb_ary_new4.
2138
+ VALUE* shape = NM_ALLOCA_N(VALUE, s->dim);
2139
+ nm_register_values(shape, s->dim);
2140
+ for (size_t index = 0; index < s->dim; ++index)
2141
+ shape[index] = INT2FIX(s->shape[index]);
2142
+
2143
+ nm_unregister_values(shape, s->dim);
2144
+ NM_CONSERVATIVE(nm_unregister_value(&self));
2145
+ return rb_ary_new4(s->dim, shape);
2146
+ }
2147
+
2148
+
2149
+ /*
2150
+ * call-seq:
2151
+ * offset -> Array
2152
+ *
2153
+ * Get the offset (slice position) of a matrix. Typically all zeros, unless you have a reference slice.
2154
+ */
2155
+ static VALUE nm_offset(VALUE self) {
2156
+ NM_CONSERVATIVE(nm_register_value(&self));
2157
+ STORAGE* s = NM_STORAGE(self);
2158
+
2159
+ // Copy elements into a VALUE array and then use those to create a Ruby array with rb_ary_new4.
2160
+ VALUE* offset = NM_ALLOCA_N(VALUE, s->dim);
2161
+ nm_register_values(offset, s->dim);
2162
+ for (size_t index = 0; index < s->dim; ++index)
2163
+ offset[index] = INT2FIX(s->offset[index]);
2164
+
2165
+ nm_unregister_values(offset, s->dim);
2166
+ NM_CONSERVATIVE(nm_unregister_value(&self));
2167
+ return rb_ary_new4(s->dim, offset);
2168
+ }
2169
+
2170
+
2171
+ /*
2172
+ * call-seq:
2173
+ * supershape -> Array
2174
+ *
2175
+ * Get the shape of a slice's parent.
2176
+ */
2177
+ static VALUE nm_supershape(VALUE self) {
2178
+
2179
+ STORAGE* s = NM_STORAGE(self);
2180
+ if (s->src == s) {
2181
+ return nm_shape(self); // easy case (not a slice)
2182
+ }
2183
+ else s = s->src;
2184
+
2185
+ NM_CONSERVATIVE(nm_register_value(&self));
2186
+
2187
+ VALUE* shape = NM_ALLOCA_N(VALUE, s->dim);
2188
+ nm_register_values(shape, s->dim);
2189
+ for (size_t index = 0; index < s->dim; ++index)
2190
+ shape[index] = INT2FIX(s->shape[index]);
2191
+
2192
+ nm_unregister_values(shape, s->dim);
2193
+ NM_CONSERVATIVE(nm_unregister_value(&self));
2194
+ return rb_ary_new4(s->dim, shape);
2195
+ }
2196
+
2197
+ /*
2198
+ * call-seq:
2199
+ * stype -> Symbol
2200
+ *
2201
+ * Get the storage type (stype) of a matrix, e.g., :yale, :dense, or :list.
2202
+ */
2203
+ static VALUE nm_stype(VALUE self) {
2204
+ NM_CONSERVATIVE(nm_register_value(&self));
2205
+ VALUE stype = ID2SYM(rb_intern(STYPE_NAMES[NM_STYPE(self)]));
2206
+ NM_CONSERVATIVE(nm_unregister_value(&self));
2207
+ return stype;
2208
+ }
2209
+
2210
+ /*
2211
+ * call-seq:
2212
+ * symmetric? -> Boolean
2213
+ *
2214
+ * Is this matrix symmetric?
2215
+ */
2216
+ static VALUE nm_symmetric(VALUE self) {
2217
+ return is_symmetric(self, false);
2218
+ }
2219
+
2220
+
2221
+ /*
2222
+ * Gets the dimension of a matrix which might be a vector (have one or more shape components of size 1).
2223
+ */
2224
+ static size_t effective_dim(STORAGE* s) {
2225
+ size_t d = 0;
2226
+ for (size_t i = 0; i < s->dim; ++i) {
2227
+ if (s->shape[i] != 1) d++;
2228
+ }
2229
+ return d;
2230
+ }
2231
+
2232
+
2233
+ /*
2234
+ * call-seq:
2235
+ * effective_dim -> Fixnum
2236
+ *
2237
+ * Returns the number of dimensions that don't have length 1. Guaranteed to be less than or equal to #dim.
2238
+ */
2239
+ static VALUE nm_effective_dim(VALUE self) {
2240
+ return INT2FIX(effective_dim(NM_STORAGE(self)));
2241
+ }
2242
+
2243
+
2244
+ /*
2245
+ * Get a slice of an NMatrix.
2246
+ */
2247
+ static VALUE nm_xslice(int argc, VALUE* argv, void* (*slice_func)(const STORAGE*, SLICE*), void (*delete_func)(NMATRIX*), VALUE self) {
2248
+ VALUE result = Qnil;
2249
+
2250
+ STORAGE* s = NM_STORAGE(self);
2251
+
2252
+ if (NM_DIM(self) < (size_t)(argc)) {
2253
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %lu)", argc, effective_dim(s));
2254
+ } else {
2255
+
2256
+ NM_CONSERVATIVE(nm_register_values(argv, argc));
2257
+ NM_CONSERVATIVE(nm_register_value(&self));
2258
+
2259
+ nm_register_value(&result);
2260
+
2261
+ SLICE slice_s;
2262
+ SLICE* slice = &slice_s;
2263
+ size_t dim = NM_DIM(self);
2264
+ slice->coords = NM_ALLOCA_N(size_t, dim);
2265
+ slice->lengths = NM_ALLOCA_N(size_t, dim);
2266
+ init_slice_no_alloc(slice, dim, argc, argv, s->shape);
2267
+
2268
+ if (slice->single) {
2269
+ static void* (*ttable[nm::NUM_STYPES])(const STORAGE*, SLICE*) = {
2270
+ nm_dense_storage_ref,
2271
+ nm_list_storage_ref,
2272
+ nm_yale_storage_ref
2273
+ };
2274
+
2275
+ if (NM_DTYPE(self) == nm::RUBYOBJ) result = *reinterpret_cast<VALUE*>( ttable[NM_STYPE(self)](s, slice) );
2276
+ else result = nm::rubyobj_from_cval( ttable[NM_STYPE(self)](s, slice), NM_DTYPE(self) ).rval;
2277
+
2278
+ } else {
2279
+
2280
+ NMATRIX* mat = NM_ALLOC(NMATRIX);
2281
+ mat->stype = NM_STYPE(self);
2282
+ mat->storage = (STORAGE*)((*slice_func)( s, slice ));
2283
+ nm_register_nmatrix(mat);
2284
+ result = Data_Wrap_Struct(CLASS_OF(self), nm_mark, delete_func, mat);
2285
+ nm_unregister_nmatrix(mat);
2286
+ }
2287
+ }
2288
+
2289
+ nm_unregister_value(&result);
2290
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
2291
+ NM_CONSERVATIVE(nm_unregister_value(&self));
2292
+
2293
+ return result;
2294
+ }
2295
+
2296
+ //////////////////////
2297
+ // Helper Functions //
2298
+ //////////////////////
2299
+
2300
+ static VALUE unary_op(nm::unaryop_t op, VALUE self) {
2301
+ NM_CONSERVATIVE(nm_register_value(&self));
2302
+ NMATRIX* left;
2303
+ UnwrapNMatrix(self, left);
2304
+ std::string sym;
2305
+
2306
+ switch(left->stype) {
2307
+ case nm::DENSE_STORE:
2308
+ sym = "__dense_unary_" + nm::UNARYOPS[op] + "__";
2309
+ break;
2310
+ case nm::YALE_STORE:
2311
+ sym = "__yale_unary_" + nm::UNARYOPS[op] + "__";
2312
+ break;
2313
+ case nm::LIST_STORE:
2314
+ sym = "__list_unary_" + nm::UNARYOPS[op] + "__";
2315
+ break;
2316
+ }
2317
+
2318
+ NM_CONSERVATIVE(nm_unregister_value(&self));
2319
+ return rb_funcall(self, rb_intern(sym.c_str()), 0);
2320
+ }
2321
+
2322
+ static void check_dims_and_shape(VALUE left_val, VALUE right_val) {
2323
+ // Check that the left- and right-hand sides have the same dimensionality.
2324
+ if (NM_DIM(left_val) != NM_DIM(right_val)) {
2325
+ rb_raise(rb_eArgError, "The left- and right-hand sides of the operation must have the same dimensionality.");
2326
+ }
2327
+ // Check that the left- and right-hand sides have the same shape.
2328
+ if (memcmp(&NM_SHAPE(left_val, 0), &NM_SHAPE(right_val, 0), sizeof(size_t) * NM_DIM(left_val)) != 0) {
2329
+ rb_raise(rb_eArgError, "The left- and right-hand sides of the operation must have the same shape.");
2330
+ }
2331
+ }
2332
+
2333
+ static VALUE elementwise_op(nm::ewop_t op, VALUE left_val, VALUE right_val) {
2334
+
2335
+ NM_CONSERVATIVE(nm_register_value(&left_val));
2336
+ NM_CONSERVATIVE(nm_register_value(&right_val));
2337
+
2338
+ NMATRIX* left;
2339
+ NMATRIX* result;
2340
+
2341
+ CheckNMatrixType(left_val);
2342
+ UnwrapNMatrix(left_val, left);
2343
+
2344
+ if (!IsNMatrixType(right_val)) {
2345
+ // This is a matrix-scalar element-wise operation.
2346
+ std::string sym;
2347
+ switch(left->stype) {
2348
+ case nm::DENSE_STORE:
2349
+ sym = "__dense_scalar_" + nm::EWOP_NAMES[op] + "__";
2350
+ break;
2351
+ case nm::YALE_STORE:
2352
+ sym = "__yale_scalar_" + nm::EWOP_NAMES[op] + "__";
2353
+ break;
2354
+ case nm::LIST_STORE:
2355
+ sym = "__list_scalar_" + nm::EWOP_NAMES[op] + "__";
2356
+ break;
2357
+ default:
2358
+ NM_CONSERVATIVE(nm_unregister_value(&left_val));
2359
+ NM_CONSERVATIVE(nm_unregister_value(&right_val));
2360
+ rb_raise(rb_eNotImpError, "unknown storage type requested scalar element-wise operation");
2361
+ }
2362
+ VALUE symv = rb_intern(sym.c_str());
2363
+ NM_CONSERVATIVE(nm_unregister_value(&left_val));
2364
+ NM_CONSERVATIVE(nm_unregister_value(&right_val));
2365
+ return rb_funcall(left_val, symv, 1, right_val);
2366
+
2367
+ } else {
2368
+
2369
+ check_dims_and_shape(left_val, right_val);
2370
+
2371
+ NMATRIX* right;
2372
+ UnwrapNMatrix(right_val, right);
2373
+
2374
+ if (left->stype == right->stype) {
2375
+ std::string sym;
2376
+
2377
+ switch(left->stype) {
2378
+ case nm::DENSE_STORE:
2379
+ sym = "__dense_elementwise_" + nm::EWOP_NAMES[op] + "__";
2380
+ break;
2381
+ case nm::YALE_STORE:
2382
+ sym = "__yale_elementwise_" + nm::EWOP_NAMES[op] + "__";
2383
+ break;
2384
+ case nm::LIST_STORE:
2385
+ sym = "__list_elementwise_" + nm::EWOP_NAMES[op] + "__";
2386
+ break;
2387
+ default:
2388
+ NM_CONSERVATIVE(nm_unregister_value(&left_val));
2389
+ NM_CONSERVATIVE(nm_unregister_value(&right_val));
2390
+ rb_raise(rb_eNotImpError, "unknown storage type requested element-wise operation");
2391
+ }
2392
+
2393
+ VALUE symv = rb_intern(sym.c_str());
2394
+ NM_CONSERVATIVE(nm_unregister_value(&left_val));
2395
+ NM_CONSERVATIVE(nm_unregister_value(&right_val));
2396
+ return rb_funcall(left_val, symv, 1, right_val);
2397
+
2398
+ } else {
2399
+ NM_CONSERVATIVE(nm_unregister_value(&left_val));
2400
+ NM_CONSERVATIVE(nm_unregister_value(&right_val));
2401
+ rb_raise(rb_eArgError, "Element-wise operations are not currently supported between matrices with differing stypes.");
2402
+ }
2403
+ }
2404
+
2405
+ NM_CONSERVATIVE(nm_unregister_value(&left_val));
2406
+ NM_CONSERVATIVE(nm_unregister_value(&right_val));
2407
+ return Data_Wrap_Struct(CLASS_OF(left_val), nm_mark, nm_delete, result);
2408
+ }
2409
+
2410
+ static VALUE noncom_elementwise_op(nm::noncom_ewop_t op, VALUE self, VALUE other, VALUE flip) {
2411
+
2412
+ NM_CONSERVATIVE(nm_register_value(&self));
2413
+ NM_CONSERVATIVE(nm_register_value(&other));
2414
+
2415
+ NMATRIX* self_nm;
2416
+ NMATRIX* result;
2417
+
2418
+ CheckNMatrixType(self);
2419
+ UnwrapNMatrix(self, self_nm);
2420
+
2421
+ if (!IsNMatrixType(other)) {
2422
+ // This is a matrix-scalar element-wise operation.
2423
+ std::string sym;
2424
+ switch(self_nm->stype) {
2425
+ case nm::DENSE_STORE:
2426
+ sym = "__dense_scalar_" + nm::NONCOM_EWOP_NAMES[op] + "__";
2427
+ break;
2428
+ case nm::YALE_STORE:
2429
+ sym = "__yale_scalar_" + nm::NONCOM_EWOP_NAMES[op] + "__";
2430
+ break;
2431
+ case nm::LIST_STORE:
2432
+ sym = "__list_scalar_" + nm::NONCOM_EWOP_NAMES[op] + "__";
2433
+ break;
2434
+ default:
2435
+ NM_CONSERVATIVE(nm_unregister_value(&self));
2436
+ NM_CONSERVATIVE(nm_unregister_value(&other));
2437
+ rb_raise(rb_eNotImpError, "unknown storage type requested scalar element-wise operation");
2438
+ }
2439
+ NM_CONSERVATIVE(nm_unregister_value(&self));
2440
+ NM_CONSERVATIVE(nm_unregister_value(&other));
2441
+ return rb_funcall(self, rb_intern(sym.c_str()), 2, other, flip);
2442
+
2443
+ } else {
2444
+
2445
+ check_dims_and_shape(self, other);
2446
+
2447
+ NMATRIX* other_nm;
2448
+ UnwrapNMatrix(other, other_nm);
2449
+
2450
+ if (self_nm->stype == other_nm->stype) {
2451
+ std::string sym;
2452
+
2453
+ switch(self_nm->stype) {
2454
+ case nm::DENSE_STORE:
2455
+ sym = "__dense_elementwise_" + nm::NONCOM_EWOP_NAMES[op] + "__";
2456
+ break;
2457
+ case nm::YALE_STORE:
2458
+ sym = "__yale_elementwise_" + nm::NONCOM_EWOP_NAMES[op] + "__";
2459
+ break;
2460
+ case nm::LIST_STORE:
2461
+ sym = "__list_elementwise_" + nm::NONCOM_EWOP_NAMES[op] + "__";
2462
+ break;
2463
+ default:
2464
+ NM_CONSERVATIVE(nm_unregister_value(&self));
2465
+ NM_CONSERVATIVE(nm_unregister_value(&other));
2466
+ rb_raise(rb_eNotImpError, "unknown storage type requested element-wise operation");
2467
+ }
2468
+ NM_CONSERVATIVE(nm_unregister_value(&self));
2469
+ NM_CONSERVATIVE(nm_unregister_value(&other));
2470
+ return rb_funcall(self, rb_intern(sym.c_str()), 2, other, flip);
2471
+
2472
+ } else {
2473
+ nm_unregister_value(&self);
2474
+ nm_unregister_value(&other);
2475
+ rb_raise(rb_eArgError, "Element-wise operations are not currently supported between matrices with differing stypes.");
2476
+ }
2477
+ }
2478
+ NM_CONSERVATIVE(nm_unregister_value(&self));
2479
+ NM_CONSERVATIVE(nm_unregister_value(&other));
2480
+ return Data_Wrap_Struct(CLASS_OF(self), nm_mark, nm_delete, result);
2481
+ }
2482
+
2483
+ /*
2484
+ * Check to determine whether matrix is a reference to another matrix.
2485
+ */
2486
+ bool is_ref(const NMATRIX* matrix) {
2487
+ return matrix->storage->src != matrix->storage;
2488
+ }
2489
+
2490
+ /*
2491
+ * Helper function for nm_symmetric and nm_hermitian.
2492
+ */
2493
+ static VALUE is_symmetric(VALUE self, bool hermitian) {
2494
+ NM_CONSERVATIVE(nm_register_value(&self));
2495
+
2496
+ NMATRIX* m;
2497
+ UnwrapNMatrix(self, m);
2498
+ bool is_symmetric = false;
2499
+
2500
+ if (m->storage->shape[0] == m->storage->shape[1] and m->storage->dim == 2) {
2501
+ if (NM_STYPE(self) == nm::DENSE_STORE) {
2502
+ if (hermitian) {
2503
+ is_symmetric = nm_dense_storage_is_hermitian((DENSE_STORAGE*)(m->storage), m->storage->shape[0]);
2504
+
2505
+ } else {
2506
+ is_symmetric = nm_dense_storage_is_symmetric((DENSE_STORAGE*)(m->storage), m->storage->shape[0]);
2507
+ }
2508
+
2509
+ } else {
2510
+ // TODO: Implement, at the very least, yale_is_symmetric. Model it after yale/transp.template.c.
2511
+ NM_CONSERVATIVE(nm_unregister_value(&self));
2512
+ rb_raise(rb_eNotImpError, "symmetric? and hermitian? only implemented for dense currently");
2513
+ }
2514
+
2515
+ }
2516
+ NM_CONSERVATIVE(nm_unregister_value(&self));
2517
+ return is_symmetric ? Qtrue : Qfalse;
2518
+ }
2519
+
2520
+ ///////////////////////
2521
+ // Utility Functions //
2522
+ ///////////////////////
2523
+
2524
+ /*
2525
+ * Guess the dtype given a Ruby VALUE and return it as a symbol.
2526
+ *
2527
+ * Not to be confused with nm_dtype_guess, which returns an nm::dtype_t. (This calls that.)
2528
+ */
2529
+ static VALUE nm_guess_dtype(VALUE self, VALUE v) {
2530
+ return ID2SYM(rb_intern(DTYPE_NAMES[nm_dtype_guess(v)]));
2531
+ }
2532
+
2533
+ /*
2534
+ * Get the minimum allowable dtype for a Ruby VALUE and return it as a symbol.
2535
+ */
2536
+ static VALUE nm_min_dtype(VALUE self, VALUE v) {
2537
+ return ID2SYM(rb_intern(DTYPE_NAMES[nm_dtype_min(v)]));
2538
+ }
2539
+
2540
+ /*
2541
+ * Helper for nm_dtype_min(), handling integers.
2542
+ */
2543
+ nm::dtype_t nm_dtype_min_fixnum(int64_t v) {
2544
+ if (v >= 0 && v <= UCHAR_MAX) return nm::BYTE;
2545
+ else {
2546
+ v = std::abs(v);
2547
+ if (v <= CHAR_MAX) return nm::INT8;
2548
+ else if (v <= SHRT_MAX) return nm::INT16;
2549
+ else if (v <= INT_MAX) return nm::INT32;
2550
+ else return nm::INT64;
2551
+ }
2552
+ }
2553
+
2554
+ /*
2555
+ * Return the minimum dtype required to store a given value.
2556
+ *
2557
+ * This is kind of arbitrary. For Float, it always returns :float32 for example, since in some cases neither :float64
2558
+ * not :float32 are sufficient.
2559
+ *
2560
+ * This function is used in upcasting for scalar math. We want to ensure that :int8 + 1 does not return an :int64, basically.
2561
+ *
2562
+ * FIXME: Eventually, this function should actually look at the value stored in Fixnums (for example), so that it knows
2563
+ * whether to return :int64 or :int32.
2564
+ */
2565
+ nm::dtype_t nm_dtype_min(VALUE v) {
2566
+
2567
+ if (RB_TYPE_P(v, T_FIXNUM))
2568
+ return nm_dtype_min_fixnum(FIX2LONG(v));
2569
+ else if (RB_TYPE_P(v, T_BIGNUM))
2570
+ return nm::INT64;
2571
+ else if (RB_TYPE_P(v, T_FLOAT))
2572
+ return nm::FLOAT32;
2573
+ else if (RB_TYPE_P(v, T_COMPLEX))
2574
+ return nm::COMPLEX64;
2575
+ else if (RB_TYPE_P(v, T_STRING))
2576
+ return RSTRING_LEN(v) == 1 ? nm::BYTE : nm::RUBYOBJ;
2577
+ else if (RB_TYPE_P(v, T_TRUE) || RB_TYPE_P(v, T_FALSE) || RB_TYPE_P(v, T_NIL))
2578
+ return nm::RUBYOBJ;
2579
+ else
2580
+ return nm::RUBYOBJ;
2581
+ }
2582
+
2583
+
2584
+ /*
2585
+ * Guess the data type given a value.
2586
+ *
2587
+ * TODO: Probably needs some work for Bignum.
2588
+ */
2589
+ nm::dtype_t nm_dtype_guess(VALUE v) {
2590
+ if (RB_TYPE_P(v, T_TRUE) || RB_TYPE_P(v, T_FALSE) || RB_TYPE_P(v, T_NIL))
2591
+ return nm::RUBYOBJ;
2592
+ else if (RB_TYPE_P(v, T_STRING))
2593
+ return RSTRING_LEN(v) == 1 ? nm::BYTE : nm::RUBYOBJ;
2594
+ else if (RB_TYPE_P(v, T_FIXNUM))
2595
+ #if SIZEOF_INT == 8
2596
+ return nm::INT64;
2597
+ #elif SIZEOF_INT == 4
2598
+ return nm::INT32;
2599
+ #else
2600
+ return nm::INT16;
2601
+ #endif
2602
+ else if (RB_TYPE_P(v, T_BIGNUM))
2603
+ return nm::INT64;
2604
+ #if SIZEOF_FLOAT == 4
2605
+ else if (RB_TYPE_P(v, T_COMPLEX))
2606
+ return nm::COMPLEX128;
2607
+ else if (RB_TYPE_P(v, T_FLOAT))
2608
+ return nm::FLOAT64;
2609
+ #elif SIZEOF_FLOAT == 2
2610
+ else if (RB_TYPE_P(v, T_COMPLEX))
2611
+ return nm::COMPLEX64;
2612
+ else if (RB_TYPE_P(v, T_FLOAT))
2613
+ return nm::FLOAT32;
2614
+ #endif
2615
+ else if (RB_TYPE_P(v, T_ARRAY))
2616
+ /*
2617
+ * May be passed for dense -- for now, just look at the first element.
2618
+ *
2619
+ * TODO: Look at entire array for most specific type.
2620
+ */
2621
+ return nm_dtype_guess(RARRAY_AREF(v, 0));
2622
+ else {
2623
+ RB_P(v);
2624
+ rb_raise(rb_eArgError, "Unable to guess a data type from provided parameters; data type must be specified manually.");
2625
+ }
2626
+ }
2627
+
2628
+ /*
2629
+ * Modify an existing SLICE object (with properly allocated memory),
2630
+ * so that it will contain the appropriate coordinate and length information
2631
+ * for accessing some part of a matrix.
2632
+ */
2633
+ static void init_slice_no_alloc(SLICE* slice, size_t dim, int argc, VALUE* arg, size_t* shape) {
2634
+ NM_CONSERVATIVE(nm_register_values(arg, argc));
2635
+
2636
+ VALUE beg, end;
2637
+ int excl;
2638
+
2639
+ slice->single = true;
2640
+
2641
+ // r is the shape position; t is the slice position. They may differ when we're dealing with a
2642
+ // matrix where the effective dimension is less than the dimension (e.g., a vector).
2643
+ for (size_t r = 0, t = 0; r < dim; ++r) {
2644
+ VALUE v = t == (unsigned int)argc ? Qnil : arg[t];
2645
+
2646
+ // if the current shape indicates a vector and fewer args were supplied than necessary, just use 0
2647
+ if (argc - t + r < dim && shape[r] == 1) {
2648
+ slice->coords[r] = 0;
2649
+ slice->lengths[r] = 1;
2650
+
2651
+ } else if (FIXNUM_P(v)) { // this used CLASS_OF before, which is inefficient for fixnum
2652
+ int v_ = FIX2INT(v);
2653
+ if (v_ < 0) // checking for negative indexes
2654
+ slice->coords[r] = shape[r]+v_;
2655
+ else
2656
+ slice->coords[r] = v_;
2657
+ slice->lengths[r] = 1;
2658
+ t++;
2659
+
2660
+ } else if (SYMBOL_P(v) && rb_to_id(v) == nm_rb_mul) { // :* means the whole possible range
2661
+
2662
+ slice->coords[r] = 0;
2663
+ slice->lengths[r] = shape[r];
2664
+ slice->single = false;
2665
+ t++;
2666
+
2667
+ } else if (CLASS_OF(v) == rb_cRange) {
2668
+ rb_range_values(arg[t], &beg, &end, &excl);
2669
+
2670
+ int begin_ = FIX2INT(beg);
2671
+ int end_ = FIX2INT(end);
2672
+
2673
+ slice->coords[r] = (begin_ < 0) ? shape[r] + begin_ : begin_;
2674
+
2675
+ // Exclude last element for a...b range
2676
+ if (end_ < 0)
2677
+ slice->lengths[r] = shape[r] + end_ - slice->coords[r] + (excl ? 0 : 1);
2678
+ else
2679
+ slice->lengths[r] = end_ - slice->coords[r] + (excl ? 0 : 1);
2680
+
2681
+ slice->single = false;
2682
+ t++;
2683
+
2684
+ } else {
2685
+ NM_CONSERVATIVE(nm_unregister_values(arg, argc));
2686
+ rb_raise(rb_eArgError, "expected Fixnum or Range for slice component instead of %s", rb_obj_classname(v));
2687
+ }
2688
+
2689
+ if (slice->coords[r] > shape[r] || slice->coords[r] + slice->lengths[r] > shape[r]) {
2690
+ NM_CONSERVATIVE(nm_unregister_values(arg, argc));
2691
+ rb_raise(rb_eRangeError, "slice is larger than matrix in dimension %lu (slice component %lu)", r, t);
2692
+ }
2693
+ }
2694
+
2695
+ NM_CONSERVATIVE(nm_unregister_values(arg, argc));
2696
+ }
2697
+
2698
+ #ifdef BENCHMARK
2699
+ /*
2700
+ * A simple function used when benchmarking NMatrix.
2701
+ */
2702
+ static double get_time(void) {
2703
+ struct timeval t;
2704
+ struct timezone tzp;
2705
+
2706
+ gettimeofday(&t, &tzp);
2707
+
2708
+ return t.tv_sec + t.tv_usec*1e-6;
2709
+ }
2710
+ #endif
2711
+
2712
+ /*
2713
+ * The argv parameter will be either 1 or 2 elements. If 1, could be either
2714
+ * initial or dtype. If 2, is initial and dtype. This function returns the
2715
+ * dtype.
2716
+ */
2717
+ static nm::dtype_t interpret_dtype(int argc, VALUE* argv, nm::stype_t stype) {
2718
+ int offset;
2719
+
2720
+ switch (argc) {
2721
+ case 1:
2722
+ offset = 0;
2723
+ break;
2724
+
2725
+ case 2:
2726
+ offset = 1;
2727
+ break;
2728
+
2729
+ default:
2730
+ rb_raise(rb_eArgError, "Need an initial value or a dtype.");
2731
+ break;
2732
+ }
2733
+
2734
+ if (SYMBOL_P(argv[offset])) {
2735
+ return nm_dtype_from_rbsymbol(argv[offset]);
2736
+
2737
+ } else if (RB_TYPE_P(argv[offset], T_STRING)) {
2738
+ return nm_dtype_from_rbstring(StringValue(argv[offset]));
2739
+
2740
+ } else if (stype == nm::YALE_STORE) {
2741
+ rb_raise(rb_eArgError, "Yale storage class requires a dtype.");
2742
+
2743
+ } else {
2744
+ return nm_dtype_guess(argv[0]);
2745
+ }
2746
+ }
2747
+
2748
+ /*
2749
+ * Convert an Ruby value or an array of Ruby values into initial C values.
2750
+ */
2751
+ static void* interpret_initial_value(VALUE arg, nm::dtype_t dtype) {
2752
+ NM_CONSERVATIVE(nm_register_value(&arg));
2753
+
2754
+ unsigned int index;
2755
+ void* init_val;
2756
+
2757
+ if (RB_TYPE_P(arg, T_ARRAY)) {
2758
+ // Array
2759
+ init_val = NM_ALLOC_N(char, DTYPE_SIZES[dtype] * RARRAY_LEN(arg));
2760
+ NM_CHECK_ALLOC(init_val);
2761
+ for (index = 0; index < RARRAY_LEN(arg); ++index) {
2762
+ rubyval_to_cval(RARRAY_AREF(arg, index), dtype, (char*)init_val + (index * DTYPE_SIZES[dtype]));
2763
+ }
2764
+
2765
+ } else {
2766
+ // Single value
2767
+ init_val = rubyobj_to_cval(arg, dtype);
2768
+ }
2769
+
2770
+ NM_CONSERVATIVE(nm_unregister_value(&arg));
2771
+ return init_val;
2772
+ }
2773
+
2774
+ /*
2775
+ * Convert the shape argument, which may be either a Ruby value or an array of
2776
+ * Ruby values, into C values. The second argument is where the dimensionality
2777
+ * of the matrix will be stored. The function itself returns a pointer to the
2778
+ * array describing the shape, which must be freed manually.
2779
+ */
2780
+ static size_t* interpret_shape(VALUE arg, size_t* dim) {
2781
+ NM_CONSERVATIVE(nm_register_value(&arg));
2782
+ size_t* shape;
2783
+
2784
+ if (RB_TYPE_P(arg, T_ARRAY)) {
2785
+ *dim = RARRAY_LEN(arg);
2786
+ shape = NM_ALLOC_N(size_t, *dim);
2787
+
2788
+ for (size_t index = 0; index < *dim; ++index) {
2789
+ shape[index] = FIX2UINT( RARRAY_AREF(arg, index) );
2790
+ }
2791
+
2792
+ } else if (FIXNUM_P(arg)) {
2793
+ *dim = 2;
2794
+ shape = NM_ALLOC_N(size_t, *dim);
2795
+
2796
+ shape[0] = FIX2UINT(arg);
2797
+ shape[1] = FIX2UINT(arg);
2798
+
2799
+ } else {
2800
+ nm_unregister_value(&arg);
2801
+ rb_raise(rb_eArgError, "Expected an array of numbers or a single Fixnum for matrix shape");
2802
+ }
2803
+
2804
+ NM_CONSERVATIVE(nm_unregister_value(&arg));
2805
+ return shape;
2806
+ }
2807
+
2808
+ /*
2809
+ * Convert a Ruby symbol or string into an storage type.
2810
+ */
2811
+ static nm::stype_t interpret_stype(VALUE arg) {
2812
+ if (SYMBOL_P(arg)) {
2813
+ return nm_stype_from_rbsymbol(arg);
2814
+
2815
+ } else if (RB_TYPE_P(arg, T_STRING)) {
2816
+ return nm_stype_from_rbstring(StringValue(arg));
2817
+
2818
+ } else {
2819
+ rb_raise(rb_eArgError, "Expected storage type");
2820
+ }
2821
+ }
2822
+
2823
+ //////////////////
2824
+ // Math Helpers //
2825
+ //////////////////
2826
+
2827
+ STORAGE* matrix_storage_cast_alloc(NMATRIX* matrix, nm::dtype_t new_dtype) {
2828
+ if (matrix->storage->dtype == new_dtype && !is_ref(matrix))
2829
+ return matrix->storage;
2830
+
2831
+ CAST_TABLE(cast_copy_storage);
2832
+ return cast_copy_storage[matrix->stype][matrix->stype](matrix->storage, new_dtype, NULL);
2833
+ }
2834
+
2835
+ STORAGE_PAIR binary_storage_cast_alloc(NMATRIX* left_matrix, NMATRIX* right_matrix) {
2836
+ nm_register_nmatrix(left_matrix);
2837
+ nm_register_nmatrix(right_matrix);
2838
+
2839
+ STORAGE_PAIR casted;
2840
+ nm::dtype_t new_dtype = Upcast[left_matrix->storage->dtype][right_matrix->storage->dtype];
2841
+
2842
+ casted.left = matrix_storage_cast_alloc(left_matrix, new_dtype);
2843
+ nm_register_storage(left_matrix->stype, casted.left);
2844
+ casted.right = matrix_storage_cast_alloc(right_matrix, new_dtype);
2845
+
2846
+ nm_unregister_nmatrix(left_matrix);
2847
+ nm_unregister_nmatrix(right_matrix);
2848
+ nm_unregister_storage(left_matrix->stype, casted.left);
2849
+
2850
+ return casted;
2851
+ }
2852
+
2853
+ static VALUE matrix_multiply_scalar(NMATRIX* left, VALUE scalar) {
2854
+ rb_raise(rb_eNotImpError, "matrix-scalar multiplication not implemented yet");
2855
+ return Qnil;
2856
+ }
2857
+
2858
+ static VALUE matrix_multiply(NMATRIX* left, NMATRIX* right) {
2859
+ nm_register_nmatrix(left);
2860
+ nm_register_nmatrix(right);
2861
+ ///TODO: multiplication for non-dense and/or non-decimal matrices
2862
+
2863
+ // Make sure both of our matrices are of the correct type.
2864
+ STORAGE_PAIR casted = binary_storage_cast_alloc(left, right);
2865
+ nm_register_storage(left->stype, casted.left);
2866
+ nm_register_storage(right->stype, casted.right);
2867
+
2868
+ size_t* resulting_shape = NM_ALLOC_N(size_t, 2);
2869
+ resulting_shape[0] = left->storage->shape[0];
2870
+ resulting_shape[1] = right->storage->shape[1];
2871
+
2872
+ // Sometimes we only need to use matrix-vector multiplication (e.g., GEMM versus GEMV). Find out.
2873
+ bool vector = false;
2874
+ if (resulting_shape[1] == 1) vector = true;
2875
+
2876
+ static STORAGE* (*storage_matrix_multiply[nm::NUM_STYPES])(const STORAGE_PAIR&, size_t*, bool) = {
2877
+ nm_dense_storage_matrix_multiply,
2878
+ nm_list_storage_matrix_multiply,
2879
+ nm_yale_storage_matrix_multiply
2880
+ };
2881
+
2882
+ STORAGE* resulting_storage = storage_matrix_multiply[left->stype](casted, resulting_shape, vector);
2883
+ NMATRIX* result = nm_create(left->stype, resulting_storage);
2884
+ nm_register_nmatrix(result);
2885
+
2886
+ // Free any casted-storage we created for the multiplication.
2887
+ // TODO: Can we make the Ruby GC take care of this stuff now that we're using it?
2888
+ // If we did that, we night not have to re-create these every time, right? Or wrong? Need to do
2889
+ // more research.
2890
+ static void (*free_storage[nm::NUM_STYPES])(STORAGE*) = {
2891
+ nm_dense_storage_delete,
2892
+ nm_list_storage_delete,
2893
+ nm_yale_storage_delete
2894
+ };
2895
+
2896
+ nm_unregister_storage(left->stype, casted.left);
2897
+ if (left->storage != casted.left) free_storage[result->stype](casted.left);
2898
+
2899
+ nm_unregister_storage(right->stype, casted.right);
2900
+ if (right->storage != casted.right) free_storage[result->stype](casted.right);
2901
+
2902
+ VALUE to_return = result ? Data_Wrap_Struct(cNMatrix, nm_mark, nm_delete, result) : Qnil; // Only if we try to multiply list matrices should we return Qnil.
2903
+
2904
+ nm_unregister_nmatrix(left);
2905
+ nm_unregister_nmatrix(right);
2906
+ nm_unregister_nmatrix(result);
2907
+
2908
+ return to_return;
2909
+ }
2910
+
2911
+ /*
2912
+ * Reduce a matrix to hessenberg form.
2913
+ *
2914
+ * == Arguments
2915
+ *
2916
+ * a - The NMatrix to be reduced. This matrix is replaced with the hessenberg form.
2917
+ *
2918
+ * == Notes
2919
+ *
2920
+ * LAPACK free.
2921
+ */
2922
+ static VALUE nm_hessenberg(VALUE self, VALUE a) {
2923
+ nm_math_hessenberg(a);
2924
+
2925
+ return a;
2926
+ }
2927
+
2928
+ /*
2929
+ * Calculate the inverse of a matrix with in-place Gauss-Jordan elimination.
2930
+ * Inverse will fail if the largest element in any column in zero.
2931
+ *
2932
+ * LAPACK free.
2933
+ */
2934
+ static VALUE nm_inverse(VALUE self, VALUE inverse, VALUE bang) {
2935
+
2936
+ if (NM_STYPE(self) != nm::DENSE_STORE) {
2937
+ rb_raise(rb_eNotImpError, "needs exact determinant implementation for this matrix stype");
2938
+ return Qnil;
2939
+ }
2940
+
2941
+ if (NM_DIM(self) != 2 || NM_SHAPE0(self) != NM_SHAPE1(self)) {
2942
+ rb_raise(nm_eShapeError, "matrices must be square to have an inverse defined");
2943
+ return Qnil;
2944
+ }
2945
+
2946
+ if (bang == Qtrue) {
2947
+ nm_math_inverse(NM_SHAPE0(self), NM_STORAGE_DENSE(self)->elements,
2948
+ NM_DTYPE(self));
2949
+
2950
+ return self;
2951
+ }
2952
+
2953
+ nm_math_inverse(NM_SHAPE0(inverse), NM_STORAGE_DENSE(inverse)->elements,
2954
+ NM_DTYPE(inverse));
2955
+
2956
+ return inverse;
2957
+ }
2958
+
2959
+ /*
2960
+ * Calculate the exact inverse of a 2x2 or 3x3 matrix.
2961
+ *
2962
+ * Does not test for invertibility!
2963
+ */
2964
+ static VALUE nm_inverse_exact(VALUE self, VALUE inverse, VALUE lda, VALUE ldb) {
2965
+ if (NM_DIM(self) != 2 || NM_SHAPE0(self) != NM_SHAPE1(self)) {
2966
+ rb_raise(nm_eShapeError, "matrices must be square to have an inverse defined");
2967
+ return Qnil;
2968
+ }
2969
+
2970
+ nm::dtype_t dtype = NM_DTYPE(self);
2971
+ void* result = NM_ALLOCA_N(char, DTYPE_SIZES[dtype]);
2972
+ if (dtype == nm::RUBYOBJ) {
2973
+ nm_register_values(reinterpret_cast<VALUE*>(result), 1);
2974
+ }
2975
+ nm::stype_t old_stype = NM_STYPE(self);
2976
+ if (old_stype == nm::LIST_STORE) {
2977
+ self = nm_cast_with_types(self, nm::YALE_STORE, dtype, result);
2978
+ inverse = nm_cast_with_types(inverse, nm::YALE_STORE, dtype, result);
2979
+ }
2980
+
2981
+ if (NM_STYPE(self) == nm::DENSE_STORE) {
2982
+ nm_math_inverse_exact_from_dense(NM_SHAPE0(self),
2983
+ NM_STORAGE_DENSE(self)->elements, FIX2INT(lda),
2984
+ NM_STORAGE_DENSE(inverse)->elements, FIX2INT(ldb), dtype);
2985
+ } else {
2986
+ nm_math_inverse_exact_from_yale(NM_SHAPE0(self),
2987
+ NM_STORAGE_YALE(self), FIX2INT(lda),
2988
+ NM_STORAGE_YALE(inverse), FIX2INT(ldb), dtype);
2989
+ }
2990
+
2991
+ if (old_stype == nm::LIST_STORE) {
2992
+ inverse = nm_cast_with_types(inverse, nm::LIST_STORE, dtype, result);
2993
+ }
2994
+ if (dtype == nm::RUBYOBJ) {
2995
+ nm_unregister_values(reinterpret_cast<VALUE*>(result), 1);
2996
+ }
2997
+ return inverse;
2998
+ }
2999
+
3000
+ /*
3001
+ * Calculate the exact determinant of a dense matrix.
3002
+ *
3003
+ * Returns nil for dense matrices which are not square or number of dimensions other than 2.
3004
+ *
3005
+ * Note: Currently only implemented for 2x2 and 3x3 matrices.
3006
+ */
3007
+ static VALUE nm_det_exact(VALUE self) {
3008
+
3009
+ if (NM_DIM(self) != 2 || NM_SHAPE0(self) != NM_SHAPE1(self)) {
3010
+ rb_raise(nm_eShapeError, "matrices must be square to have a determinant defined");
3011
+ return Qnil;
3012
+ }
3013
+
3014
+ nm::dtype_t dtype = NM_DTYPE(self);
3015
+ void* result = NM_ALLOCA_N(char, DTYPE_SIZES[dtype]);
3016
+ if (NM_STYPE(self) == nm::LIST_STORE) {
3017
+ self = nm_cast_with_types(self, nm::YALE_STORE, dtype, result);
3018
+ }
3019
+
3020
+ NM_CONSERVATIVE(nm_register_value(&self));
3021
+
3022
+ // Calculate the determinant and then assign it to the return value
3023
+ if (NM_STYPE(self) == nm::DENSE_STORE) {
3024
+ nm_math_det_exact_from_dense(NM_SHAPE0(self), NM_STORAGE_DENSE(self)->elements,
3025
+ NM_SHAPE0(self), NM_DTYPE(self), result);
3026
+ } else {
3027
+ nm_math_det_exact_from_yale(NM_SHAPE0(self), NM_STORAGE_YALE(self),
3028
+ NM_SHAPE0(self), NM_DTYPE(self), result);
3029
+ }
3030
+
3031
+ VALUE to_return;
3032
+ if (dtype == nm::RUBYOBJ) {
3033
+ to_return = *reinterpret_cast<VALUE*>(result);
3034
+
3035
+ } else {
3036
+ to_return = nm::rubyobj_from_cval(result, NM_DTYPE(self)).rval;
3037
+ }
3038
+ NM_CONSERVATIVE(nm_unregister_value(&self));
3039
+
3040
+ return to_return;
3041
+ }
3042
+
3043
+
3044
+
3045
+ /*
3046
+ * Returns the pointer to the matrix storage's data. This is useful primarily when you are using FFI with NMatrix --
3047
+ * say, for example, you want to pass a float* to some function, and your NMatrix is a :float32 :dense matrix. Then you
3048
+ * can call this function and get that pointer directly instead of copying the data.
3049
+ */
3050
+ static VALUE nm_data_pointer(VALUE self) {
3051
+ //if (NM_DTYPE(self) == nm::LIST_STORE)
3052
+ // rb_warn("pointer requested for list storage, which may be meaningless");
3053
+
3054
+ // This is actually pretty easy, since all of the storage types have their elements positioned in the same place
3055
+ // relative to one another. So yes, believe it or not, this should work just as well for Yale or list storage as for
3056
+ // dense.
3057
+ return INT2FIX(NM_STORAGE_DENSE(self)->elements);
3058
+ }
3059
+
3060
+
3061
+ /////////////////
3062
+ // Exposed API //
3063
+ /////////////////
3064
+
3065
+ /*
3066
+ * Create a dense matrix. Used by the NMatrix GSL fork. Unlike nm_create, this one copies all of the
3067
+ * arrays and such passed in -- so you don't have to allocate and pass a new shape object for every
3068
+ * matrix you want to create, for example. Same goes for elements.
3069
+ *
3070
+ * Returns a properly-wrapped Ruby object as a VALUE.
3071
+ *
3072
+ * *** Note that this function is for API only. Please do not use it internally.
3073
+ *
3074
+ * TODO: Add a column-major option for libraries that use column-major matrices.
3075
+ */
3076
+ VALUE rb_nmatrix_dense_create(nm::dtype_t dtype, size_t* shape, size_t dim, void* elements, size_t length) {
3077
+
3078
+ if (dtype == nm::RUBYOBJ) {
3079
+ nm_register_values(reinterpret_cast<VALUE*>(elements), length);
3080
+ }
3081
+
3082
+ NMATRIX* nm;
3083
+ size_t nm_dim;
3084
+ size_t* shape_copy;
3085
+
3086
+ // Do not allow a dim of 1. Treat it as a column or row matrix.
3087
+ if (dim == 1) {
3088
+ nm_dim = 2;
3089
+ shape_copy = NM_ALLOC_N(size_t, nm_dim);
3090
+ shape_copy[0] = shape[0];
3091
+ shape_copy[1] = 1;
3092
+
3093
+ } else {
3094
+ nm_dim = dim;
3095
+ shape_copy = NM_ALLOC_N(size_t, nm_dim);
3096
+ memcpy(shape_copy, shape, sizeof(size_t)*nm_dim);
3097
+ }
3098
+
3099
+ // Copy elements
3100
+ void* elements_copy = NM_ALLOC_N(char, DTYPE_SIZES[dtype]*length);
3101
+ memcpy(elements_copy, elements, DTYPE_SIZES[dtype]*length);
3102
+
3103
+ // allocate and create the matrix and its storage
3104
+ nm = nm_create(nm::DENSE_STORE, nm_dense_storage_create(dtype, shape_copy, dim, elements_copy, length));
3105
+
3106
+ nm_register_nmatrix(nm);
3107
+
3108
+ VALUE to_return = Data_Wrap_Struct(cNMatrix, nm_mark, nm_delete, nm);
3109
+
3110
+ nm_unregister_nmatrix(nm);
3111
+ if (dtype == nm::RUBYOBJ) {
3112
+ nm_unregister_values(reinterpret_cast<VALUE*>(elements), length);
3113
+ }
3114
+
3115
+ // tell Ruby about the matrix and its storage, particularly how to garbage collect it.
3116
+ return to_return;
3117
+ }
3118
+
3119
+ /*
3120
+ * Create a dense vector. Used by the NMatrix GSL fork.
3121
+ *
3122
+ * Basically just a convenience wrapper for rb_nmatrix_dense_create().
3123
+ *
3124
+ * Returns a properly-wrapped Ruby NMatrix object as a VALUE. Included for backwards compatibility
3125
+ * for when NMatrix had an NVector class.
3126
+ */
3127
+ VALUE rb_nvector_dense_create(nm::dtype_t dtype, void* elements, size_t length) {
3128
+ size_t dim = 1, shape = length;
3129
+ return rb_nmatrix_dense_create(dtype, &shape, dim, elements, length);
3130
+ }