nmatrix 0.0.8 → 0.0.9
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.
- checksums.yaml +4 -4
- data/.gitignore +3 -8
- data/.rspec +1 -1
- data/.travis.yml +12 -0
- data/CONTRIBUTING.md +27 -12
- data/Gemfile +1 -0
- data/History.txt +38 -0
- data/Manifest.txt +15 -15
- data/README.rdoc +7 -6
- data/Rakefile +40 -5
- data/ext/nmatrix/data/data.cpp +2 -37
- data/ext/nmatrix/data/data.h +19 -121
- data/ext/nmatrix/data/meta.h +70 -0
- data/ext/nmatrix/extconf.rb +40 -12
- data/ext/nmatrix/math/math.h +13 -103
- data/ext/nmatrix/nmatrix.cpp +10 -2018
- data/ext/nmatrix/nmatrix.h +16 -13
- data/ext/nmatrix/ruby_constants.cpp +12 -1
- data/ext/nmatrix/ruby_constants.h +7 -1
- data/ext/nmatrix/ruby_nmatrix.c +2169 -0
- data/ext/nmatrix/storage/dense.cpp +123 -14
- data/ext/nmatrix/storage/dense.h +10 -4
- data/ext/nmatrix/storage/list.cpp +265 -48
- data/ext/nmatrix/storage/list.h +6 -9
- data/ext/nmatrix/storage/storage.cpp +44 -54
- data/ext/nmatrix/storage/storage.h +2 -2
- data/ext/nmatrix/storage/yale/class.h +1070 -0
- data/ext/nmatrix/storage/yale/iterators/base.h +142 -0
- data/ext/nmatrix/storage/yale/iterators/iterator.h +130 -0
- data/ext/nmatrix/storage/yale/iterators/row.h +449 -0
- data/ext/nmatrix/storage/yale/iterators/row_stored.h +139 -0
- data/ext/nmatrix/storage/yale/iterators/row_stored_nd.h +167 -0
- data/ext/nmatrix/storage/yale/iterators/stored_diagonal.h +123 -0
- data/ext/nmatrix/storage/yale/math/transpose.h +110 -0
- data/ext/nmatrix/storage/yale/yale.cpp +1785 -0
- data/ext/nmatrix/storage/{yale.h → yale/yale.h} +23 -55
- data/ext/nmatrix/types.h +2 -0
- data/ext/nmatrix/util/io.cpp +27 -45
- data/ext/nmatrix/util/io.h +0 -2
- data/ext/nmatrix/util/sl_list.cpp +169 -28
- data/ext/nmatrix/util/sl_list.h +9 -3
- data/lib/nmatrix/blas.rb +20 -20
- data/lib/nmatrix/enumerate.rb +1 -1
- data/lib/nmatrix/io/mat5_reader.rb +8 -14
- data/lib/nmatrix/lapack.rb +3 -3
- data/lib/nmatrix/math.rb +3 -3
- data/lib/nmatrix/nmatrix.rb +19 -5
- data/lib/nmatrix/nvector.rb +2 -0
- data/lib/nmatrix/shortcuts.rb +90 -125
- data/lib/nmatrix/version.rb +1 -1
- data/nmatrix.gemspec +7 -8
- data/spec/{nmatrix_spec.rb → 00_nmatrix_spec.rb} +45 -208
- data/spec/01_enum_spec.rb +184 -0
- data/spec/{slice_spec.rb → 02_slice_spec.rb} +55 -39
- data/spec/blas_spec.rb +22 -54
- data/spec/elementwise_spec.rb +9 -8
- data/spec/io_spec.rb +6 -4
- data/spec/lapack_spec.rb +26 -26
- data/spec/math_spec.rb +9 -5
- data/spec/nmatrix_yale_spec.rb +29 -61
- data/spec/shortcuts_spec.rb +34 -22
- data/spec/slice_set_spec.rb +157 -0
- data/spec/spec_helper.rb +42 -2
- data/spec/stat_spec.rb +192 -0
- metadata +52 -55
- data/ext/nmatrix/storage/yale.cpp +0 -2284
- data/spec/nmatrix_list_spec.rb +0 -113
- data/spec/nvector_spec.rb +0 -112
@@ -0,0 +1,70 @@
|
|
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 - 2013, Ruby Science Foundation
|
13
|
+
// NMatrix is Copyright (c) 2013, 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
|
+
// == meta.h
|
25
|
+
//
|
26
|
+
// Header file for dealing with template metaprogramming.
|
27
|
+
|
28
|
+
#ifndef META_H
|
29
|
+
# define META_H
|
30
|
+
|
31
|
+
namespace nm {
|
32
|
+
/*
|
33
|
+
* Template Metaprogramming
|
34
|
+
*/
|
35
|
+
template <typename T> struct ctype_to_dtype_enum {
|
36
|
+
static const nm::dtype_t value_type = nm::BYTE;
|
37
|
+
};
|
38
|
+
template <> struct ctype_to_dtype_enum<uint8_t> { static const nm::dtype_t value_type = nm::BYTE; };
|
39
|
+
template <> struct ctype_to_dtype_enum<int8_t> { static const nm::dtype_t value_type = nm::INT8; };
|
40
|
+
template <> struct ctype_to_dtype_enum<int16_t> { static const nm::dtype_t value_type = nm::INT16; };
|
41
|
+
template <> struct ctype_to_dtype_enum<int32_t> { static const nm::dtype_t value_type = nm::INT32; };
|
42
|
+
template <> struct ctype_to_dtype_enum<int64_t> { static const nm::dtype_t value_type = nm::INT64; };
|
43
|
+
template <> struct ctype_to_dtype_enum<float> { static const nm::dtype_t value_type = nm::FLOAT32; };
|
44
|
+
template <> struct ctype_to_dtype_enum<double> { static const nm::dtype_t value_type = nm::FLOAT64; };
|
45
|
+
template <> struct ctype_to_dtype_enum<Complex64> { static const nm::dtype_t value_type = nm::COMPLEX64; };
|
46
|
+
template <> struct ctype_to_dtype_enum<Complex128> { static const nm::dtype_t value_type = nm::COMPLEX128; };
|
47
|
+
template <> struct ctype_to_dtype_enum<Rational32> { static const nm::dtype_t value_type = nm::RATIONAL32; };
|
48
|
+
template <> struct ctype_to_dtype_enum<Rational64> { static const nm::dtype_t value_type = nm::RATIONAL64; };
|
49
|
+
template <> struct ctype_to_dtype_enum<Rational128> { static const nm::dtype_t value_type = nm::RATIONAL128; };
|
50
|
+
template <> struct ctype_to_dtype_enum<RubyObject> { static const nm::dtype_t value_type = nm::RUBYOBJ; };
|
51
|
+
|
52
|
+
|
53
|
+
template <nm::dtype_t Enum> struct dtype_enum_T;
|
54
|
+
template <> struct dtype_enum_T<nm::BYTE> { typedef uint8_t type; };
|
55
|
+
template <> struct dtype_enum_T<nm::INT8> { typedef int8_t type; };
|
56
|
+
template <> struct dtype_enum_T<nm::INT16> { typedef int16_t type; };
|
57
|
+
template <> struct dtype_enum_T<nm::INT32> { typedef int32_t type; };
|
58
|
+
template <> struct dtype_enum_T<nm::INT64> { typedef int64_t type; };
|
59
|
+
template <> struct dtype_enum_T<nm::FLOAT32> { typedef float type; };
|
60
|
+
template <> struct dtype_enum_T<nm::FLOAT64> { typedef double type; };
|
61
|
+
template <> struct dtype_enum_T<nm::COMPLEX64> { typedef nm::Complex64 type; };
|
62
|
+
template <> struct dtype_enum_T<nm::COMPLEX128> { typedef nm::Complex128 type; };
|
63
|
+
template <> struct dtype_enum_T<nm::RATIONAL32> { typedef nm::Rational32 type; };
|
64
|
+
template <> struct dtype_enum_T<nm::RATIONAL64> { typedef nm::Rational64 type; };
|
65
|
+
template <> struct dtype_enum_T<nm::RATIONAL128> { typedef nm::Rational128 type; };
|
66
|
+
template <> struct dtype_enum_T<nm::RUBYOBJ> { typedef nm::RubyObject type; };
|
67
|
+
|
68
|
+
} // end namespace nm
|
69
|
+
|
70
|
+
#endif
|
data/ext/nmatrix/extconf.rb
CHANGED
@@ -74,6 +74,10 @@ def create_conf_h(file) #:nodoc:
|
|
74
74
|
hfile.puts "#define RUBY_2 1"
|
75
75
|
end
|
76
76
|
|
77
|
+
if RUBY_VERSION < '1.9.3'
|
78
|
+
hfile.puts "#define OLD_RB_SCAN_ARGS"
|
79
|
+
end
|
80
|
+
|
77
81
|
for line in $defs
|
78
82
|
line =~ /^-D(.*)/
|
79
83
|
hfile.printf "#define %s 1\n", $1
|
@@ -111,7 +115,7 @@ $srcs = [
|
|
111
115
|
'storage/common.cpp',
|
112
116
|
'storage/storage.cpp',
|
113
117
|
'storage/dense.cpp',
|
114
|
-
'storage/yale.cpp',
|
118
|
+
'storage/yale/yale.cpp',
|
115
119
|
'storage/list.cpp'
|
116
120
|
]
|
117
121
|
# add smmp in to get generic transp; remove smmp2 to eliminate funcptr transp
|
@@ -130,21 +134,37 @@ $srcs = [
|
|
130
134
|
# export CPLUS_INCLUDE_PATH=/usr/local/atlas/include
|
131
135
|
# (substituting in the path of your cblas.h and clapack.h for the path I used). -- JW 8/27/12
|
132
136
|
|
137
|
+
idefaults = {lapack: ["/usr/include/atlas"],
|
138
|
+
cblas: ["/usr/local/atlas/include", "/usr/include/atlas"],
|
139
|
+
atlas: ["/usr/local/atlas/include", "/usr/include/atlas"]}
|
140
|
+
|
141
|
+
ldefaults = {lapack: ["/usr/local/lib", "/usr/local/atlas/lib"],
|
142
|
+
cblas: ["/usr/local/lib", "/usr/local/atlas/lib"],
|
143
|
+
atlas: ["/usr/local/atlas/lib", "/usr/local/lib", "/usr/lib"]}
|
133
144
|
|
134
|
-
unless have_library("lapack")
|
135
|
-
dir_config("lapack", [
|
145
|
+
unless have_library("lapack")
|
146
|
+
dir_config("lapack", idefaults[:lapack], ldefaults[:lapack])
|
136
147
|
end
|
137
148
|
|
138
|
-
unless have_library("cblas")
|
139
|
-
dir_config("cblas", [
|
149
|
+
unless have_library("cblas")
|
150
|
+
dir_config("cblas", idefaults[:cblas], ldefaults[:cblas])
|
140
151
|
end
|
141
152
|
|
142
153
|
unless have_library("atlas")
|
143
|
-
dir_config("atlas", [
|
154
|
+
dir_config("atlas", idefaults[:atlas], ldefaults[:atlas])
|
144
155
|
end
|
145
156
|
|
146
|
-
#
|
157
|
+
# this needs to go before cblas.h checks -- on Ubuntu, the clapack in the
|
158
|
+
# include path found for cblas.h doesn't seem to contain all the necessary
|
159
|
+
# functions
|
147
160
|
have_header("clapack.h")
|
161
|
+
|
162
|
+
# this ensures that we find the header on Ubuntu, where by default the library
|
163
|
+
# can be found but not the header
|
164
|
+
unless have_header("cblas.h")
|
165
|
+
find_header("cblas.h", *idefaults[:cblas])
|
166
|
+
end
|
167
|
+
|
148
168
|
have_header("cblas.h")
|
149
169
|
|
150
170
|
have_func("clapack_dgetrf", ["cblas.h", "clapack.h"])
|
@@ -153,14 +173,16 @@ have_func("dgesvd_", "clapack.h")
|
|
153
173
|
|
154
174
|
have_func("cblas_dgemm", "cblas.h")
|
155
175
|
|
176
|
+
#have_func("rb_scan_args", "ruby.h")
|
156
177
|
|
178
|
+
#find_library("lapack", "clapack_dgetrf")
|
157
179
|
#find_library("cblas", "cblas_dgemm")
|
158
180
|
#find_library("atlas", "ATL_dgemmNN")
|
159
181
|
|
160
182
|
# Order matters here: ATLAS has to go after LAPACK: http://mail.scipy.org/pipermail/scipy-user/2007-January/010717.html
|
161
183
|
$libs += " -llapack -lcblas -latlas "
|
162
184
|
|
163
|
-
$objs = %w{nmatrix ruby_constants data/data util/io math util/sl_list storage/common storage/storage storage/dense storage/yale storage/list}.map { |i| i + ".o" }
|
185
|
+
$objs = %w{nmatrix ruby_constants data/data util/io math util/sl_list storage/common storage/storage storage/dense storage/yale/yale storage/list}.map { |i| i + ".o" }
|
164
186
|
|
165
187
|
#CONFIG['CXX'] = 'clang++'
|
166
188
|
CONFIG['CXX'] = 'g++'
|
@@ -206,10 +228,10 @@ else
|
|
206
228
|
end
|
207
229
|
|
208
230
|
# For release, these next two should both be changed to -O3.
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
231
|
+
$CFLAGS += " -O3 " #" -O0 -g "
|
232
|
+
#$CFLAGS += " -static -O0 -g "
|
233
|
+
$CPPFLAGS += " -O3 -std=#{$CPP_STANDARD} " #" -O0 -g -std=#{$CPP_STANDARD} " #-fmax-errors=10 -save-temps
|
234
|
+
#$CPPFLAGS += " -static -O0 -g -std=#{$CPP_STANDARD} "
|
213
235
|
|
214
236
|
CONFIG['warnflags'].gsub!('-Wshorten-64-to-32', '') # doesn't work except in Mac-patched gcc (4.2)
|
215
237
|
CONFIG['warnflags'].gsub!('-Wdeclaration-after-statement', '')
|
@@ -221,6 +243,12 @@ create_makefile("nmatrix")
|
|
221
243
|
Dir.mkdir("data") unless Dir.exists?("data")
|
222
244
|
Dir.mkdir("util") unless Dir.exists?("util")
|
223
245
|
Dir.mkdir("storage") unless Dir.exists?("storage")
|
246
|
+
Dir.chdir("storage") do
|
247
|
+
Dir.mkdir("yale") unless Dir.exists?("yale")
|
248
|
+
Dir.chdir("yale") do
|
249
|
+
Dir.mkdir("iterators") unless Dir.exists?("iterators")
|
250
|
+
end
|
251
|
+
end
|
224
252
|
|
225
253
|
# to clean up object files in subdirectories:
|
226
254
|
open('Makefile', 'a') do |f|
|
data/ext/nmatrix/math/math.h
CHANGED
@@ -205,7 +205,7 @@ inline void trmm(const enum CBLAS_ORDER order, const enum CBLAS_SIDE side, const
|
|
205
205
|
|
206
206
|
|
207
207
|
// Yale: numeric matrix multiply c=a*b
|
208
|
-
template <typename DType
|
208
|
+
template <typename DType>
|
209
209
|
inline void numbmm(const unsigned int n, const unsigned int m, const unsigned int l, const IType* ia, const IType* ja, const DType* a, const bool diaga,
|
210
210
|
const IType* ib, const IType* jb, const DType* b, const bool diagb, IType* ic, IType* jc, DType* c, const bool diagc) {
|
211
211
|
const unsigned int max_lmn = std::max(std::max(m, n), l);
|
@@ -323,7 +323,6 @@ inline void new_yale_matrix_multiply(const unsigned int m, const IType* ija, con
|
|
323
323
|
*/
|
324
324
|
|
325
325
|
// Yale: Symbolic matrix multiply c=a*b
|
326
|
-
template <typename IType>
|
327
326
|
inline size_t symbmm(const unsigned int n, const unsigned int m, const unsigned int l, const IType* ia, const IType* ja, const bool diaga,
|
328
327
|
const IType* ib, const IType* jb, const bool diagb, IType* ic, const bool diagc) {
|
329
328
|
unsigned int max_lmn = std::max(std::max(m,n), l);
|
@@ -378,7 +377,7 @@ inline size_t symbmm(const unsigned int n, const unsigned int m, const unsigned
|
|
378
377
|
namespace smmp_sort {
|
379
378
|
const size_t THRESHOLD = 4; // switch to insertion sort for 4 elements or fewer
|
380
379
|
|
381
|
-
template <typename DType
|
380
|
+
template <typename DType>
|
382
381
|
void print_array(DType* vals, IType* array, IType left, IType right) {
|
383
382
|
for (IType i = left; i <= right; ++i) {
|
384
383
|
std::cerr << array[i] << ":" << vals[i] << " ";
|
@@ -386,7 +385,7 @@ namespace smmp_sort {
|
|
386
385
|
std::cerr << std::endl;
|
387
386
|
}
|
388
387
|
|
389
|
-
template <typename DType
|
388
|
+
template <typename DType>
|
390
389
|
IType partition(DType* vals, IType* array, IType left, IType right, IType pivot) {
|
391
390
|
IType pivotJ = array[pivot];
|
392
391
|
DType pivotV = vals[pivot];
|
@@ -414,8 +413,8 @@ namespace smmp_sort {
|
|
414
413
|
}
|
415
414
|
|
416
415
|
// Recommended to use the median of left, right, and mid for the pivot.
|
417
|
-
template <typename
|
418
|
-
|
416
|
+
template <typename I>
|
417
|
+
inline I median(I a, I b, I c) {
|
419
418
|
if (a < b) {
|
420
419
|
if (b < c) return b; // a b c
|
421
420
|
if (a < c) return c; // a c b
|
@@ -430,7 +429,7 @@ namespace smmp_sort {
|
|
430
429
|
|
431
430
|
|
432
431
|
// Insertion sort is more efficient than quicksort for small N
|
433
|
-
template <typename DType
|
432
|
+
template <typename DType>
|
434
433
|
void insertion_sort(DType* vals, IType* array, IType left, IType right) {
|
435
434
|
for (IType idx = left; idx <= right; ++idx) {
|
436
435
|
IType col_to_insert = array[idx];
|
@@ -448,7 +447,7 @@ namespace smmp_sort {
|
|
448
447
|
}
|
449
448
|
|
450
449
|
|
451
|
-
template <typename DType
|
450
|
+
template <typename DType>
|
452
451
|
void quicksort(DType* vals, IType* array, IType left, IType right) {
|
453
452
|
|
454
453
|
if (left < right) {
|
@@ -456,14 +455,14 @@ namespace smmp_sort {
|
|
456
455
|
insertion_sort(vals, array, left, right);
|
457
456
|
} else {
|
458
457
|
// choose any pivot such that left < pivot < right
|
459
|
-
IType pivot = median(left, right, (IType)(((unsigned long)left + (unsigned long)right) / 2));
|
458
|
+
IType pivot = median<IType>(left, right, (IType)(((unsigned long)left + (unsigned long)right) / 2));
|
460
459
|
pivot = partition(vals, array, left, right, pivot);
|
461
460
|
|
462
461
|
// recursively sort elements smaller than the pivot
|
463
|
-
quicksort<DType
|
462
|
+
quicksort<DType>(vals, array, left, pivot-1);
|
464
463
|
|
465
464
|
// recursively sort elements at least as big as the pivot
|
466
|
-
quicksort<DType
|
465
|
+
quicksort<DType>(vals, array, pivot+1, right);
|
467
466
|
}
|
468
467
|
}
|
469
468
|
}
|
@@ -483,108 +482,19 @@ namespace smmp_sort {
|
|
483
482
|
* ordering. If someone is doing a lot of Yale matrix multiplication, it might benefit them to consider even insertion
|
484
483
|
* sort.
|
485
484
|
*/
|
486
|
-
template <typename DType
|
485
|
+
template <typename DType>
|
487
486
|
inline void smmp_sort_columns(const size_t n, const IType* ia, IType* ja, DType* a) {
|
488
487
|
for (size_t i = 0; i < n; ++i) {
|
489
488
|
if (ia[i+1] - ia[i] < 2) continue; // no need to sort rows containing only one or two elements.
|
490
489
|
else if (ia[i+1] - ia[i] <= smmp_sort::THRESHOLD) {
|
491
|
-
smmp_sort::insertion_sort<DType
|
490
|
+
smmp_sort::insertion_sort<DType>(a, ja, ia[i], ia[i+1]-1); // faster for small rows
|
492
491
|
} else {
|
493
|
-
smmp_sort::quicksort<DType
|
492
|
+
smmp_sort::quicksort<DType>(a, ja, ia[i], ia[i+1]-1); // faster for large rows (and may call insertion_sort as well)
|
494
493
|
}
|
495
494
|
}
|
496
495
|
}
|
497
496
|
|
498
497
|
|
499
|
-
|
500
|
-
/*
|
501
|
-
* Transposes a generic Yale matrix (old or new). Specify new by setting diaga = true.
|
502
|
-
*
|
503
|
-
* Based on transp from SMMP (same as symbmm and numbmm).
|
504
|
-
*
|
505
|
-
* This is not named in the same way as most yale_storage functions because it does not act on a YALE_STORAGE
|
506
|
-
* object.
|
507
|
-
*/
|
508
|
-
template <typename DType, typename IType>
|
509
|
-
void transpose_yale(const size_t n, const size_t m, const void* ia_, const void* ja_, const void* a_,
|
510
|
-
const bool diaga, void* ib_, void* jb_, void* b_, const bool move)
|
511
|
-
{
|
512
|
-
const IType *ia = reinterpret_cast<const IType*>(ia_),
|
513
|
-
*ja = reinterpret_cast<const IType*>(ja_);
|
514
|
-
const DType *a = reinterpret_cast<const DType*>(a_);
|
515
|
-
|
516
|
-
IType *ib = reinterpret_cast<IType*>(ib_),
|
517
|
-
*jb = reinterpret_cast<IType*>(jb_);
|
518
|
-
DType *b = reinterpret_cast<DType*>(b_);
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
size_t index;
|
523
|
-
|
524
|
-
// Clear B
|
525
|
-
for (size_t i = 0; i < m+1; ++i) ib[i] = 0;
|
526
|
-
|
527
|
-
if (move)
|
528
|
-
for (size_t i = 0; i < m+1; ++i) b[i] = 0;
|
529
|
-
|
530
|
-
if (diaga) ib[0] = m + 1;
|
531
|
-
else ib[0] = 0;
|
532
|
-
|
533
|
-
/* count indices for each column */
|
534
|
-
|
535
|
-
for (size_t i = 0; i < n; ++i) {
|
536
|
-
for (size_t j = ia[i]; j < ia[i+1]; ++j) {
|
537
|
-
++(ib[ja[j]+1]);
|
538
|
-
}
|
539
|
-
}
|
540
|
-
|
541
|
-
for (size_t i = 0; i < m; ++i) {
|
542
|
-
ib[i+1] = ib[i] + ib[i+1];
|
543
|
-
}
|
544
|
-
|
545
|
-
/* now make jb */
|
546
|
-
|
547
|
-
for (size_t i = 0; i < n; ++i) {
|
548
|
-
|
549
|
-
for (size_t j = ia[i]; j < ia[i+1]; ++j) {
|
550
|
-
index = ja[j];
|
551
|
-
jb[ib[index]] = i;
|
552
|
-
|
553
|
-
if (move)
|
554
|
-
b[ib[index]] = a[j];
|
555
|
-
|
556
|
-
++(ib[index]);
|
557
|
-
}
|
558
|
-
}
|
559
|
-
|
560
|
-
/* now fixup ib */
|
561
|
-
|
562
|
-
for (size_t i = m; i >= 1; --i) {
|
563
|
-
ib[i] = ib[i-1];
|
564
|
-
}
|
565
|
-
|
566
|
-
|
567
|
-
if (diaga) {
|
568
|
-
if (move) {
|
569
|
-
size_t j = std::min(n,m);
|
570
|
-
|
571
|
-
for (size_t i = 0; i < j; ++i) {
|
572
|
-
b[i] = a[i];
|
573
|
-
}
|
574
|
-
}
|
575
|
-
ib[0] = m + 1;
|
576
|
-
|
577
|
-
} else {
|
578
|
-
ib[0] = 0;
|
579
|
-
}
|
580
|
-
}
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
498
|
/*
|
589
499
|
* From ATLAS 3.8.0:
|
590
500
|
*
|
data/ext/nmatrix/nmatrix.cpp
CHANGED
@@ -45,6 +45,7 @@ extern "C" {
|
|
45
45
|
/*
|
46
46
|
* Project Includes
|
47
47
|
*/
|
48
|
+
#include "nmatrix_config.h"
|
48
49
|
|
49
50
|
#include "types.h"
|
50
51
|
#include "data/data.h"
|
@@ -52,12 +53,17 @@ extern "C" {
|
|
52
53
|
#include "util/io.h"
|
53
54
|
#include "storage/storage.h"
|
54
55
|
#include "storage/list.h"
|
55
|
-
#include "storage/yale.h"
|
56
|
+
#include "storage/yale/yale.h"
|
56
57
|
|
57
58
|
#include "nmatrix.h"
|
58
59
|
|
59
60
|
#include "ruby_constants.h"
|
60
61
|
|
62
|
+
/*
|
63
|
+
* Ruby internals
|
64
|
+
*/
|
65
|
+
|
66
|
+
|
61
67
|
/*
|
62
68
|
* Macros
|
63
69
|
*/
|
@@ -74,7 +80,6 @@ namespace nm {
|
|
74
80
|
*
|
75
81
|
* shape should already be allocated before calling this.
|
76
82
|
*/
|
77
|
-
template <typename IType>
|
78
83
|
void read_padded_shape(std::ifstream& f, size_t dim, size_t* shape) {
|
79
84
|
size_t bytes_read = 0;
|
80
85
|
|
@@ -91,7 +96,6 @@ namespace nm {
|
|
91
96
|
f.ignore(bytes_read % 8);
|
92
97
|
}
|
93
98
|
|
94
|
-
template <typename IType>
|
95
99
|
void write_padded_shape(std::ofstream& f, size_t dim, size_t* shape) {
|
96
100
|
size_t bytes_written = 0;
|
97
101
|
|
@@ -246,7 +250,7 @@ namespace nm {
|
|
246
250
|
if (bytes_read % 8) f.ignore(bytes_read % 8);
|
247
251
|
}
|
248
252
|
|
249
|
-
template <typename DType
|
253
|
+
template <typename DType>
|
250
254
|
void write_padded_yale_elements(std::ofstream& f, YALE_STORAGE* storage, size_t length, nm::symm_t symm) {
|
251
255
|
if (symm != nm::NONSYMM) rb_raise(rb_eNotImpError, "Yale matrices can only be read/written in full form");
|
252
256
|
|
@@ -268,7 +272,7 @@ namespace nm {
|
|
268
272
|
}
|
269
273
|
|
270
274
|
|
271
|
-
template <typename DType
|
275
|
+
template <typename DType>
|
272
276
|
void read_padded_yale_elements(std::ifstream& f, YALE_STORAGE* storage, size_t length, nm::symm_t symm) {
|
273
277
|
if (symm != NONSYMM) rb_raise(rb_eNotImpError, "Yale matrices can only be read/written in full form");
|
274
278
|
|
@@ -324,2017 +328,5 @@ namespace nm {
|
|
324
328
|
} // end of namespace nm
|
325
329
|
|
326
330
|
extern "C" {
|
327
|
-
|
328
|
-
/*
|
329
|
-
* Forward Declarations
|
330
|
-
*/
|
331
|
-
|
332
|
-
static VALUE nm_init(int argc, VALUE* argv, VALUE nm);
|
333
|
-
static VALUE nm_init_copy(VALUE copy, VALUE original);
|
334
|
-
static VALUE nm_init_transposed(VALUE self);
|
335
|
-
static VALUE nm_cast(VALUE self, VALUE new_stype_symbol, VALUE new_dtype_symbol, VALUE init);
|
336
|
-
static VALUE nm_read(int argc, VALUE* argv, VALUE self);
|
337
|
-
static VALUE nm_write(int argc, VALUE* argv, VALUE self);
|
338
|
-
static VALUE nm_init_yale_from_old_yale(VALUE shape, VALUE dtype, VALUE ia, VALUE ja, VALUE a, VALUE from_dtype, VALUE nm);
|
339
|
-
static VALUE nm_alloc(VALUE klass);
|
340
|
-
static VALUE nm_dtype(VALUE self);
|
341
|
-
static VALUE nm_itype(VALUE self);
|
342
|
-
static VALUE nm_stype(VALUE self);
|
343
|
-
static VALUE nm_default_value(VALUE self);
|
344
|
-
static size_t effective_dim(STORAGE* s);
|
345
|
-
static VALUE nm_effective_dim(VALUE self);
|
346
|
-
static VALUE nm_dim(VALUE self);
|
347
|
-
static VALUE nm_offset(VALUE self);
|
348
|
-
static VALUE nm_shape(VALUE self);
|
349
|
-
static VALUE nm_supershape(int argc, VALUE* argv, VALUE self);
|
350
|
-
static VALUE nm_capacity(VALUE self);
|
351
|
-
static VALUE nm_each_with_indices(VALUE nmatrix);
|
352
|
-
static VALUE nm_each_stored_with_indices(VALUE nmatrix);
|
353
|
-
|
354
|
-
static SLICE* get_slice(size_t dim, int argc, VALUE* arg, size_t* shape);
|
355
|
-
static VALUE nm_xslice(int argc, VALUE* argv, void* (*slice_func)(STORAGE*, SLICE*), void (*delete_func)(NMATRIX*), VALUE self);
|
356
|
-
static VALUE nm_mset(int argc, VALUE* argv, VALUE self);
|
357
|
-
static VALUE nm_mget(int argc, VALUE* argv, VALUE self);
|
358
|
-
static VALUE nm_mref(int argc, VALUE* argv, VALUE self);
|
359
|
-
static VALUE nm_is_ref(VALUE self);
|
360
|
-
|
361
|
-
static VALUE is_symmetric(VALUE self, bool hermitian);
|
362
|
-
|
363
|
-
static VALUE nm_guess_dtype(VALUE self, VALUE v);
|
364
|
-
static VALUE nm_min_dtype(VALUE self, VALUE v);
|
365
|
-
|
366
|
-
/*
|
367
|
-
* Macro defines an element-wise accessor function for some operation.
|
368
|
-
*
|
369
|
-
* This is only responsible for the Ruby accessor! You still have to write the actual functions, obviously.
|
370
|
-
*/
|
371
|
-
#define DEF_ELEMENTWISE_RUBY_ACCESSOR(oper, name) \
|
372
|
-
static VALUE nm_ew_##name(VALUE left_val, VALUE right_val) { \
|
373
|
-
return elementwise_op(nm::EW_##oper, left_val, right_val); \
|
374
|
-
}
|
375
|
-
|
376
|
-
/*
|
377
|
-
* Macro declares a corresponding accessor function prototype for some element-wise operation.
|
378
|
-
*/
|
379
|
-
#define DECL_ELEMENTWISE_RUBY_ACCESSOR(name) static VALUE nm_ew_##name(VALUE left_val, VALUE right_val);
|
380
|
-
|
381
|
-
DECL_ELEMENTWISE_RUBY_ACCESSOR(add)
|
382
|
-
DECL_ELEMENTWISE_RUBY_ACCESSOR(subtract)
|
383
|
-
DECL_ELEMENTWISE_RUBY_ACCESSOR(multiply)
|
384
|
-
DECL_ELEMENTWISE_RUBY_ACCESSOR(divide)
|
385
|
-
DECL_ELEMENTWISE_RUBY_ACCESSOR(power)
|
386
|
-
DECL_ELEMENTWISE_RUBY_ACCESSOR(mod)
|
387
|
-
DECL_ELEMENTWISE_RUBY_ACCESSOR(eqeq)
|
388
|
-
DECL_ELEMENTWISE_RUBY_ACCESSOR(neq)
|
389
|
-
DECL_ELEMENTWISE_RUBY_ACCESSOR(lt)
|
390
|
-
DECL_ELEMENTWISE_RUBY_ACCESSOR(gt)
|
391
|
-
DECL_ELEMENTWISE_RUBY_ACCESSOR(leq)
|
392
|
-
DECL_ELEMENTWISE_RUBY_ACCESSOR(geq)
|
393
|
-
|
394
|
-
static VALUE elementwise_op(nm::ewop_t op, VALUE left_val, VALUE right_val);
|
395
|
-
|
396
|
-
static VALUE nm_symmetric(VALUE self);
|
397
|
-
static VALUE nm_hermitian(VALUE self);
|
398
|
-
|
399
|
-
static VALUE nm_eqeq(VALUE left, VALUE right);
|
400
|
-
|
401
|
-
static VALUE matrix_multiply_scalar(NMATRIX* left, VALUE scalar);
|
402
|
-
static VALUE matrix_multiply(NMATRIX* left, NMATRIX* right);
|
403
|
-
static VALUE nm_multiply(VALUE left_v, VALUE right_v);
|
404
|
-
static VALUE nm_det_exact(VALUE self);
|
405
|
-
static VALUE nm_complex_conjugate_bang(VALUE self);
|
406
|
-
|
407
|
-
static nm::dtype_t interpret_dtype(int argc, VALUE* argv, nm::stype_t stype);
|
408
|
-
static void* interpret_initial_value(VALUE arg, nm::dtype_t dtype);
|
409
|
-
static size_t* interpret_shape(VALUE arg, size_t* dim);
|
410
|
-
static nm::stype_t interpret_stype(VALUE arg);
|
411
|
-
|
412
|
-
/* Singleton methods */
|
413
|
-
static VALUE nm_itype_by_shape(VALUE self, VALUE shape_arg);
|
414
|
-
static VALUE nm_upcast(VALUE self, VALUE t1, VALUE t2);
|
415
|
-
|
416
|
-
|
417
|
-
#ifdef BENCHMARK
|
418
|
-
static double get_time(void);
|
419
|
-
#endif
|
420
|
-
|
421
|
-
///////////////////
|
422
|
-
// Ruby Bindings //
|
423
|
-
///////////////////
|
424
|
-
|
425
|
-
void Init_nmatrix() {
|
426
|
-
|
427
|
-
|
428
|
-
///////////////////////
|
429
|
-
// Class Definitions //
|
430
|
-
///////////////////////
|
431
|
-
|
432
|
-
cNMatrix = rb_define_class("NMatrix", rb_cObject);
|
433
|
-
//cNVector = rb_define_class("NVector", cNMatrix);
|
434
|
-
|
435
|
-
// Special exceptions
|
436
|
-
|
437
|
-
/*
|
438
|
-
* Exception raised when there's a problem with data.
|
439
|
-
*/
|
440
|
-
nm_eDataTypeError = rb_define_class("DataTypeError", rb_eStandardError);
|
441
|
-
|
442
|
-
/*
|
443
|
-
* Exception raised when something goes wrong with the storage of a matrix.
|
444
|
-
*/
|
445
|
-
nm_eStorageTypeError = rb_define_class("StorageTypeError", rb_eStandardError);
|
446
|
-
|
447
|
-
///////////////////
|
448
|
-
// Class Methods //
|
449
|
-
///////////////////
|
450
|
-
|
451
|
-
rb_define_alloc_func(cNMatrix, nm_alloc);
|
452
|
-
|
453
|
-
///////////////////////
|
454
|
-
// Singleton Methods //
|
455
|
-
///////////////////////
|
456
|
-
|
457
|
-
rb_define_singleton_method(cNMatrix, "upcast", (METHOD)nm_upcast, 2);
|
458
|
-
rb_define_singleton_method(cNMatrix, "itype_by_shape", (METHOD)nm_itype_by_shape, 1);
|
459
|
-
rb_define_singleton_method(cNMatrix, "guess_dtype", (METHOD)nm_guess_dtype, 1);
|
460
|
-
rb_define_singleton_method(cNMatrix, "min_dtype", (METHOD)nm_min_dtype, 1);
|
461
|
-
|
462
|
-
//////////////////////
|
463
|
-
// Instance Methods //
|
464
|
-
//////////////////////
|
465
|
-
|
466
|
-
rb_define_method(cNMatrix, "initialize", (METHOD)nm_init, -1);
|
467
|
-
rb_define_method(cNMatrix, "initialize_copy", (METHOD)nm_init_copy, 1);
|
468
|
-
rb_define_singleton_method(cNMatrix, "read", (METHOD)nm_read, -1);
|
469
|
-
|
470
|
-
rb_define_method(cNMatrix, "write", (METHOD)nm_write, -1);
|
471
|
-
|
472
|
-
// Technically, the following function is a copy constructor.
|
473
|
-
rb_define_method(cNMatrix, "transpose", (METHOD)nm_init_transposed, 0);
|
474
|
-
|
475
|
-
rb_define_method(cNMatrix, "dtype", (METHOD)nm_dtype, 0);
|
476
|
-
rb_define_method(cNMatrix, "itype", (METHOD)nm_itype, 0);
|
477
|
-
rb_define_method(cNMatrix, "stype", (METHOD)nm_stype, 0);
|
478
|
-
rb_define_method(cNMatrix, "cast_full", (METHOD)nm_cast, 3);
|
479
|
-
rb_define_method(cNMatrix, "default_value", (METHOD)nm_default_value, 0);
|
480
|
-
rb_define_protected_method(cNMatrix, "__list_default_value__", (METHOD)nm_list_default_value, 0);
|
481
|
-
rb_define_protected_method(cNMatrix, "__yale_default_value__", (METHOD)nm_yale_default_value, 0);
|
482
|
-
|
483
|
-
rb_define_method(cNMatrix, "[]", (METHOD)nm_mref, -1);
|
484
|
-
rb_define_method(cNMatrix, "slice", (METHOD)nm_mget, -1);
|
485
|
-
rb_define_method(cNMatrix, "[]=", (METHOD)nm_mset, -1);
|
486
|
-
rb_define_method(cNMatrix, "is_ref?", (METHOD)nm_is_ref, 0);
|
487
|
-
rb_define_method(cNMatrix, "dimensions", (METHOD)nm_dim, 0);
|
488
|
-
rb_define_method(cNMatrix, "effective_dimensions", (METHOD)nm_effective_dim, 0);
|
489
|
-
|
490
|
-
rb_define_protected_method(cNMatrix, "__list_to_hash__", (METHOD)nm_to_hash, 0); // handles list and dense, which are n-dimensional
|
491
|
-
|
492
|
-
rb_define_method(cNMatrix, "shape", (METHOD)nm_shape, 0);
|
493
|
-
rb_define_method(cNMatrix, "supershape", (METHOD)nm_supershape, -1);
|
494
|
-
rb_define_method(cNMatrix, "offset", (METHOD)nm_offset, 0);
|
495
|
-
rb_define_method(cNMatrix, "det_exact", (METHOD)nm_det_exact, 0);
|
496
|
-
rb_define_method(cNMatrix, "complex_conjugate!", (METHOD)nm_complex_conjugate_bang, 0);
|
497
|
-
|
498
|
-
rb_define_protected_method(cNMatrix, "__dense_each__", (METHOD)nm_dense_each, 0);
|
499
|
-
rb_define_protected_method(cNMatrix, "__dense_map__", (METHOD)nm_dense_map, 0);
|
500
|
-
rb_define_protected_method(cNMatrix, "__dense_map_pair__", (METHOD)nm_dense_map_pair, 1);
|
501
|
-
rb_define_method(cNMatrix, "each_with_indices", (METHOD)nm_each_with_indices, 0);
|
502
|
-
rb_define_method(cNMatrix, "each_stored_with_indices", (METHOD)nm_each_stored_with_indices, 0);
|
503
|
-
rb_define_protected_method(cNMatrix, "__list_map_merged_stored__", (METHOD)nm_list_map_merged_stored, 2);
|
504
|
-
rb_define_protected_method(cNMatrix, "__yale_map_merged_stored__", (METHOD)nm_yale_map_merged_stored, 2);
|
505
|
-
rb_define_protected_method(cNMatrix, "__yale_map_stored__", (METHOD)nm_yale_map_stored, 0);
|
506
|
-
|
507
|
-
rb_define_method(cNMatrix, "==", (METHOD)nm_eqeq, 1);
|
508
|
-
|
509
|
-
rb_define_method(cNMatrix, "+", (METHOD)nm_ew_add, 1);
|
510
|
-
rb_define_method(cNMatrix, "-", (METHOD)nm_ew_subtract, 1);
|
511
|
-
rb_define_method(cNMatrix, "*", (METHOD)nm_ew_multiply, 1);
|
512
|
-
rb_define_method(cNMatrix, "/", (METHOD)nm_ew_divide, 1);
|
513
|
-
rb_define_method(cNMatrix, "**", (METHOD)nm_ew_power, 1);
|
514
|
-
rb_define_method(cNMatrix, "%", (METHOD)nm_ew_mod, 1);
|
515
|
-
|
516
|
-
rb_define_method(cNMatrix, "=~", (METHOD)nm_ew_eqeq, 1);
|
517
|
-
rb_define_method(cNMatrix, "!~", (METHOD)nm_ew_neq, 1);
|
518
|
-
rb_define_method(cNMatrix, "<=", (METHOD)nm_ew_leq, 1);
|
519
|
-
rb_define_method(cNMatrix, ">=", (METHOD)nm_ew_geq, 1);
|
520
|
-
rb_define_method(cNMatrix, "<", (METHOD)nm_ew_lt, 1);
|
521
|
-
rb_define_method(cNMatrix, ">", (METHOD)nm_ew_gt, 1);
|
522
|
-
|
523
|
-
/////////////////////////////
|
524
|
-
// Helper Instance Methods //
|
525
|
-
/////////////////////////////
|
526
|
-
rb_define_protected_method(cNMatrix, "__yale_vector_set__", (METHOD)nm_vector_set, -1);
|
527
|
-
|
528
|
-
/////////////////////////
|
529
|
-
// Matrix Math Methods //
|
530
|
-
/////////////////////////
|
531
|
-
rb_define_method(cNMatrix, "dot", (METHOD)nm_multiply, 1);
|
532
|
-
|
533
|
-
rb_define_method(cNMatrix, "symmetric?", (METHOD)nm_symmetric, 0);
|
534
|
-
rb_define_method(cNMatrix, "hermitian?", (METHOD)nm_hermitian, 0);
|
535
|
-
|
536
|
-
rb_define_method(cNMatrix, "capacity", (METHOD)nm_capacity, 0);
|
537
|
-
|
538
|
-
/////////////
|
539
|
-
// Aliases //
|
540
|
-
/////////////
|
541
|
-
|
542
|
-
rb_define_alias(cNMatrix, "dim", "dimensions");
|
543
|
-
rb_define_alias(cNMatrix, "effective_dim", "effective_dimensions");
|
544
|
-
rb_define_alias(cNMatrix, "equal?", "eql?");
|
545
|
-
|
546
|
-
///////////////////////
|
547
|
-
// Symbol Generation //
|
548
|
-
///////////////////////
|
549
|
-
|
550
|
-
nm_init_ruby_constants();
|
551
|
-
|
552
|
-
//////////////////////////
|
553
|
-
// YaleFunctions module //
|
554
|
-
//////////////////////////
|
555
|
-
|
556
|
-
nm_init_yale_functions();
|
557
|
-
|
558
|
-
/////////////////
|
559
|
-
// BLAS module //
|
560
|
-
/////////////////
|
561
|
-
|
562
|
-
nm_math_init_blas();
|
563
|
-
|
564
|
-
///////////////
|
565
|
-
// IO module //
|
566
|
-
///////////////
|
567
|
-
nm_init_io();
|
568
|
-
|
569
|
-
/////////////////////////////////////////////////
|
570
|
-
// Force compilation of necessary constructors //
|
571
|
-
/////////////////////////////////////////////////
|
572
|
-
nm_init_data();
|
573
|
-
}
|
574
|
-
|
575
|
-
|
576
|
-
//////////////////
|
577
|
-
// Ruby Methods //
|
578
|
-
//////////////////
|
579
|
-
|
580
|
-
|
581
|
-
/*
|
582
|
-
* Slice constructor.
|
583
|
-
*/
|
584
|
-
static SLICE* alloc_slice(size_t dim) {
|
585
|
-
SLICE* slice = ALLOC(SLICE);
|
586
|
-
slice->coords = ALLOC_N(size_t, dim);
|
587
|
-
slice->lengths = ALLOC_N(size_t, dim);
|
588
|
-
return slice;
|
589
|
-
}
|
590
|
-
|
591
|
-
|
592
|
-
/*
|
593
|
-
* Slice destructor.
|
594
|
-
*/
|
595
|
-
static void free_slice(SLICE* slice) {
|
596
|
-
xfree(slice->coords);
|
597
|
-
xfree(slice->lengths);
|
598
|
-
xfree(slice);
|
599
|
-
}
|
600
|
-
|
601
|
-
|
602
|
-
/*
|
603
|
-
* Allocator.
|
604
|
-
*/
|
605
|
-
static VALUE nm_alloc(VALUE klass) {
|
606
|
-
NMATRIX* mat = ALLOC(NMATRIX);
|
607
|
-
mat->storage = NULL;
|
608
|
-
// 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.
|
609
|
-
// mat->stype = nm::NUM_STYPES;
|
610
|
-
|
611
|
-
//STYPE_MARK_TABLE(mark_table);
|
612
|
-
|
613
|
-
return Data_Wrap_Struct(klass, NULL, nm_delete, mat);
|
614
|
-
}
|
615
|
-
|
616
|
-
/*
|
617
|
-
* Find the capacity of an NMatrix. The capacity only differs from the size for
|
618
|
-
* Yale matrices, which occasionally allocate more space than they need. For
|
619
|
-
* list and dense, capacity gives the number of elements in the matrix.
|
620
|
-
*
|
621
|
-
* If you call this on a slice, it may behave unpredictably. Most likely it'll
|
622
|
-
* just return the original matrix's capacity.
|
623
|
-
*/
|
624
|
-
static VALUE nm_capacity(VALUE self) {
|
625
|
-
VALUE cap;
|
626
|
-
|
627
|
-
switch(NM_STYPE(self)) {
|
628
|
-
case nm::YALE_STORE:
|
629
|
-
cap = UINT2NUM(reinterpret_cast<YALE_STORAGE*>(NM_STORAGE_YALE(self)->src)->capacity);
|
630
|
-
break;
|
631
|
-
|
632
|
-
case nm::DENSE_STORE:
|
633
|
-
cap = UINT2NUM(nm_storage_count_max_elements( NM_STORAGE_DENSE(self) ));
|
634
|
-
break;
|
635
|
-
|
636
|
-
case nm::LIST_STORE:
|
637
|
-
cap = UINT2NUM(nm_list_storage_count_elements( NM_STORAGE_LIST(self) ));
|
638
|
-
break;
|
639
|
-
|
640
|
-
default:
|
641
|
-
rb_raise(nm_eStorageTypeError, "unrecognized stype in nm_capacity()");
|
642
|
-
}
|
643
|
-
|
644
|
-
return cap;
|
645
|
-
}
|
646
|
-
|
647
|
-
/*
|
648
|
-
* Destructor.
|
649
|
-
*/
|
650
|
-
void nm_delete(NMATRIX* mat) {
|
651
|
-
static void (*ttable[nm::NUM_STYPES])(STORAGE*) = {
|
652
|
-
nm_dense_storage_delete,
|
653
|
-
nm_list_storage_delete,
|
654
|
-
nm_yale_storage_delete
|
655
|
-
};
|
656
|
-
ttable[mat->stype](mat->storage);
|
657
|
-
|
658
|
-
xfree(mat);
|
659
|
-
}
|
660
|
-
|
661
|
-
/*
|
662
|
-
* Slicing destructor.
|
663
|
-
*/
|
664
|
-
void nm_delete_ref(NMATRIX* mat) {
|
665
|
-
static void (*ttable[nm::NUM_STYPES])(STORAGE*) = {
|
666
|
-
nm_dense_storage_delete_ref,
|
667
|
-
nm_list_storage_delete_ref,
|
668
|
-
nm_yale_storage_delete_ref
|
669
|
-
};
|
670
|
-
ttable[mat->stype](mat->storage);
|
671
|
-
|
672
|
-
xfree(mat);
|
673
|
-
}
|
674
|
-
|
675
|
-
/*
|
676
|
-
* call-seq:
|
677
|
-
* dtype -> Symbol
|
678
|
-
*
|
679
|
-
* Get the data type (dtype) of a matrix, e.g., :byte, :int8, :int16, :int32,
|
680
|
-
* :int64, :float32, :float64, :complex64, :complex128, :rational32,
|
681
|
-
* :rational64, :rational128, or :object (the last is a Ruby object).
|
682
|
-
*/
|
683
|
-
static VALUE nm_dtype(VALUE self) {
|
684
|
-
ID dtype = rb_intern(DTYPE_NAMES[NM_DTYPE(self)]);
|
685
|
-
return ID2SYM(dtype);
|
686
|
-
}
|
687
|
-
|
688
|
-
/*
|
689
|
-
* call-seq:
|
690
|
-
* itype -> Symbol or nil
|
691
|
-
*
|
692
|
-
* Get the index data type (dtype) of a matrix. Defined only for yale; others return nil.
|
693
|
-
*/
|
694
|
-
static VALUE nm_itype(VALUE self) {
|
695
|
-
if (NM_STYPE(self) == nm::YALE_STORE) {
|
696
|
-
ID itype = rb_intern(ITYPE_NAMES[NM_ITYPE(self)]);
|
697
|
-
return ID2SYM(itype);
|
698
|
-
}
|
699
|
-
return Qnil;
|
700
|
-
}
|
701
|
-
|
702
|
-
/*
|
703
|
-
* Get the index data type (dtype) of a matrix. Defined only for yale; others return nil.
|
704
|
-
*/
|
705
|
-
static VALUE nm_itype_by_shape(VALUE self, VALUE shape_arg) {
|
706
|
-
|
707
|
-
size_t dim;
|
708
|
-
size_t* shape = interpret_shape(shape_arg, &dim);
|
709
|
-
|
710
|
-
nm::itype_t itype = nm_yale_storage_itype_by_shape(shape);
|
711
|
-
ID itype_id = rb_intern(ITYPE_NAMES[itype]);
|
712
|
-
|
713
|
-
return ID2SYM(itype_id);
|
714
|
-
}
|
715
|
-
|
716
|
-
/*
|
717
|
-
* call-seq:
|
718
|
-
* upcast(first_dtype, second_dtype) -> Symbol
|
719
|
-
*
|
720
|
-
* Given a binary operation between types t1 and t2, what type will be returned?
|
721
|
-
*
|
722
|
-
* This is a singleton method on NMatrix, e.g., NMatrix.upcast(:int32, :int64)
|
723
|
-
*/
|
724
|
-
static VALUE nm_upcast(VALUE self, VALUE t1, VALUE t2) {
|
725
|
-
|
726
|
-
nm::dtype_t d1 = nm_dtype_from_rbsymbol(t1),
|
727
|
-
d2 = nm_dtype_from_rbsymbol(t2);
|
728
|
-
|
729
|
-
return ID2SYM(rb_intern( DTYPE_NAMES[ Upcast[d1][d2] ] ));
|
730
|
-
}
|
731
|
-
|
732
|
-
|
733
|
-
/*
|
734
|
-
* call-seq:
|
735
|
-
default_value -> ...
|
736
|
-
*
|
737
|
-
* Get the default value for the matrix. For dense, this is undefined and will return Qnil. For list, it is user-defined.
|
738
|
-
* For yale, it's going to be some variation on zero, but may be Qfalse or Qnil.
|
739
|
-
*/
|
740
|
-
static VALUE nm_default_value(VALUE self) {
|
741
|
-
switch(NM_STYPE(self)) {
|
742
|
-
case nm::YALE_STORE:
|
743
|
-
return nm_yale_default_value(self);
|
744
|
-
case nm::LIST_STORE:
|
745
|
-
return nm_list_default_value(self);
|
746
|
-
case nm::DENSE_STORE:
|
747
|
-
default:
|
748
|
-
return Qnil;
|
749
|
-
}
|
750
|
-
}
|
751
|
-
|
752
|
-
|
753
|
-
/*
|
754
|
-
* call-seq:
|
755
|
-
* each_with_indices -> Enumerator
|
756
|
-
*
|
757
|
-
* Iterate over all entries of any matrix in standard storage order (as with #each), and include the indices.
|
758
|
-
*/
|
759
|
-
static VALUE nm_each_with_indices(VALUE nmatrix) {
|
760
|
-
volatile VALUE nm = nmatrix;
|
761
|
-
|
762
|
-
switch(NM_STYPE(nm)) {
|
763
|
-
case nm::YALE_STORE:
|
764
|
-
return nm_yale_each_with_indices(nm);
|
765
|
-
case nm::DENSE_STORE:
|
766
|
-
return nm_dense_each_with_indices(nm);
|
767
|
-
case nm::LIST_STORE:
|
768
|
-
return nm_list_each_with_indices(nm, false);
|
769
|
-
default:
|
770
|
-
rb_raise(nm_eDataTypeError, "Not a proper storage type");
|
771
|
-
}
|
772
|
-
}
|
773
|
-
|
774
|
-
/*
|
775
|
-
* call-seq:
|
776
|
-
* each_stored_with_indices -> Enumerator
|
777
|
-
*
|
778
|
-
* Iterate over the stored entries of any matrix. For dense and yale, this iterates over non-zero
|
779
|
-
* entries; for list, this iterates over non-default entries. Yields dim+1 values for each entry:
|
780
|
-
* i, j, ..., and the entry itself.
|
781
|
-
*/
|
782
|
-
static VALUE nm_each_stored_with_indices(VALUE nmatrix) {
|
783
|
-
volatile VALUE nm = nmatrix;
|
784
|
-
|
785
|
-
switch(NM_STYPE(nm)) {
|
786
|
-
case nm::YALE_STORE:
|
787
|
-
return nm_yale_each_stored_with_indices(nm);
|
788
|
-
case nm::DENSE_STORE:
|
789
|
-
return nm_dense_each_with_indices(nm);
|
790
|
-
case nm::LIST_STORE:
|
791
|
-
return nm_list_each_with_indices(nm, true);
|
792
|
-
default:
|
793
|
-
rb_raise(nm_eDataTypeError, "Not a proper storage type");
|
794
|
-
}
|
795
|
-
}
|
796
|
-
|
797
|
-
|
798
|
-
/*
|
799
|
-
* Equality operator. Returns a single true or false value indicating whether
|
800
|
-
* the matrices are equivalent.
|
801
|
-
*
|
802
|
-
* For elementwise, use =~ instead.
|
803
|
-
*
|
804
|
-
* This method will raise an exception if dimensions do not match.
|
805
|
-
*/
|
806
|
-
static VALUE nm_eqeq(VALUE left, VALUE right) {
|
807
|
-
NMATRIX *l, *r;
|
808
|
-
|
809
|
-
CheckNMatrixType(left);
|
810
|
-
CheckNMatrixType(right);
|
811
|
-
|
812
|
-
UnwrapNMatrix(left, l);
|
813
|
-
UnwrapNMatrix(right, r);
|
814
|
-
|
815
|
-
if (l->stype != r->stype)
|
816
|
-
rb_raise(rb_eNotImpError, "comparison between different matrix stypes not yet implemented");
|
817
|
-
|
818
|
-
bool result = false;
|
819
|
-
|
820
|
-
switch(l->stype) {
|
821
|
-
case nm::DENSE_STORE:
|
822
|
-
result = nm_dense_storage_eqeq(l->storage, r->storage);
|
823
|
-
break;
|
824
|
-
case nm::LIST_STORE:
|
825
|
-
result = nm_list_storage_eqeq(l->storage, r->storage);
|
826
|
-
break;
|
827
|
-
case nm::YALE_STORE:
|
828
|
-
result = nm_yale_storage_eqeq(l->storage, r->storage);
|
829
|
-
break;
|
830
|
-
}
|
831
|
-
|
832
|
-
return result ? Qtrue : Qfalse;
|
833
|
-
}
|
834
|
-
|
835
|
-
DEF_ELEMENTWISE_RUBY_ACCESSOR(ADD, add)
|
836
|
-
DEF_ELEMENTWISE_RUBY_ACCESSOR(SUB, subtract)
|
837
|
-
DEF_ELEMENTWISE_RUBY_ACCESSOR(MUL, multiply)
|
838
|
-
DEF_ELEMENTWISE_RUBY_ACCESSOR(DIV, divide)
|
839
|
-
DEF_ELEMENTWISE_RUBY_ACCESSOR(POW, power)
|
840
|
-
DEF_ELEMENTWISE_RUBY_ACCESSOR(MOD, mod)
|
841
|
-
DEF_ELEMENTWISE_RUBY_ACCESSOR(EQEQ, eqeq)
|
842
|
-
DEF_ELEMENTWISE_RUBY_ACCESSOR(NEQ, neq)
|
843
|
-
DEF_ELEMENTWISE_RUBY_ACCESSOR(LEQ, leq)
|
844
|
-
DEF_ELEMENTWISE_RUBY_ACCESSOR(GEQ, geq)
|
845
|
-
DEF_ELEMENTWISE_RUBY_ACCESSOR(LT, lt)
|
846
|
-
DEF_ELEMENTWISE_RUBY_ACCESSOR(GT, gt)
|
847
|
-
|
848
|
-
/*
|
849
|
-
* call-seq:
|
850
|
-
* hermitian? -> Boolean
|
851
|
-
*
|
852
|
-
* Is this matrix hermitian?
|
853
|
-
*
|
854
|
-
* Definition: http://en.wikipedia.org/wiki/Hermitian_matrix
|
855
|
-
*
|
856
|
-
* For non-complex matrices, this function should return the same result as symmetric?.
|
857
|
-
*/
|
858
|
-
static VALUE nm_hermitian(VALUE self) {
|
859
|
-
return is_symmetric(self, true);
|
860
|
-
}
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
/*
|
865
|
-
* call-seq:
|
866
|
-
* complex_conjugate -> NMatrix
|
867
|
-
*
|
868
|
-
* Transform the matrix (in-place) to its complex conjugate. Only works on complex matrices.
|
869
|
-
*
|
870
|
-
* FIXME: For non-complex matrices, someone needs to implement a non-in-place complex conjugate (which doesn't use a bang).
|
871
|
-
* Bang should imply that no copy is being made, even temporarily.
|
872
|
-
*/
|
873
|
-
static VALUE nm_complex_conjugate_bang(VALUE self) {
|
874
|
-
NMATRIX* m;
|
875
|
-
void* elem;
|
876
|
-
size_t size, p;
|
877
|
-
|
878
|
-
UnwrapNMatrix(self, m);
|
879
|
-
|
880
|
-
if (m->stype == nm::DENSE_STORE) {
|
881
|
-
|
882
|
-
size = nm_storage_count_max_elements(NM_STORAGE(self));
|
883
|
-
elem = NM_STORAGE_DENSE(self)->elements;
|
884
|
-
|
885
|
-
} else if (m->stype == nm::YALE_STORE) {
|
886
|
-
|
887
|
-
size = nm_yale_storage_get_size(NM_STORAGE_YALE(self));
|
888
|
-
elem = NM_STORAGE_YALE(self)->a;
|
889
|
-
|
890
|
-
} else {
|
891
|
-
rb_raise(rb_eNotImpError, "please cast to yale or dense (complex) first");
|
892
|
-
}
|
893
|
-
|
894
|
-
// Walk through and negate the imaginary component
|
895
|
-
if (NM_DTYPE(self) == nm::COMPLEX64) {
|
896
|
-
|
897
|
-
for (p = 0; p < size; ++p) {
|
898
|
-
reinterpret_cast<nm::Complex64*>(elem)[p].i = -reinterpret_cast<nm::Complex64*>(elem)[p].i;
|
899
|
-
}
|
900
|
-
|
901
|
-
} else if (NM_DTYPE(self) == nm::COMPLEX128) {
|
902
|
-
|
903
|
-
for (p = 0; p < size; ++p) {
|
904
|
-
reinterpret_cast<nm::Complex128*>(elem)[p].i = -reinterpret_cast<nm::Complex128*>(elem)[p].i;
|
905
|
-
}
|
906
|
-
|
907
|
-
} else {
|
908
|
-
rb_raise(nm_eDataTypeError, "can only calculate in-place complex conjugate on matrices of type :complex64 or :complex128");
|
909
|
-
}
|
910
|
-
|
911
|
-
return self;
|
912
|
-
}
|
913
|
-
|
914
|
-
/*
|
915
|
-
* Helper function for creating a matrix. You have to create the storage and pass it in, but you don't
|
916
|
-
* need to worry about deleting it.
|
917
|
-
*/
|
918
|
-
NMATRIX* nm_create(nm::stype_t stype, STORAGE* storage) {
|
919
|
-
NMATRIX* mat = ALLOC(NMATRIX);
|
920
|
-
|
921
|
-
mat->stype = stype;
|
922
|
-
mat->storage = storage;
|
923
|
-
|
924
|
-
return mat;
|
925
|
-
}
|
926
|
-
|
927
|
-
/*
|
928
|
-
* call-seq:
|
929
|
-
* new -> NMatrix
|
930
|
-
*
|
931
|
-
* Create a new NMatrix.
|
932
|
-
*
|
933
|
-
* There are several ways to do this. In every case, the constructor needs to know the dtype, the dimensions, the stype,
|
934
|
-
* and either an initial capacity (:yale) or some number of initial values (:list needs exactly one initial value, but
|
935
|
-
* :dense can accept an array). In many cases, the parameters can be guessed from other parameters.
|
936
|
-
*
|
937
|
-
* Here is the full form for a :dense 3x4 :float64 matrix initialized to alternate the values 0.0, 1.0, and 2.0:
|
938
|
-
*
|
939
|
-
* NMatrix.new(:dense, [3,4], [0.0, 1.0, 2.0], :float64)
|
940
|
-
*
|
941
|
-
* Since :dense is the default, we can actually leave that out. Additionally, the constructor will parse 0.0 and
|
942
|
-
* interpret that to be a :float64. So we can actually short-hand this as follows:
|
943
|
-
*
|
944
|
-
* NMatrix.new([3,4], [0.0,1,2])
|
945
|
-
*
|
946
|
-
* Note that :list and :yale matrices will not accept a default value array. For list storage, a single default value
|
947
|
-
* is permissible, which will be treated as the background for the sparse matrix and defaults to 0:
|
948
|
-
*
|
949
|
-
* NMatrix.new(:list, [3,4], 0) # standard :int64 sparse matrix
|
950
|
-
* NMatrix.new(:list, [2,3], 1.0) # :float64 sparse matrix: [[1,1,1],[1,1,1]] (no storage used)
|
951
|
-
* NMatrix.new(:list, [3,4], [0,1]) # undefined behavior, will probably fill matrix with 0. Avoid this.
|
952
|
-
*
|
953
|
-
* For Yale storage, the default value must always be 0. Thus, if you provide an initial value, it will be interpreted
|
954
|
-
* as the initial matrix capacity.
|
955
|
-
*
|
956
|
-
* NMatrix.new(:yale, [4,3], :rational128) # Use default initial capacity. Most common.
|
957
|
-
* NMatrix.new(:yale, [3,4], 1000) # Error! Needs a dtype!
|
958
|
-
* NMatrix.new(:yale, [3,4], 1000, :int64) # Silly! Why would a 3x4 sparse matrix need storage space of 1,000?
|
959
|
-
* NMatrix.new(:yale, [3,4], 0.0, :float64) # Totally ignores non-sensical 3rd arg and creates 7 storage instead.
|
960
|
-
* NMatrix.new(:yale, [3,4], 8, :rational128) # Initial capacity of 8 rationals.
|
961
|
-
*
|
962
|
-
* That leaves only two other notes. First of all, if your matrix is square, you don't need to type [3,3] for 3x3.
|
963
|
-
* Instead, just do 3:
|
964
|
-
*
|
965
|
-
* NMatrix.new(3, [0,1,2], :rational128) # dense 3x3 rational matrix consisting of columns of 0s, 1s, and 2s
|
966
|
-
*
|
967
|
-
* Secondly, if you create a dense matrix without initial values, you may see unpredictable results! It'll fill the
|
968
|
-
* matrix with whatever is already in memory, not with zeros.
|
969
|
-
*
|
970
|
-
* NMatrix.new(:dense, 4, :int64)
|
971
|
-
* # => [8, 140486578196280, 0, 0] [0, 0, 0, 0] [0, 0, 0, 140486608794928] [140486577962496, -4294967280, 1, 140734734392208]
|
972
|
-
*
|
973
|
-
* There is one additional constructor for advanced users, which takes seven arguments and is only for creating Yale
|
974
|
-
* matrices with known IA, JA, and A arrays. This is used primarily internally for IO, e.g., reading Matlab matrices,
|
975
|
-
* which are stored in old Yale (not our Yale) format. But be careful; there are no overflow warnings. All of these
|
976
|
-
* constructors are defined for power-users. Everyone else should probably resort to the shortcut functions defined in
|
977
|
-
* shortcuts.rb.
|
978
|
-
*/
|
979
|
-
static VALUE nm_init(int argc, VALUE* argv, VALUE nm) {
|
980
|
-
|
981
|
-
if (argc < 2) {
|
982
|
-
rb_raise(rb_eArgError, "Expected 2-4 arguments (or 7 for internal Yale creation)");
|
983
|
-
return Qnil;
|
984
|
-
}
|
985
|
-
|
986
|
-
/* First, determine stype (dense by default) */
|
987
|
-
nm::stype_t stype;
|
988
|
-
size_t offset = 0;
|
989
|
-
|
990
|
-
if (!SYMBOL_P(argv[0]) && TYPE(argv[0]) != T_STRING) {
|
991
|
-
stype = nm::DENSE_STORE;
|
992
|
-
|
993
|
-
} else {
|
994
|
-
// 0: String or Symbol
|
995
|
-
stype = interpret_stype(argv[0]);
|
996
|
-
offset = 1;
|
997
|
-
}
|
998
|
-
|
999
|
-
// If there are 7 arguments and Yale, refer to a different init function with fewer sanity checks.
|
1000
|
-
if (argc == 7) {
|
1001
|
-
if (stype == nm::YALE_STORE) {
|
1002
|
-
return nm_init_yale_from_old_yale(argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], nm);
|
1003
|
-
|
1004
|
-
} else {
|
1005
|
-
rb_raise(rb_eArgError, "Expected 2-4 arguments (or 7 for internal Yale creation)");
|
1006
|
-
}
|
1007
|
-
}
|
1008
|
-
|
1009
|
-
// 1: Array or Fixnum
|
1010
|
-
size_t dim;
|
1011
|
-
size_t* shape = interpret_shape(argv[offset], &dim);
|
1012
|
-
|
1013
|
-
// 2-3: dtype
|
1014
|
-
nm::dtype_t dtype = interpret_dtype(argc-1-offset, argv+offset+1, stype);
|
1015
|
-
|
1016
|
-
size_t init_cap = 0, init_val_len = 0;
|
1017
|
-
void* init_val = NULL;
|
1018
|
-
if (!SYMBOL_P(argv[1+offset]) || TYPE(argv[1+offset]) == T_ARRAY) {
|
1019
|
-
// Initial value provided (could also be initial capacity, if yale).
|
1020
|
-
|
1021
|
-
if (stype == nm::YALE_STORE && NM_RUBYVAL_IS_NUMERIC(argv[1+offset])) {
|
1022
|
-
init_cap = FIX2UINT(argv[1+offset]);
|
1023
|
-
|
1024
|
-
} else {
|
1025
|
-
// 4: initial value / dtype
|
1026
|
-
init_val = interpret_initial_value(argv[1+offset], dtype);
|
1027
|
-
|
1028
|
-
if (TYPE(argv[1+offset]) == T_ARRAY) init_val_len = RARRAY_LEN(argv[1+offset]);
|
1029
|
-
else init_val_len = 1;
|
1030
|
-
}
|
1031
|
-
|
1032
|
-
} else {
|
1033
|
-
// DType is RUBYOBJ.
|
1034
|
-
|
1035
|
-
if (stype == nm::DENSE_STORE) {
|
1036
|
-
/*
|
1037
|
-
* No need to initialize dense with any kind of default value unless it's
|
1038
|
-
* an RUBYOBJ matrix.
|
1039
|
-
*/
|
1040
|
-
if (dtype == nm::RUBYOBJ) {
|
1041
|
-
// Pretend [nil] was passed for RUBYOBJ.
|
1042
|
-
init_val = ALLOC(VALUE);
|
1043
|
-
*(VALUE*)init_val = Qnil;
|
1044
|
-
|
1045
|
-
init_val_len = 1;
|
1046
|
-
|
1047
|
-
} else {
|
1048
|
-
init_val = NULL;
|
1049
|
-
}
|
1050
|
-
} else if (stype == nm::LIST_STORE) {
|
1051
|
-
init_val = ALLOC_N(char, DTYPE_SIZES[dtype]);
|
1052
|
-
std::memset(init_val, 0, DTYPE_SIZES[dtype]);
|
1053
|
-
}
|
1054
|
-
}
|
1055
|
-
|
1056
|
-
// TODO: Update to allow an array as the initial value.
|
1057
|
-
NMATRIX* nmatrix;
|
1058
|
-
UnwrapNMatrix(nm, nmatrix);
|
1059
|
-
|
1060
|
-
nmatrix->stype = stype;
|
1061
|
-
|
1062
|
-
switch (stype) {
|
1063
|
-
case nm::DENSE_STORE:
|
1064
|
-
nmatrix->storage = (STORAGE*)nm_dense_storage_create(dtype, shape, dim, init_val, init_val_len);
|
1065
|
-
break;
|
1066
|
-
|
1067
|
-
case nm::LIST_STORE:
|
1068
|
-
nmatrix->storage = (STORAGE*)nm_list_storage_create(dtype, shape, dim, init_val);
|
1069
|
-
break;
|
1070
|
-
|
1071
|
-
case nm::YALE_STORE:
|
1072
|
-
nmatrix->storage = (STORAGE*)nm_yale_storage_create(dtype, shape, dim, init_cap, nm::UINT8);
|
1073
|
-
nm_yale_storage_init((YALE_STORAGE*)(nmatrix->storage), NULL);
|
1074
|
-
break;
|
1075
|
-
}
|
1076
|
-
|
1077
|
-
return nm;
|
1078
|
-
}
|
1079
|
-
|
1080
|
-
|
1081
|
-
/*
|
1082
|
-
* call-seq:
|
1083
|
-
* cast(stype) -> NMatrix
|
1084
|
-
* cast(stype, dtype, sparse_basis) -> NMatrix
|
1085
|
-
*
|
1086
|
-
* Copy constructor for changing dtypes and stypes.
|
1087
|
-
*/
|
1088
|
-
static VALUE nm_cast(VALUE self, VALUE new_stype_symbol, VALUE new_dtype_symbol, VALUE init) {
|
1089
|
-
nm::dtype_t new_dtype = nm_dtype_from_rbsymbol(new_dtype_symbol);
|
1090
|
-
nm::stype_t new_stype = nm_stype_from_rbsymbol(new_stype_symbol);
|
1091
|
-
|
1092
|
-
CheckNMatrixType(self);
|
1093
|
-
|
1094
|
-
NMATRIX *lhs = ALLOC(NMATRIX),
|
1095
|
-
*rhs;
|
1096
|
-
lhs->stype = new_stype;
|
1097
|
-
|
1098
|
-
UnwrapNMatrix( self, rhs );
|
1099
|
-
|
1100
|
-
void* init_ptr = ALLOCA_N(char, DTYPE_SIZES[new_dtype]);
|
1101
|
-
rubyval_to_cval(init, new_dtype, init_ptr);
|
1102
|
-
|
1103
|
-
// Copy the storage
|
1104
|
-
CAST_TABLE(cast_copy);
|
1105
|
-
lhs->storage = cast_copy[lhs->stype][rhs->stype](rhs->storage, new_dtype, init_ptr);
|
1106
|
-
|
1107
|
-
STYPE_MARK_TABLE(mark);
|
1108
|
-
|
1109
|
-
return Data_Wrap_Struct(CLASS_OF(self), mark[lhs->stype], nm_delete, lhs);
|
1110
|
-
}
|
1111
|
-
|
1112
|
-
/*
|
1113
|
-
* Copy constructor for transposing.
|
1114
|
-
*/
|
1115
|
-
static VALUE nm_init_transposed(VALUE self) {
|
1116
|
-
static STORAGE* (*storage_copy_transposed[nm::NUM_STYPES])(const STORAGE* rhs_base) = {
|
1117
|
-
nm_dense_storage_copy_transposed,
|
1118
|
-
nm_list_storage_copy_transposed,
|
1119
|
-
nm_yale_storage_copy_transposed
|
1120
|
-
};
|
1121
|
-
|
1122
|
-
NMATRIX* lhs = nm_create( NM_STYPE(self),
|
1123
|
-
storage_copy_transposed[NM_STYPE(self)]( NM_STORAGE(self) )
|
1124
|
-
);
|
1125
|
-
|
1126
|
-
STYPE_MARK_TABLE(mark);
|
1127
|
-
|
1128
|
-
return Data_Wrap_Struct(CLASS_OF(self), mark[lhs->stype], nm_delete, lhs);
|
1129
|
-
}
|
1130
|
-
|
1131
|
-
/*
|
1132
|
-
* Copy constructor for no change of dtype or stype (used for #initialize_copy hook).
|
1133
|
-
*/
|
1134
|
-
static VALUE nm_init_copy(VALUE copy, VALUE original) {
|
1135
|
-
NMATRIX *lhs, *rhs;
|
1136
|
-
|
1137
|
-
CheckNMatrixType(original);
|
1138
|
-
|
1139
|
-
if (copy == original) return copy;
|
1140
|
-
|
1141
|
-
UnwrapNMatrix( original, rhs );
|
1142
|
-
UnwrapNMatrix( copy, lhs );
|
1143
|
-
|
1144
|
-
lhs->stype = rhs->stype;
|
1145
|
-
|
1146
|
-
// Copy the storage
|
1147
|
-
CAST_TABLE(ttable);
|
1148
|
-
lhs->storage = ttable[lhs->stype][rhs->stype](rhs->storage, rhs->storage->dtype, NULL);
|
1149
|
-
|
1150
|
-
return copy;
|
1151
|
-
}
|
1152
|
-
|
1153
|
-
/*
|
1154
|
-
* Get major, minor, and release components of NMatrix::VERSION. Store in function parameters.
|
1155
|
-
*/
|
1156
|
-
static void get_version_info(uint16_t& major, uint16_t& minor, uint16_t& release) {
|
1157
|
-
// Get VERSION and split it on periods. Result is an Array.
|
1158
|
-
VALUE version = rb_funcall(rb_const_get(cNMatrix, rb_intern("VERSION")), rb_intern("split"), 1, rb_str_new_cstr("."));
|
1159
|
-
VALUE* ary = RARRAY_PTR(version); // major, minor, and release
|
1160
|
-
|
1161
|
-
// Convert each to an integer
|
1162
|
-
VALUE maj = rb_funcall(ary[0], rb_intern("to_i"), 0);
|
1163
|
-
VALUE min = rb_funcall(ary[1], rb_intern("to_i"), 0);
|
1164
|
-
VALUE rel = rb_funcall(ary[2], rb_intern("to_i"), 0);
|
1165
|
-
|
1166
|
-
major = static_cast<uint16_t>(nm::RubyObject(maj));
|
1167
|
-
minor = static_cast<uint16_t>(nm::RubyObject(min));
|
1168
|
-
release = static_cast<uint16_t>(nm::RubyObject(rel));
|
1169
|
-
}
|
1170
|
-
|
1171
|
-
|
1172
|
-
/*
|
1173
|
-
* Interpret the NMatrix::write symmetry argument (which should be nil or a symbol). Return a symm_t (enum).
|
1174
|
-
*/
|
1175
|
-
static nm::symm_t interpret_symm(VALUE symm) {
|
1176
|
-
if (symm == Qnil) return nm::NONSYMM;
|
1177
|
-
|
1178
|
-
ID rb_symm = rb_intern("symmetric"),
|
1179
|
-
rb_skew = rb_intern("skew"),
|
1180
|
-
rb_herm = rb_intern("hermitian");
|
1181
|
-
// nm_rb_upper, nm_rb_lower already set
|
1182
|
-
|
1183
|
-
ID symm_id = rb_to_id(symm);
|
1184
|
-
|
1185
|
-
if (symm_id == rb_symm) return nm::SYMM;
|
1186
|
-
else if (symm_id == rb_skew) return nm::SKEW;
|
1187
|
-
else if (symm_id == rb_herm) return nm::HERM;
|
1188
|
-
else if (symm_id == nm_rb_upper) return nm::UPPER;
|
1189
|
-
else if (symm_id == nm_rb_lower) return nm::LOWER;
|
1190
|
-
else rb_raise(rb_eArgError, "unrecognized symmetry argument");
|
1191
|
-
|
1192
|
-
return nm::NONSYMM;
|
1193
|
-
}
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
void read_padded_shape(std::ifstream& f, size_t dim, size_t* shape, nm::itype_t itype) {
|
1198
|
-
NAMED_ITYPE_TEMPLATE_TABLE(ttable, nm::read_padded_shape, void, std::ifstream&, size_t, size_t*)
|
1199
|
-
|
1200
|
-
ttable[itype](f, dim, shape);
|
1201
|
-
}
|
1202
|
-
|
1203
|
-
|
1204
|
-
void write_padded_shape(std::ofstream& f, size_t dim, size_t* shape, nm::itype_t itype) {
|
1205
|
-
NAMED_ITYPE_TEMPLATE_TABLE(ttable, nm::write_padded_shape, void, std::ofstream&, size_t, size_t*)
|
1206
|
-
|
1207
|
-
ttable[itype](f, dim, shape);
|
1208
|
-
}
|
1209
|
-
|
1210
|
-
|
1211
|
-
void read_padded_yale_elements(std::ifstream& f, YALE_STORAGE* storage, size_t length, nm::symm_t symm, nm::dtype_t dtype, nm::itype_t itype) {
|
1212
|
-
NAMED_LI_DTYPE_TEMPLATE_TABLE_NO_ROBJ(ttable, nm::read_padded_yale_elements, void, std::ifstream&, YALE_STORAGE*, size_t, nm::symm_t)
|
1213
|
-
|
1214
|
-
ttable[dtype][itype](f, storage, length, symm);
|
1215
|
-
}
|
1216
|
-
|
1217
|
-
|
1218
|
-
void write_padded_yale_elements(std::ofstream& f, YALE_STORAGE* storage, size_t length, nm::symm_t symm, nm::dtype_t dtype, nm::itype_t itype) {
|
1219
|
-
NAMED_LI_DTYPE_TEMPLATE_TABLE_NO_ROBJ(ttable, nm::write_padded_yale_elements, void, std::ofstream& f, YALE_STORAGE*, size_t, nm::symm_t)
|
1220
|
-
|
1221
|
-
ttable[dtype][itype](f, storage, length, symm);
|
1222
|
-
}
|
1223
|
-
|
1224
|
-
|
1225
|
-
void read_padded_dense_elements(std::ifstream& f, DENSE_STORAGE* storage, nm::symm_t symm, nm::dtype_t dtype) {
|
1226
|
-
NAMED_DTYPE_TEMPLATE_TABLE_NO_ROBJ(ttable, nm::read_padded_dense_elements, void, std::ifstream&, DENSE_STORAGE*, nm::symm_t)
|
1227
|
-
|
1228
|
-
ttable[dtype](f, storage, symm);
|
1229
|
-
}
|
1230
|
-
|
1231
|
-
|
1232
|
-
void write_padded_dense_elements(std::ofstream& f, DENSE_STORAGE* storage, nm::symm_t symm, nm::dtype_t dtype) {
|
1233
|
-
NAMED_DTYPE_TEMPLATE_TABLE_NO_ROBJ(ttable, nm::write_padded_dense_elements, void, std::ofstream& f, DENSE_STORAGE*, nm::symm_t)
|
1234
|
-
|
1235
|
-
ttable[dtype](f, storage, symm);
|
1236
|
-
}
|
1237
|
-
|
1238
|
-
|
1239
|
-
/*
|
1240
|
-
* Helper function to get exceptions in the module Errno (e.g., ENOENT). Example:
|
1241
|
-
*
|
1242
|
-
* rb_raise(rb_get_errno_exc("ENOENT"), RSTRING_PTR(filename));
|
1243
|
-
*/
|
1244
|
-
static VALUE rb_get_errno_exc(const char* which) {
|
1245
|
-
return rb_const_get(rb_const_get(rb_cObject, rb_intern("Errno")), rb_intern(which));
|
1246
|
-
}
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
/*
|
1251
|
-
* Binary file writer for NMatrix standard format. file should be a path, which we aren't going to
|
1252
|
-
* check very carefully (in other words, this function should generally be called from a Ruby
|
1253
|
-
* helper method). Function also takes a symmetry argument, which allows us to specify that we only want to
|
1254
|
-
* save the upper triangular portion of the matrix (or if the matrix is a lower triangular matrix, only
|
1255
|
-
* the lower triangular portion). nil means regular storage.
|
1256
|
-
*/
|
1257
|
-
static VALUE nm_write(int argc, VALUE* argv, VALUE self) {
|
1258
|
-
using std::ofstream;
|
1259
|
-
|
1260
|
-
if (argc < 1 || argc > 2) {
|
1261
|
-
rb_raise(rb_eArgError, "Expected one or two arguments");
|
1262
|
-
}
|
1263
|
-
VALUE file = argv[0],
|
1264
|
-
symm = argc == 1 ? Qnil : argv[1];
|
1265
|
-
|
1266
|
-
NMATRIX* nmatrix;
|
1267
|
-
UnwrapNMatrix( self, nmatrix );
|
1268
|
-
|
1269
|
-
nm::symm_t symm_ = interpret_symm(symm);
|
1270
|
-
nm::itype_t itype = (nmatrix->stype == nm::YALE_STORE) ? reinterpret_cast<YALE_STORAGE*>(nmatrix->storage)->itype : nm::UINT32;
|
1271
|
-
|
1272
|
-
if (nmatrix->storage->dtype == nm::RUBYOBJ) {
|
1273
|
-
rb_raise(rb_eNotImpError, "Ruby Object writing is not implemented yet");
|
1274
|
-
}
|
1275
|
-
|
1276
|
-
// Get the dtype, stype, itype, and symm and ensure they're the correct number of bytes.
|
1277
|
-
uint8_t st = static_cast<uint8_t>(nmatrix->stype),
|
1278
|
-
dt = static_cast<uint8_t>(nmatrix->storage->dtype),
|
1279
|
-
sm = static_cast<uint8_t>(symm_),
|
1280
|
-
it = static_cast<uint8_t>(itype);
|
1281
|
-
uint16_t dim = nmatrix->storage->dim;
|
1282
|
-
|
1283
|
-
// Check arguments before starting to write.
|
1284
|
-
if (nmatrix->stype == nm::LIST_STORE) rb_raise(nm_eStorageTypeError, "cannot save list matrix; cast to yale or dense first");
|
1285
|
-
if (symm_ != nm::NONSYMM) {
|
1286
|
-
if (dim != 2) rb_raise(rb_eArgError, "symmetry/triangularity not defined for a non-2D matrix");
|
1287
|
-
if (nmatrix->storage->shape[0] != nmatrix->storage->shape[1])
|
1288
|
-
rb_raise(rb_eArgError, "symmetry/triangularity not defined for a non-square matrix");
|
1289
|
-
if (symm_ == nm::HERM &&
|
1290
|
-
dt != static_cast<uint8_t>(nm::COMPLEX64) && dt != static_cast<uint8_t>(nm::COMPLEX128) && dt != static_cast<uint8_t>(nm::RUBYOBJ))
|
1291
|
-
rb_raise(rb_eArgError, "cannot save a non-complex matrix as hermitian");
|
1292
|
-
}
|
1293
|
-
|
1294
|
-
ofstream f(RSTRING_PTR(file), std::ios::out | std::ios::binary);
|
1295
|
-
|
1296
|
-
// Get the NMatrix version information.
|
1297
|
-
uint16_t major, minor, release, null16 = 0;
|
1298
|
-
get_version_info(major, minor, release);
|
1299
|
-
|
1300
|
-
// WRITE FIRST 64-BIT BLOCK
|
1301
|
-
f.write(reinterpret_cast<const char*>(&major), sizeof(uint16_t));
|
1302
|
-
f.write(reinterpret_cast<const char*>(&minor), sizeof(uint16_t));
|
1303
|
-
f.write(reinterpret_cast<const char*>(&release), sizeof(uint16_t));
|
1304
|
-
f.write(reinterpret_cast<const char*>(&null16), sizeof(uint16_t));
|
1305
|
-
|
1306
|
-
// WRITE SECOND 64-BIT BLOCK
|
1307
|
-
f.write(reinterpret_cast<const char*>(&dt), sizeof(uint8_t));
|
1308
|
-
f.write(reinterpret_cast<const char*>(&st), sizeof(uint8_t));
|
1309
|
-
f.write(reinterpret_cast<const char*>(&it), sizeof(uint8_t));
|
1310
|
-
f.write(reinterpret_cast<const char*>(&sm), sizeof(uint8_t));
|
1311
|
-
f.write(reinterpret_cast<const char*>(&null16), sizeof(uint16_t));
|
1312
|
-
f.write(reinterpret_cast<const char*>(&dim), sizeof(uint16_t));
|
1313
|
-
|
1314
|
-
// Write shape (in 64-bit blocks)
|
1315
|
-
write_padded_shape(f, nmatrix->storage->dim, nmatrix->storage->shape, itype);
|
1316
|
-
|
1317
|
-
if (nmatrix->stype == nm::DENSE_STORE) {
|
1318
|
-
write_padded_dense_elements(f, reinterpret_cast<DENSE_STORAGE*>(nmatrix->storage), symm_, nmatrix->storage->dtype);
|
1319
|
-
} else if (nmatrix->stype == nm::YALE_STORE) {
|
1320
|
-
YALE_STORAGE* s = reinterpret_cast<YALE_STORAGE*>(nmatrix->storage);
|
1321
|
-
uint32_t ndnz = s->ndnz,
|
1322
|
-
length = nm_yale_storage_get_size(s);
|
1323
|
-
f.write(reinterpret_cast<const char*>(&ndnz), sizeof(uint32_t));
|
1324
|
-
f.write(reinterpret_cast<const char*>(&length), sizeof(uint32_t));
|
1325
|
-
|
1326
|
-
write_padded_yale_elements(f, s, length, symm_, s->dtype, itype);
|
1327
|
-
}
|
1328
|
-
|
1329
|
-
f.close();
|
1330
|
-
|
1331
|
-
return Qtrue;
|
1332
|
-
}
|
1333
|
-
|
1334
|
-
|
1335
|
-
/*
|
1336
|
-
* Binary file reader for NMatrix standard format. file should be a path, which we aren't going to
|
1337
|
-
* check very carefully (in other words, this function should generally be called from a Ruby
|
1338
|
-
* helper method).
|
1339
|
-
*
|
1340
|
-
* Note that currently, this function will by default refuse to read files that are newer than
|
1341
|
-
* your version of NMatrix. To force an override, set the second argument to anything other than nil.
|
1342
|
-
*
|
1343
|
-
* Returns an NMatrix Ruby object.
|
1344
|
-
*/
|
1345
|
-
static VALUE nm_read(int argc, VALUE* argv, VALUE self) {
|
1346
|
-
using std::ifstream;
|
1347
|
-
|
1348
|
-
VALUE file, force_;
|
1349
|
-
|
1350
|
-
// Read the arguments
|
1351
|
-
rb_scan_args(argc, argv, "11", &file, &force_);
|
1352
|
-
bool force = (force_ != Qnil && force_ != Qfalse);
|
1353
|
-
|
1354
|
-
|
1355
|
-
if (!RB_FILE_EXISTS(file)) { // FIXME: Errno::ENOENT
|
1356
|
-
rb_raise(rb_get_errno_exc("ENOENT"), "%s", RSTRING_PTR(file));
|
1357
|
-
}
|
1358
|
-
|
1359
|
-
// Open a file stream
|
1360
|
-
ifstream f(RSTRING_PTR(file), std::ios::in | std::ios::binary);
|
1361
|
-
|
1362
|
-
uint16_t major, minor, release;
|
1363
|
-
get_version_info(major, minor, release); // compare to NMatrix version
|
1364
|
-
|
1365
|
-
uint16_t fmajor, fminor, frelease, null16;
|
1366
|
-
|
1367
|
-
// READ FIRST 64-BIT BLOCK
|
1368
|
-
f.read(reinterpret_cast<char*>(&fmajor), sizeof(uint16_t));
|
1369
|
-
f.read(reinterpret_cast<char*>(&fminor), sizeof(uint16_t));
|
1370
|
-
f.read(reinterpret_cast<char*>(&frelease), sizeof(uint16_t));
|
1371
|
-
f.read(reinterpret_cast<char*>(&null16), sizeof(uint16_t));
|
1372
|
-
|
1373
|
-
int ver = major * 10000 + minor * 100 + release,
|
1374
|
-
fver = fmajor * 10000 + fminor * 100 + release;
|
1375
|
-
if (fver > ver && force == false) {
|
1376
|
-
rb_raise(rb_eIOError, "File was created in newer version of NMatrix than current");
|
1377
|
-
}
|
1378
|
-
if (null16 != 0) fprintf(stderr, "Warning: Expected zero padding was not zero\n");
|
1379
|
-
|
1380
|
-
uint8_t dt, st, it, sm;
|
1381
|
-
uint16_t dim;
|
1382
|
-
|
1383
|
-
// READ SECOND 64-BIT BLOCK
|
1384
|
-
f.read(reinterpret_cast<char*>(&dt), sizeof(uint8_t));
|
1385
|
-
f.read(reinterpret_cast<char*>(&st), sizeof(uint8_t));
|
1386
|
-
f.read(reinterpret_cast<char*>(&it), sizeof(uint8_t));
|
1387
|
-
f.read(reinterpret_cast<char*>(&sm), sizeof(uint8_t));
|
1388
|
-
f.read(reinterpret_cast<char*>(&null16), sizeof(uint16_t));
|
1389
|
-
f.read(reinterpret_cast<char*>(&dim), sizeof(uint16_t));
|
1390
|
-
|
1391
|
-
if (null16 != 0) fprintf(stderr, "Warning: Expected zero padding was not zero\n");
|
1392
|
-
nm::stype_t stype = static_cast<nm::stype_t>(st);
|
1393
|
-
nm::dtype_t dtype = static_cast<nm::dtype_t>(dt);
|
1394
|
-
nm::symm_t symm = static_cast<nm::symm_t>(sm);
|
1395
|
-
nm::itype_t itype = static_cast<nm::itype_t>(it);
|
1396
|
-
|
1397
|
-
// READ NEXT FEW 64-BIT BLOCKS
|
1398
|
-
size_t* shape = ALLOC_N(size_t, dim);
|
1399
|
-
read_padded_shape(f, dim, shape, itype);
|
1400
|
-
|
1401
|
-
STORAGE* s;
|
1402
|
-
if (stype == nm::DENSE_STORE) {
|
1403
|
-
s = nm_dense_storage_create(dtype, shape, dim, NULL, 0);
|
1404
|
-
|
1405
|
-
read_padded_dense_elements(f, reinterpret_cast<DENSE_STORAGE*>(s), symm, dtype);
|
1406
|
-
|
1407
|
-
} else if (stype == nm::YALE_STORE) {
|
1408
|
-
uint32_t ndnz, length;
|
1409
|
-
|
1410
|
-
// READ YALE-SPECIFIC 64-BIT BLOCK
|
1411
|
-
f.read(reinterpret_cast<char*>(&ndnz), sizeof(uint32_t));
|
1412
|
-
f.read(reinterpret_cast<char*>(&length), sizeof(uint32_t));
|
1413
|
-
|
1414
|
-
s = nm_yale_storage_create(dtype, shape, dim, length, itype); // set length as init capacity
|
1415
|
-
|
1416
|
-
read_padded_yale_elements(f, reinterpret_cast<YALE_STORAGE*>(s), length, symm, dtype, itype);
|
1417
|
-
} else {
|
1418
|
-
rb_raise(nm_eStorageTypeError, "please convert to yale or dense before saving");
|
1419
|
-
}
|
1420
|
-
|
1421
|
-
NMATRIX* nm = nm_create(stype, s);
|
1422
|
-
|
1423
|
-
// Return the appropriate matrix object (Ruby VALUE)
|
1424
|
-
// FIXME: This should probably return CLASS_OF(self) instead of cNMatrix, but I don't know how that works for
|
1425
|
-
// FIXME: class methods.
|
1426
|
-
switch(stype) {
|
1427
|
-
case nm::DENSE_STORE:
|
1428
|
-
return Data_Wrap_Struct(cNMatrix, nm_dense_storage_mark, nm_delete, nm);
|
1429
|
-
case nm::YALE_STORE:
|
1430
|
-
return Data_Wrap_Struct(cNMatrix, nm_yale_storage_mark, nm_delete, nm);
|
1431
|
-
default:
|
1432
|
-
return Qnil;
|
1433
|
-
}
|
1434
|
-
|
1435
|
-
}
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
/*
|
1440
|
-
* Create a new NMatrix helper for handling internal ia, ja, and a arguments.
|
1441
|
-
*
|
1442
|
-
* This constructor is only called by Ruby code, so we can skip most of the
|
1443
|
-
* checks.
|
1444
|
-
*/
|
1445
|
-
static VALUE nm_init_yale_from_old_yale(VALUE shape, VALUE dtype, VALUE ia, VALUE ja, VALUE a, VALUE from_dtype, VALUE nm) {
|
1446
|
-
size_t dim = 2;
|
1447
|
-
size_t* shape_ = interpret_shape(shape, &dim);
|
1448
|
-
nm::dtype_t dtype_ = nm_dtype_from_rbsymbol(dtype);
|
1449
|
-
char *ia_ = RSTRING_PTR(ia),
|
1450
|
-
*ja_ = RSTRING_PTR(ja),
|
1451
|
-
*a_ = RSTRING_PTR(a);
|
1452
|
-
nm::dtype_t from_dtype_ = nm_dtype_from_rbsymbol(from_dtype);
|
1453
|
-
NMATRIX* nmatrix;
|
1454
|
-
|
1455
|
-
UnwrapNMatrix( nm, nmatrix );
|
1456
|
-
|
1457
|
-
nmatrix->stype = nm::YALE_STORE;
|
1458
|
-
nmatrix->storage = (STORAGE*)nm_yale_storage_create_from_old_yale(dtype_, shape_, ia_, ja_, a_, from_dtype_);
|
1459
|
-
|
1460
|
-
return nm;
|
1461
|
-
}
|
1462
|
-
|
1463
|
-
/*
|
1464
|
-
* Check to determine whether matrix is a reference to another matrix.
|
1465
|
-
*/
|
1466
|
-
static VALUE nm_is_ref(VALUE self) {
|
1467
|
-
if (NM_SRC(self) == NM_STORAGE(self)) return Qfalse;
|
1468
|
-
else return Qtrue;
|
1469
|
-
}
|
1470
|
-
|
1471
|
-
/*
|
1472
|
-
* call-seq:
|
1473
|
-
* slice -> ...
|
1474
|
-
*
|
1475
|
-
* Access the contents of an NMatrix at given coordinates, using copying.
|
1476
|
-
*
|
1477
|
-
* n.slice(3,3) # => 5.0
|
1478
|
-
* n.slice(0..1,0..1) #=> matrix [2,2]
|
1479
|
-
*
|
1480
|
-
*/
|
1481
|
-
static VALUE nm_mget(int argc, VALUE* argv, VALUE self) {
|
1482
|
-
static void* (*ttable[nm::NUM_STYPES])(STORAGE*, SLICE*) = {
|
1483
|
-
nm_dense_storage_get,
|
1484
|
-
nm_list_storage_get,
|
1485
|
-
nm_yale_storage_get
|
1486
|
-
};
|
1487
|
-
return nm_xslice(argc, argv, ttable[NM_STYPE(self)], nm_delete, self);
|
1488
|
-
}
|
1489
|
-
|
1490
|
-
/*
|
1491
|
-
* call-seq:
|
1492
|
-
* matrix[indices] -> ...
|
1493
|
-
*
|
1494
|
-
* Access the contents of an NMatrix at given coordinates by reference.
|
1495
|
-
*
|
1496
|
-
* n[3,3] # => 5.0
|
1497
|
-
* n[0..1,0..1] #=> matrix [2,2]
|
1498
|
-
*
|
1499
|
-
*/
|
1500
|
-
static VALUE nm_mref(int argc, VALUE* argv, VALUE self) {
|
1501
|
-
static void* (*ttable[nm::NUM_STYPES])(STORAGE*, SLICE*) = {
|
1502
|
-
nm_dense_storage_ref,
|
1503
|
-
nm_list_storage_ref,
|
1504
|
-
nm_yale_storage_ref
|
1505
|
-
};
|
1506
|
-
return nm_xslice(argc, argv, ttable[NM_STYPE(self)], nm_delete_ref, self);
|
1507
|
-
}
|
1508
|
-
|
1509
|
-
/*
|
1510
|
-
* Modify the contents of an NMatrix in the given cell
|
1511
|
-
*
|
1512
|
-
* n[3,3] = 5.0
|
1513
|
-
*
|
1514
|
-
* Also returns the new contents, so you can chain:
|
1515
|
-
*
|
1516
|
-
* n[3,3] = n[2,3] = 5.0
|
1517
|
-
*/
|
1518
|
-
static VALUE nm_mset(int argc, VALUE* argv, VALUE self) {
|
1519
|
-
size_t dim = NM_DIM(self); // last arg is the value
|
1520
|
-
|
1521
|
-
if ((size_t)(argc) > NM_DIM(self)+1) {
|
1522
|
-
rb_raise(rb_eArgError, "wrong number of arguments (%d for %u)", argc, effective_dim(NM_STORAGE(self))+1);
|
1523
|
-
} else {
|
1524
|
-
SLICE* slice = get_slice(dim, argc-1, argv, NM_STORAGE(self)->shape);
|
1525
|
-
|
1526
|
-
void* value = rubyobj_to_cval(argv[argc-1], NM_DTYPE(self));
|
1527
|
-
|
1528
|
-
// FIXME: Can't use a function pointer table here currently because these functions have different
|
1529
|
-
// signatures (namely the return type).
|
1530
|
-
switch(NM_STYPE(self)) {
|
1531
|
-
case nm::DENSE_STORE:
|
1532
|
-
nm_dense_storage_set(NM_STORAGE(self), slice, value);
|
1533
|
-
xfree(value);
|
1534
|
-
break;
|
1535
|
-
case nm::LIST_STORE:
|
1536
|
-
// Remove if it's a zero, insert otherwise
|
1537
|
-
if (!std::memcmp(value, NM_STORAGE_LIST(self)->default_val, DTYPE_SIZES[NM_DTYPE(self)])) {
|
1538
|
-
xfree(value);
|
1539
|
-
value = nm_list_storage_remove(NM_STORAGE(self), slice);
|
1540
|
-
xfree(value);
|
1541
|
-
} else {
|
1542
|
-
nm_list_storage_insert(NM_STORAGE(self), slice, value);
|
1543
|
-
// no need to free value here since it was inserted directly into the list.
|
1544
|
-
}
|
1545
|
-
break;
|
1546
|
-
case nm::YALE_STORE:
|
1547
|
-
nm_yale_storage_set(NM_STORAGE(self), slice, value);
|
1548
|
-
xfree(value);
|
1549
|
-
break;
|
1550
|
-
}
|
1551
|
-
free_slice(slice);
|
1552
|
-
|
1553
|
-
return argv[argc-1];
|
1554
|
-
}
|
1555
|
-
return Qnil;
|
1556
|
-
}
|
1557
|
-
|
1558
|
-
/*
|
1559
|
-
* Matrix multiply (dot product): against another matrix or a vector.
|
1560
|
-
*
|
1561
|
-
* For elementwise, use * instead.
|
1562
|
-
*
|
1563
|
-
* The two matrices must be of the same stype (for now). If dtype differs, an upcast will occur.
|
1564
|
-
*/
|
1565
|
-
static VALUE nm_multiply(VALUE left_v, VALUE right_v) {
|
1566
|
-
NMATRIX *left, *right;
|
1567
|
-
|
1568
|
-
UnwrapNMatrix( left_v, left );
|
1569
|
-
|
1570
|
-
if (NM_RUBYVAL_IS_NUMERIC(right_v))
|
1571
|
-
return matrix_multiply_scalar(left, right_v);
|
1572
|
-
|
1573
|
-
else if (TYPE(right_v) == T_ARRAY)
|
1574
|
-
rb_raise(rb_eNotImpError, "please convert array to nx1 or 1xn NMatrix first");
|
1575
|
-
|
1576
|
-
else { // both are matrices (probably)
|
1577
|
-
CheckNMatrixType(right_v);
|
1578
|
-
UnwrapNMatrix( right_v, right );
|
1579
|
-
|
1580
|
-
if (left->storage->shape[1] != right->storage->shape[0])
|
1581
|
-
rb_raise(rb_eArgError, "incompatible dimensions");
|
1582
|
-
|
1583
|
-
if (left->stype != right->stype)
|
1584
|
-
rb_raise(rb_eNotImpError, "matrices must have same stype");
|
1585
|
-
|
1586
|
-
return matrix_multiply(left, right);
|
1587
|
-
|
1588
|
-
}
|
1589
|
-
|
1590
|
-
return Qnil;
|
1591
|
-
}
|
1592
|
-
|
1593
|
-
|
1594
|
-
/*
|
1595
|
-
* call-seq:
|
1596
|
-
* dim -> Integer
|
1597
|
-
*
|
1598
|
-
* Get the number of dimensions of a matrix.
|
1599
|
-
*
|
1600
|
-
* In other words, if you set your matrix to be 3x4, the dim is 2. If the
|
1601
|
-
* matrix was initialized as 3x4x3, the dim is 3.
|
1602
|
-
*
|
1603
|
-
* Use #effective_dim to get the dimension of an NMatrix which acts as a vector (e.g., a column or row).
|
1604
|
-
*/
|
1605
|
-
static VALUE nm_dim(VALUE self) {
|
1606
|
-
return INT2FIX(NM_STORAGE(self)->dim);
|
1607
|
-
}
|
1608
|
-
|
1609
|
-
/*
|
1610
|
-
* call-seq:
|
1611
|
-
* shape -> Array
|
1612
|
-
*
|
1613
|
-
* Get the shape (dimensions) of a matrix.
|
1614
|
-
*/
|
1615
|
-
static VALUE nm_shape(VALUE self) {
|
1616
|
-
STORAGE* s = NM_STORAGE(self);
|
1617
|
-
|
1618
|
-
// Copy elements into a VALUE array and then use those to create a Ruby array with rb_ary_new4.
|
1619
|
-
VALUE* shape = ALLOCA_N(VALUE, s->dim);
|
1620
|
-
for (size_t index = 0; index < s->dim; ++index)
|
1621
|
-
shape[index] = INT2FIX(s->shape[index]);
|
1622
|
-
|
1623
|
-
return rb_ary_new4(s->dim, shape);
|
1624
|
-
}
|
1625
|
-
|
1626
|
-
|
1627
|
-
/*
|
1628
|
-
* call-seq:
|
1629
|
-
* offset -> Array
|
1630
|
-
*
|
1631
|
-
* Get the offset (slice position) of a matrix. Typically all zeros, unless you have a reference slice.
|
1632
|
-
*/
|
1633
|
-
static VALUE nm_offset(VALUE self) {
|
1634
|
-
STORAGE* s = NM_STORAGE(self);
|
1635
|
-
|
1636
|
-
// Copy elements into a VALUE array and then use those to create a Ruby array with rb_ary_new4.
|
1637
|
-
VALUE* offset = ALLOCA_N(VALUE, s->dim);
|
1638
|
-
for (size_t index = 0; index < s->dim; ++index)
|
1639
|
-
offset[index] = INT2FIX(s->offset[index]);
|
1640
|
-
|
1641
|
-
return rb_ary_new4(s->dim, offset);
|
1642
|
-
}
|
1643
|
-
|
1644
|
-
|
1645
|
-
/*
|
1646
|
-
* call-seq:
|
1647
|
-
* supershape(n) -> Array
|
1648
|
-
* supershape -> Array
|
1649
|
-
*
|
1650
|
-
* Get the shape of a slice's nth-order parent. If the slice doesn't have n orders, returns the shape
|
1651
|
-
* of the original ancestor.
|
1652
|
-
*/
|
1653
|
-
static VALUE nm_supershape(int argc, VALUE* argv, VALUE self) {
|
1654
|
-
VALUE n; rb_scan_args(argc, argv, "01", &n);
|
1655
|
-
|
1656
|
-
STORAGE* s = NM_STORAGE(self);
|
1657
|
-
if (s->src == s) return nm_shape(self); // easy case (not a slice)
|
1658
|
-
int order = n == Qnil ? 1 : FIX2INT(n);
|
1659
|
-
|
1660
|
-
if (order <= 0) rb_raise(rb_eRangeError, "expected argument to be positive");
|
1661
|
-
|
1662
|
-
for (; order > 0; --order) {
|
1663
|
-
s = s->src; // proceed to next parent
|
1664
|
-
}
|
1665
|
-
|
1666
|
-
VALUE* shape = ALLOCA_N(VALUE, s->dim);
|
1667
|
-
for (size_t index = 0; index < s->dim; ++index)
|
1668
|
-
shape[index] = INT2FIX(s->shape[index]);
|
1669
|
-
|
1670
|
-
return rb_ary_new4(s->dim, shape);
|
1671
|
-
}
|
1672
|
-
|
1673
|
-
/*
|
1674
|
-
* call-seq:
|
1675
|
-
* stype -> Symbol
|
1676
|
-
*
|
1677
|
-
* Get the storage type (stype) of a matrix, e.g., :yale, :dense, or :list.
|
1678
|
-
*/
|
1679
|
-
static VALUE nm_stype(VALUE self) {
|
1680
|
-
ID stype = rb_intern(STYPE_NAMES[NM_STYPE(self)]);
|
1681
|
-
return ID2SYM(stype);
|
1682
|
-
}
|
1683
|
-
|
1684
|
-
/*
|
1685
|
-
* call-seq:
|
1686
|
-
* symmetric? -> Boolean
|
1687
|
-
*
|
1688
|
-
* Is this matrix symmetric?
|
1689
|
-
*/
|
1690
|
-
static VALUE nm_symmetric(VALUE self) {
|
1691
|
-
return is_symmetric(self, false);
|
1692
|
-
}
|
1693
|
-
|
1694
|
-
|
1695
|
-
/*
|
1696
|
-
* Gets the dimension of a matrix which might be a vector (have one or more shape components of size 1).
|
1697
|
-
*/
|
1698
|
-
static size_t effective_dim(STORAGE* s) {
|
1699
|
-
size_t d = 0;
|
1700
|
-
for (size_t i = 0; i < s->dim; ++i) {
|
1701
|
-
if (s->shape[i] != 1) d++;
|
1702
|
-
}
|
1703
|
-
return d;
|
1704
|
-
}
|
1705
|
-
|
1706
|
-
|
1707
|
-
/*
|
1708
|
-
* call-seq:
|
1709
|
-
* effective_dim -> Fixnum
|
1710
|
-
*
|
1711
|
-
* Returns the number of dimensions that don't have length 1. Guaranteed to be less than or equal to #dim.
|
1712
|
-
*/
|
1713
|
-
static VALUE nm_effective_dim(VALUE self) {
|
1714
|
-
return INT2FIX(effective_dim(NM_STORAGE(self)));
|
1715
|
-
}
|
1716
|
-
|
1717
|
-
|
1718
|
-
/*
|
1719
|
-
* Get a slice of an NMatrix.
|
1720
|
-
*/
|
1721
|
-
static VALUE nm_xslice(int argc, VALUE* argv, void* (*slice_func)(STORAGE*, SLICE*), void (*delete_func)(NMATRIX*), VALUE self) {
|
1722
|
-
VALUE result = Qnil;
|
1723
|
-
STORAGE* s = NM_STORAGE(self);
|
1724
|
-
|
1725
|
-
if (NM_DIM(self) < (size_t)(argc)) {
|
1726
|
-
rb_raise(rb_eArgError, "wrong number of arguments (%d for %u)", argc, effective_dim(s));
|
1727
|
-
} else {
|
1728
|
-
SLICE* slice = get_slice(NM_DIM(self), argc, argv, s->shape);
|
1729
|
-
|
1730
|
-
if (slice->single) {
|
1731
|
-
static void* (*ttable[nm::NUM_STYPES])(STORAGE*, SLICE*) = {
|
1732
|
-
nm_dense_storage_ref,
|
1733
|
-
nm_list_storage_ref,
|
1734
|
-
nm_yale_storage_ref
|
1735
|
-
};
|
1736
|
-
|
1737
|
-
if (NM_DTYPE(self) == nm::RUBYOBJ) result = *reinterpret_cast<VALUE*>( ttable[NM_STYPE(self)](s, slice) );
|
1738
|
-
else result = rubyobj_from_cval( ttable[NM_STYPE(self)](s, slice), NM_DTYPE(self) ).rval;
|
1739
|
-
|
1740
|
-
} else {
|
1741
|
-
STYPE_MARK_TABLE(mark_table);
|
1742
|
-
|
1743
|
-
NMATRIX* mat = ALLOC(NMATRIX);
|
1744
|
-
mat->stype = NM_STYPE(self);
|
1745
|
-
mat->storage = (STORAGE*)((*slice_func)( s, slice ));
|
1746
|
-
|
1747
|
-
result = Data_Wrap_Struct(CLASS_OF(self), mark_table[mat->stype], delete_func, mat);
|
1748
|
-
}
|
1749
|
-
|
1750
|
-
free_slice(slice);
|
1751
|
-
}
|
1752
|
-
|
1753
|
-
return result;
|
1754
|
-
}
|
1755
|
-
|
1756
|
-
//////////////////////
|
1757
|
-
// Helper Functions //
|
1758
|
-
//////////////////////
|
1759
|
-
|
1760
|
-
static VALUE elementwise_op(nm::ewop_t op, VALUE left_val, VALUE right_val) {
|
1761
|
-
STYPE_MARK_TABLE(mark);
|
1762
|
-
|
1763
|
-
NMATRIX* left;
|
1764
|
-
NMATRIX* result;
|
1765
|
-
|
1766
|
-
CheckNMatrixType(left_val);
|
1767
|
-
UnwrapNMatrix(left_val, left);
|
1768
|
-
|
1769
|
-
if (TYPE(right_val) != T_DATA || (RDATA(right_val)->dfree != (RUBY_DATA_FUNC)nm_delete && RDATA(right_val)->dfree != (RUBY_DATA_FUNC)nm_delete_ref)) {
|
1770
|
-
// This is a matrix-scalar element-wise operation.
|
1771
|
-
std::string sym;
|
1772
|
-
switch(left->stype) {
|
1773
|
-
case nm::DENSE_STORE:
|
1774
|
-
sym = "__dense_scalar_" + nm::EWOP_NAMES[op] + "__";
|
1775
|
-
break;
|
1776
|
-
case nm::YALE_STORE:
|
1777
|
-
sym = "__yale_scalar_" + nm::EWOP_NAMES[op] + "__";
|
1778
|
-
break;
|
1779
|
-
case nm::LIST_STORE:
|
1780
|
-
sym = "__list_scalar_" + nm::EWOP_NAMES[op] + "__";
|
1781
|
-
break;
|
1782
|
-
default:
|
1783
|
-
rb_raise(rb_eNotImpError, "unknown storage type requested scalar element-wise operation");
|
1784
|
-
}
|
1785
|
-
return rb_funcall(left_val, rb_intern(sym.c_str()), 1, right_val);
|
1786
|
-
|
1787
|
-
} else {
|
1788
|
-
|
1789
|
-
// Check that the left- and right-hand sides have the same dimensionality.
|
1790
|
-
if (NM_DIM(left_val) != NM_DIM(right_val)) {
|
1791
|
-
rb_raise(rb_eArgError, "The left- and right-hand sides of the operation must have the same dimensionality.");
|
1792
|
-
}
|
1793
|
-
|
1794
|
-
// Check that the left- and right-hand sides have the same shape.
|
1795
|
-
if (memcmp(&NM_SHAPE(left_val, 0), &NM_SHAPE(right_val, 0), sizeof(size_t) * NM_DIM(left_val)) != 0) {
|
1796
|
-
rb_raise(rb_eArgError, "The left- and right-hand sides of the operation must have the same shape.");
|
1797
|
-
}
|
1798
|
-
|
1799
|
-
NMATRIX* right;
|
1800
|
-
UnwrapNMatrix(right_val, right);
|
1801
|
-
|
1802
|
-
if (left->stype == right->stype) {
|
1803
|
-
std::string sym;
|
1804
|
-
|
1805
|
-
switch(left->stype) {
|
1806
|
-
case nm::DENSE_STORE:
|
1807
|
-
sym = "__dense_elementwise_" + nm::EWOP_NAMES[op] + "__";
|
1808
|
-
break;
|
1809
|
-
case nm::YALE_STORE:
|
1810
|
-
sym = "__yale_elementwise_" + nm::EWOP_NAMES[op] + "__";
|
1811
|
-
break;
|
1812
|
-
case nm::LIST_STORE:
|
1813
|
-
sym = "__list_elementwise_" + nm::EWOP_NAMES[op] + "__";
|
1814
|
-
break;
|
1815
|
-
default:
|
1816
|
-
rb_raise(rb_eNotImpError, "unknown storage type requested element-wise operation");
|
1817
|
-
}
|
1818
|
-
return rb_funcall(left_val, rb_intern(sym.c_str()), 1, right_val);
|
1819
|
-
|
1820
|
-
} else {
|
1821
|
-
rb_raise(rb_eArgError, "Element-wise operations are not currently supported between matrices with differing stypes.");
|
1822
|
-
}
|
1823
|
-
}
|
1824
|
-
|
1825
|
-
return Data_Wrap_Struct(CLASS_OF(left_val), mark[result->stype], nm_delete, result);
|
1826
|
-
}
|
1827
|
-
|
1828
|
-
/*
|
1829
|
-
* Check to determine whether matrix is a reference to another matrix.
|
1830
|
-
*/
|
1831
|
-
bool is_ref(const NMATRIX* matrix) {
|
1832
|
-
return matrix->storage->src != matrix->storage;
|
1833
|
-
}
|
1834
|
-
|
1835
|
-
/*
|
1836
|
-
* Helper function for nm_symmetric and nm_hermitian.
|
1837
|
-
*/
|
1838
|
-
static VALUE is_symmetric(VALUE self, bool hermitian) {
|
1839
|
-
NMATRIX* m;
|
1840
|
-
UnwrapNMatrix(self, m);
|
1841
|
-
|
1842
|
-
if (m->storage->shape[0] == m->storage->shape[1] and m->storage->dim == 2) {
|
1843
|
-
if (NM_STYPE(self) == nm::DENSE_STORE) {
|
1844
|
-
if (hermitian) {
|
1845
|
-
nm_dense_storage_is_hermitian((DENSE_STORAGE*)(m->storage), m->storage->shape[0]);
|
1846
|
-
|
1847
|
-
} else {
|
1848
|
-
nm_dense_storage_is_symmetric((DENSE_STORAGE*)(m->storage), m->storage->shape[0]);
|
1849
|
-
}
|
1850
|
-
|
1851
|
-
} else {
|
1852
|
-
// TODO: Implement, at the very least, yale_is_symmetric. Model it after yale/transp.template.c.
|
1853
|
-
rb_raise(rb_eNotImpError, "symmetric? and hermitian? only implemented for dense currently");
|
1854
|
-
}
|
1855
|
-
|
1856
|
-
}
|
1857
|
-
|
1858
|
-
return Qfalse;
|
1859
|
-
}
|
1860
|
-
|
1861
|
-
///////////////////////
|
1862
|
-
// Utility Functions //
|
1863
|
-
///////////////////////
|
1864
|
-
|
1865
|
-
/*
|
1866
|
-
* Guess the dtype given a Ruby VALUE and return it as a symbol.
|
1867
|
-
*
|
1868
|
-
* Not to be confused with nm_dtype_guess, which returns an nm::dtype_t. (This calls that.)
|
1869
|
-
*/
|
1870
|
-
static VALUE nm_guess_dtype(VALUE self, VALUE v) {
|
1871
|
-
return ID2SYM(rb_intern(DTYPE_NAMES[nm_dtype_guess(v)]));
|
1872
|
-
}
|
1873
|
-
|
1874
|
-
/*
|
1875
|
-
* Get the minimum allowable dtype for a Ruby VALUE and return it as a symbol.
|
1876
|
-
*/
|
1877
|
-
static VALUE nm_min_dtype(VALUE self, VALUE v) {
|
1878
|
-
return ID2SYM(rb_intern(DTYPE_NAMES[nm_dtype_min(v)]));
|
1879
|
-
}
|
1880
|
-
|
1881
|
-
/*
|
1882
|
-
* Helper for nm_dtype_min(), handling integers.
|
1883
|
-
*/
|
1884
|
-
nm::dtype_t nm_dtype_min_fixnum(int64_t v) {
|
1885
|
-
if (v >= 0 && v <= UCHAR_MAX) return nm::BYTE;
|
1886
|
-
else {
|
1887
|
-
v = std::abs(v);
|
1888
|
-
if (v <= CHAR_MAX) return nm::INT8;
|
1889
|
-
else if (v <= SHRT_MAX) return nm::INT16;
|
1890
|
-
else if (v <= INT_MAX) return nm::INT32;
|
1891
|
-
else return nm::INT64;
|
1892
|
-
}
|
1893
|
-
}
|
1894
|
-
|
1895
|
-
/*
|
1896
|
-
* Helper for nm_dtype_min(), handling rationals.
|
1897
|
-
*/
|
1898
|
-
nm::dtype_t nm_dtype_min_rational(VALUE vv) {
|
1899
|
-
nm::Rational128* v = ALLOCA_N(nm::Rational128, 1);
|
1900
|
-
rubyval_to_cval(vv, nm::RATIONAL128, v);
|
1901
|
-
|
1902
|
-
int64_t i = std::max(std::abs(v->n), v->d);
|
1903
|
-
if (i <= SHRT_MAX) return nm::INT16;
|
1904
|
-
else if (i <= INT_MAX) return nm::INT32;
|
1905
|
-
else return nm::INT64;
|
1906
|
-
}
|
1907
|
-
|
1908
|
-
/*
|
1909
|
-
* Return the minimum dtype required to store a given value.
|
1910
|
-
*
|
1911
|
-
* This is kind of arbitrary. For Float, it always returns :float32 for example, since in some cases neither :float64
|
1912
|
-
* not :float32 are sufficient.
|
1913
|
-
*
|
1914
|
-
* This function is used in upcasting for scalar math. We want to ensure that :int8 + 1 does not return an :int64, basically.
|
1915
|
-
*
|
1916
|
-
* FIXME: Eventually, this function should actually look at the value stored in Fixnums (for example), so that it knows
|
1917
|
-
* whether to return :int64 or :int32.
|
1918
|
-
*/
|
1919
|
-
nm::dtype_t nm_dtype_min(VALUE v) {
|
1920
|
-
|
1921
|
-
switch(TYPE(v)) {
|
1922
|
-
case T_FIXNUM:
|
1923
|
-
return nm_dtype_min_fixnum(FIX2LONG(v));
|
1924
|
-
case T_BIGNUM:
|
1925
|
-
return nm::INT64;
|
1926
|
-
case T_FLOAT:
|
1927
|
-
return nm::FLOAT32;
|
1928
|
-
case T_COMPLEX:
|
1929
|
-
return nm::COMPLEX64;
|
1930
|
-
case T_RATIONAL:
|
1931
|
-
return nm_dtype_min_rational(v);
|
1932
|
-
case T_STRING:
|
1933
|
-
return RSTRING_LEN(v) == 1 ? nm::BYTE : nm::RUBYOBJ;
|
1934
|
-
case T_TRUE:
|
1935
|
-
case T_FALSE:
|
1936
|
-
case T_NIL:
|
1937
|
-
default:
|
1938
|
-
return nm::RUBYOBJ;
|
1939
|
-
}
|
1940
|
-
}
|
1941
|
-
|
1942
|
-
|
1943
|
-
/*
|
1944
|
-
* Guess the data type given a value.
|
1945
|
-
*
|
1946
|
-
* TODO: Probably needs some work for Bignum.
|
1947
|
-
*/
|
1948
|
-
nm::dtype_t nm_dtype_guess(VALUE v) {
|
1949
|
-
switch(TYPE(v)) {
|
1950
|
-
case T_TRUE:
|
1951
|
-
case T_FALSE:
|
1952
|
-
case T_NIL:
|
1953
|
-
return nm::RUBYOBJ;
|
1954
|
-
case T_STRING:
|
1955
|
-
return RSTRING_LEN(v) == 1 ? nm::BYTE : nm::RUBYOBJ;
|
1956
|
-
|
1957
|
-
#if SIZEOF_INT == 8
|
1958
|
-
case T_FIXNUM:
|
1959
|
-
return nm::INT64;
|
1960
|
-
|
1961
|
-
case T_RATIONAL:
|
1962
|
-
return nm::RATIONAL128;
|
1963
|
-
|
1964
|
-
#else
|
1965
|
-
# if SIZEOF_INT == 4
|
1966
|
-
case T_FIXNUM:
|
1967
|
-
return nm::INT32;
|
1968
|
-
|
1969
|
-
case T_RATIONAL:
|
1970
|
-
return nm::RATIONAL64;
|
1971
|
-
|
1972
|
-
#else
|
1973
|
-
case T_FIXNUM:
|
1974
|
-
return nm::INT16;
|
1975
|
-
|
1976
|
-
case T_RATIONAL:
|
1977
|
-
return nm::RATIONAL32;
|
1978
|
-
# endif
|
1979
|
-
#endif
|
1980
|
-
|
1981
|
-
case T_BIGNUM:
|
1982
|
-
return nm::INT64;
|
1983
|
-
|
1984
|
-
#if SIZEOF_FLOAT == 4
|
1985
|
-
case T_COMPLEX:
|
1986
|
-
return nm::COMPLEX128;
|
1987
|
-
|
1988
|
-
case T_FLOAT:
|
1989
|
-
return nm::FLOAT64;
|
1990
|
-
|
1991
|
-
#else
|
1992
|
-
# if SIZEOF_FLOAT == 2
|
1993
|
-
case T_COMPLEX:
|
1994
|
-
return nm::COMPLEX64;
|
1995
|
-
|
1996
|
-
case T_FLOAT:
|
1997
|
-
return nm::FLOAT32;
|
1998
|
-
# endif
|
1999
|
-
#endif
|
2000
|
-
|
2001
|
-
case T_ARRAY:
|
2002
|
-
/*
|
2003
|
-
* May be passed for dense -- for now, just look at the first element.
|
2004
|
-
*
|
2005
|
-
* TODO: Look at entire array for most specific type.
|
2006
|
-
*/
|
2007
|
-
|
2008
|
-
return nm_dtype_guess(RARRAY_PTR(v)[0]);
|
2009
|
-
|
2010
|
-
default:
|
2011
|
-
rb_raise(rb_eArgError, "Unable to guess a data type from provided parameters; data type must be specified manually.");
|
2012
|
-
}
|
2013
|
-
}
|
2014
|
-
|
2015
|
-
|
2016
|
-
|
2017
|
-
/*
|
2018
|
-
* Allocate and return a SLICE object, which will contain the appropriate coordinate and length information for
|
2019
|
-
* accessing some part of a matrix.
|
2020
|
-
*/
|
2021
|
-
static SLICE* get_slice(size_t dim, int argc, VALUE* arg, size_t* shape) {
|
2022
|
-
VALUE beg, end;
|
2023
|
-
int excl;
|
2024
|
-
|
2025
|
-
SLICE* slice = alloc_slice(dim);
|
2026
|
-
slice->single = true;
|
2027
|
-
|
2028
|
-
// r is the shape position; t is the slice position. They may differ when we're dealing with a
|
2029
|
-
// matrix where the effective dimension is less than the dimension (e.g., a vector).
|
2030
|
-
for (size_t r = 0, t = 0; r < dim; ++r) {
|
2031
|
-
VALUE v = t == argc ? Qnil : arg[t];
|
2032
|
-
|
2033
|
-
// if the current shape indicates a vector and fewer args were supplied than necessary, just use 0
|
2034
|
-
if (argc - t + r < dim && shape[r] == 1) {
|
2035
|
-
slice->coords[r] = 0;
|
2036
|
-
slice->lengths[r] = 1;
|
2037
|
-
|
2038
|
-
} else if (FIXNUM_P(v)) { // this used CLASS_OF before, which is inefficient for fixnum
|
2039
|
-
|
2040
|
-
slice->coords[r] = FIX2UINT(v);
|
2041
|
-
slice->lengths[r] = 1;
|
2042
|
-
t++;
|
2043
|
-
|
2044
|
-
} else if (TYPE(arg[t]) == T_HASH) { // 3:5 notation (inclusive)
|
2045
|
-
VALUE begin_end = rb_funcall(v, rb_intern("shift"), 0); // rb_hash_shift
|
2046
|
-
slice->coords[r] = FIX2UINT(rb_ary_entry(begin_end, 0));
|
2047
|
-
slice->lengths[r] = FIX2UINT(rb_ary_entry(begin_end, 1)) - slice->coords[r];
|
2048
|
-
|
2049
|
-
if (RHASH_EMPTY_P(v)) t++; // go on to the next
|
2050
|
-
|
2051
|
-
slice->single = false;
|
2052
|
-
|
2053
|
-
} else if (CLASS_OF(v) == rb_cRange) {
|
2054
|
-
rb_range_values(arg[t], &beg, &end, &excl);
|
2055
|
-
slice->coords[r] = FIX2UINT(beg);
|
2056
|
-
// Exclude last element for a...b range
|
2057
|
-
slice->lengths[r] = FIX2UINT(end) - slice->coords[r] + (excl ? 0 : 1);
|
2058
|
-
|
2059
|
-
slice->single = false;
|
2060
|
-
|
2061
|
-
t++;
|
2062
|
-
|
2063
|
-
} else {
|
2064
|
-
rb_raise(rb_eArgError, "expected Fixnum, Range, or Hash for slice component instead of %s", rb_obj_classname(v));
|
2065
|
-
}
|
2066
|
-
|
2067
|
-
if (slice->coords[r] > shape[r] || slice->coords[r] + slice->lengths[r] > shape[r])
|
2068
|
-
rb_raise(rb_eRangeError, "slice is larger than matrix in dimension %u (slice component %u)", r, t);
|
2069
|
-
}
|
2070
|
-
|
2071
|
-
return slice;
|
2072
|
-
}
|
2073
|
-
|
2074
|
-
#ifdef BENCHMARK
|
2075
|
-
/*
|
2076
|
-
* A simple function used when benchmarking NMatrix.
|
2077
|
-
*/
|
2078
|
-
static double get_time(void) {
|
2079
|
-
struct timeval t;
|
2080
|
-
struct timezone tzp;
|
2081
|
-
|
2082
|
-
gettimeofday(&t, &tzp);
|
2083
|
-
|
2084
|
-
return t.tv_sec + t.tv_usec*1e-6;
|
2085
|
-
}
|
2086
|
-
#endif
|
2087
|
-
|
2088
|
-
/*
|
2089
|
-
* The argv parameter will be either 1 or 2 elements. If 1, could be either
|
2090
|
-
* initial or dtype. If 2, is initial and dtype. This function returns the
|
2091
|
-
* dtype.
|
2092
|
-
*/
|
2093
|
-
static nm::dtype_t interpret_dtype(int argc, VALUE* argv, nm::stype_t stype) {
|
2094
|
-
int offset;
|
2095
|
-
|
2096
|
-
switch (argc) {
|
2097
|
-
case 1:
|
2098
|
-
offset = 0;
|
2099
|
-
break;
|
2100
|
-
|
2101
|
-
case 2:
|
2102
|
-
offset = 1;
|
2103
|
-
break;
|
2104
|
-
|
2105
|
-
default:
|
2106
|
-
rb_raise(rb_eArgError, "Need an initial value or a dtype.");
|
2107
|
-
break;
|
2108
|
-
}
|
2109
|
-
|
2110
|
-
if (SYMBOL_P(argv[offset])) {
|
2111
|
-
return nm_dtype_from_rbsymbol(argv[offset]);
|
2112
|
-
|
2113
|
-
} else if (TYPE(argv[offset]) == T_STRING) {
|
2114
|
-
return nm_dtype_from_rbstring(StringValue(argv[offset]));
|
2115
|
-
|
2116
|
-
} else if (stype == nm::YALE_STORE) {
|
2117
|
-
rb_raise(rb_eArgError, "Yale storage class requires a dtype.");
|
2118
|
-
|
2119
|
-
} else {
|
2120
|
-
return nm_dtype_guess(argv[0]);
|
2121
|
-
}
|
2122
|
-
}
|
2123
|
-
|
2124
|
-
/*
|
2125
|
-
* Convert an Ruby value or an array of Ruby values into initial C values.
|
2126
|
-
*/
|
2127
|
-
static void* interpret_initial_value(VALUE arg, nm::dtype_t dtype) {
|
2128
|
-
unsigned int index;
|
2129
|
-
void* init_val;
|
2130
|
-
|
2131
|
-
if (TYPE(arg) == T_ARRAY) {
|
2132
|
-
// Array
|
2133
|
-
|
2134
|
-
init_val = ALLOC_N(int8_t, DTYPE_SIZES[dtype] * RARRAY_LEN(arg));
|
2135
|
-
for (index = 0; index < RARRAY_LEN(arg); ++index) {
|
2136
|
-
rubyval_to_cval(RARRAY_PTR(arg)[index], dtype, (char*)init_val + (index * DTYPE_SIZES[dtype]));
|
2137
|
-
}
|
2138
|
-
|
2139
|
-
} else {
|
2140
|
-
// Single value
|
2141
|
-
|
2142
|
-
init_val = rubyobj_to_cval(arg, dtype);
|
2143
|
-
}
|
2144
|
-
|
2145
|
-
return init_val;
|
2146
|
-
}
|
2147
|
-
|
2148
|
-
/*
|
2149
|
-
* Convert the shape argument, which may be either a Ruby value or an array of
|
2150
|
-
* Ruby values, into C values. The second argument is where the dimensionality
|
2151
|
-
* of the matrix will be stored. The function itself returns a pointer to the
|
2152
|
-
* array describing the shape, which must be freed manually.
|
2153
|
-
*/
|
2154
|
-
static size_t* interpret_shape(VALUE arg, size_t* dim) {
|
2155
|
-
size_t* shape;
|
2156
|
-
|
2157
|
-
if (TYPE(arg) == T_ARRAY) {
|
2158
|
-
*dim = RARRAY_LEN(arg);
|
2159
|
-
shape = ALLOC_N(size_t, *dim);
|
2160
|
-
|
2161
|
-
for (size_t index = 0; index < *dim; ++index) {
|
2162
|
-
shape[index] = FIX2UINT( RARRAY_PTR(arg)[index] );
|
2163
|
-
}
|
2164
|
-
|
2165
|
-
} else if (FIXNUM_P(arg)) {
|
2166
|
-
*dim = 2;
|
2167
|
-
shape = ALLOC_N(size_t, *dim);
|
2168
|
-
|
2169
|
-
shape[0] = FIX2UINT(arg);
|
2170
|
-
shape[1] = FIX2UINT(arg);
|
2171
|
-
|
2172
|
-
} else {
|
2173
|
-
rb_raise(rb_eArgError, "Expected an array of numbers or a single Fixnum for matrix shape");
|
2174
|
-
}
|
2175
|
-
|
2176
|
-
return shape;
|
2177
|
-
}
|
2178
|
-
|
2179
|
-
/*
|
2180
|
-
* Convert a Ruby symbol or string into an storage type.
|
2181
|
-
*/
|
2182
|
-
static nm::stype_t interpret_stype(VALUE arg) {
|
2183
|
-
if (SYMBOL_P(arg)) {
|
2184
|
-
return nm_stype_from_rbsymbol(arg);
|
2185
|
-
|
2186
|
-
} else if (TYPE(arg) == T_STRING) {
|
2187
|
-
return nm_stype_from_rbstring(StringValue(arg));
|
2188
|
-
|
2189
|
-
} else {
|
2190
|
-
rb_raise(rb_eArgError, "Expected storage type");
|
2191
|
-
}
|
2192
|
-
}
|
2193
|
-
|
2194
|
-
//////////////////
|
2195
|
-
// Math Helpers //
|
2196
|
-
//////////////////
|
2197
|
-
|
2198
|
-
STORAGE* matrix_storage_cast_alloc(NMATRIX* matrix, nm::dtype_t new_dtype) {
|
2199
|
-
if (matrix->storage->dtype == new_dtype && !is_ref(matrix))
|
2200
|
-
return matrix->storage;
|
2201
|
-
|
2202
|
-
CAST_TABLE(cast_copy_storage);
|
2203
|
-
return cast_copy_storage[matrix->stype][matrix->stype](matrix->storage, new_dtype, NULL);
|
2204
|
-
}
|
2205
|
-
|
2206
|
-
STORAGE_PAIR binary_storage_cast_alloc(NMATRIX* left_matrix, NMATRIX* right_matrix) {
|
2207
|
-
STORAGE_PAIR casted;
|
2208
|
-
nm::dtype_t new_dtype = Upcast[left_matrix->storage->dtype][right_matrix->storage->dtype];
|
2209
|
-
|
2210
|
-
casted.left = matrix_storage_cast_alloc(left_matrix, new_dtype);
|
2211
|
-
casted.right = matrix_storage_cast_alloc(right_matrix, new_dtype);
|
2212
|
-
|
2213
|
-
return casted;
|
2214
|
-
}
|
2215
|
-
|
2216
|
-
static VALUE matrix_multiply_scalar(NMATRIX* left, VALUE scalar) {
|
2217
|
-
rb_raise(rb_eNotImpError, "matrix-scalar multiplication not implemented yet");
|
2218
|
-
return Qnil;
|
2219
|
-
}
|
2220
|
-
|
2221
|
-
static VALUE matrix_multiply(NMATRIX* left, NMATRIX* right) {
|
2222
|
-
///TODO: multiplication for non-dense and/or non-decimal matrices
|
2223
|
-
|
2224
|
-
// Make sure both of our matrices are of the correct type.
|
2225
|
-
STORAGE_PAIR casted = binary_storage_cast_alloc(left, right);
|
2226
|
-
|
2227
|
-
size_t* resulting_shape = ALLOC_N(size_t, 2);
|
2228
|
-
resulting_shape[0] = left->storage->shape[0];
|
2229
|
-
resulting_shape[1] = right->storage->shape[1];
|
2230
|
-
|
2231
|
-
// Sometimes we only need to use matrix-vector multiplication (e.g., GEMM versus GEMV). Find out.
|
2232
|
-
bool vector = false;
|
2233
|
-
if (resulting_shape[1] == 1) vector = true;
|
2234
|
-
|
2235
|
-
static STORAGE* (*storage_matrix_multiply[nm::NUM_STYPES])(const STORAGE_PAIR&, size_t*, bool) = {
|
2236
|
-
nm_dense_storage_matrix_multiply,
|
2237
|
-
nm_list_storage_matrix_multiply,
|
2238
|
-
nm_yale_storage_matrix_multiply
|
2239
|
-
};
|
2240
|
-
|
2241
|
-
STORAGE* resulting_storage = storage_matrix_multiply[left->stype](casted, resulting_shape, vector);
|
2242
|
-
NMATRIX* result = nm_create(left->stype, resulting_storage);
|
2243
|
-
|
2244
|
-
// Free any casted-storage we created for the multiplication.
|
2245
|
-
// TODO: Can we make the Ruby GC take care of this stuff now that we're using it?
|
2246
|
-
// If we did that, we night not have to re-create these every time, right? Or wrong? Need to do
|
2247
|
-
// more research.
|
2248
|
-
static void (*free_storage[nm::NUM_STYPES])(STORAGE*) = {
|
2249
|
-
nm_dense_storage_delete,
|
2250
|
-
nm_list_storage_delete,
|
2251
|
-
nm_yale_storage_delete
|
2252
|
-
};
|
2253
|
-
|
2254
|
-
if (left->storage != casted.left) free_storage[result->stype](casted.left);
|
2255
|
-
if (right->storage != casted.right) free_storage[result->stype](casted.right);
|
2256
|
-
|
2257
|
-
|
2258
|
-
STYPE_MARK_TABLE(mark_table);
|
2259
|
-
|
2260
|
-
if (result) return Data_Wrap_Struct(cNMatrix, mark_table[result->stype], nm_delete, result);
|
2261
|
-
return Qnil; // Only if we try to multiply list matrices should we return Qnil.
|
2262
|
-
}
|
2263
|
-
|
2264
|
-
/*
|
2265
|
-
* Calculate the exact determinant of a dense matrix.
|
2266
|
-
*
|
2267
|
-
* Returns nil for dense matrices which are not square or number of dimensions other than 2.
|
2268
|
-
*
|
2269
|
-
* Note: Currently only implemented for 2x2 and 3x3 matrices.
|
2270
|
-
*/
|
2271
|
-
static VALUE nm_det_exact(VALUE self) {
|
2272
|
-
if (NM_STYPE(self) != nm::DENSE_STORE) rb_raise(nm_eStorageTypeError, "can only calculate exact determinant for dense matrices");
|
2273
|
-
|
2274
|
-
if (NM_DIM(self) != 2 || NM_SHAPE0(self) != NM_SHAPE1(self)) return Qnil;
|
2275
|
-
|
2276
|
-
// Calculate the determinant and then assign it to the return value
|
2277
|
-
void* result = ALLOCA_N(char, DTYPE_SIZES[NM_DTYPE(self)]);
|
2278
|
-
nm_math_det_exact(NM_SHAPE0(self), NM_STORAGE_DENSE(self)->elements, NM_SHAPE0(self), NM_DTYPE(self), result);
|
2279
|
-
|
2280
|
-
return rubyobj_from_cval(result, NM_DTYPE(self)).rval;
|
2281
|
-
}
|
2282
|
-
|
2283
|
-
/////////////////
|
2284
|
-
// Exposed API //
|
2285
|
-
/////////////////
|
2286
|
-
|
2287
|
-
/*
|
2288
|
-
* Create a dense matrix. Used by the NMatrix GSL fork. Unlike nm_create, this one copies all of the
|
2289
|
-
* arrays and such passed in -- so you don't have to allocate and pass a new shape object for every
|
2290
|
-
* matrix you want to create, for example. Same goes for elements.
|
2291
|
-
*
|
2292
|
-
* Returns a properly-wrapped Ruby object as a VALUE.
|
2293
|
-
*
|
2294
|
-
* *** Note that this function is for API only. Please do not use it internally.
|
2295
|
-
*
|
2296
|
-
* TODO: Add a column-major option for libraries that use column-major matrices.
|
2297
|
-
*/
|
2298
|
-
VALUE rb_nmatrix_dense_create(nm::dtype_t dtype, size_t* shape, size_t dim, void* elements, size_t length) {
|
2299
|
-
NMATRIX* nm;
|
2300
|
-
size_t nm_dim;
|
2301
|
-
size_t* shape_copy;
|
2302
|
-
|
2303
|
-
// Do not allow a dim of 1. Treat it as a column or row matrix.
|
2304
|
-
if (dim == 1) {
|
2305
|
-
nm_dim = 2;
|
2306
|
-
shape_copy = ALLOC_N(size_t, nm_dim);
|
2307
|
-
shape_copy[0] = shape[0];
|
2308
|
-
shape_copy[1] = 1;
|
2309
|
-
|
2310
|
-
} else {
|
2311
|
-
nm_dim = dim;
|
2312
|
-
shape_copy = ALLOC_N(size_t, nm_dim);
|
2313
|
-
memcpy(shape_copy, shape, sizeof(size_t)*nm_dim);
|
2314
|
-
}
|
2315
|
-
|
2316
|
-
// Copy elements
|
2317
|
-
void* elements_copy = ALLOC_N(char, DTYPE_SIZES[dtype]*length);
|
2318
|
-
memcpy(elements_copy, elements, DTYPE_SIZES[dtype]*length);
|
2319
|
-
|
2320
|
-
// allocate and create the matrix and its storage
|
2321
|
-
nm = nm_create(nm::DENSE_STORE, nm_dense_storage_create(dtype, shape_copy, dim, elements_copy, length));
|
2322
|
-
|
2323
|
-
// tell Ruby about the matrix and its storage, particularly how to garbage collect it.
|
2324
|
-
return Data_Wrap_Struct(cNMatrix, nm_dense_storage_mark, nm_dense_storage_delete, nm);
|
2325
|
-
}
|
2326
|
-
|
2327
|
-
/*
|
2328
|
-
* Create a dense vector. Used by the NMatrix GSL fork.
|
2329
|
-
*
|
2330
|
-
* Basically just a convenience wrapper for rb_nmatrix_dense_create().
|
2331
|
-
*
|
2332
|
-
* Returns a properly-wrapped Ruby NMatrix object as a VALUE. Included for backwards compatibility
|
2333
|
-
* for when NMatrix had an NVector class.
|
2334
|
-
*/
|
2335
|
-
VALUE rb_nvector_dense_create(nm::dtype_t dtype, void* elements, size_t length) {
|
2336
|
-
size_t dim = 1, shape = length;
|
2337
|
-
return rb_nmatrix_dense_create(dtype, &shape, dim, elements, length);
|
2338
|
-
}
|
2339
|
-
|
331
|
+
#include "ruby_nmatrix.c"
|
2340
332
|
} // end of extern "C"
|