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