nmatrix 0.1.0.rc1 → 0.1.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -1
- data/Gemfile +1 -4
- data/History.txt +64 -2
- data/Manifest.txt +6 -4
- data/README.rdoc +8 -5
- data/Rakefile +0 -2
- data/ext/nmatrix/data/data.cpp +2 -1
- data/ext/nmatrix/data/data.h +3 -2
- data/ext/nmatrix/extconf.rb +4 -9
- data/ext/nmatrix/math.cpp +65 -0
- data/ext/nmatrix/math/math.h +1 -0
- data/ext/nmatrix/nmatrix.h +2 -2
- data/ext/nmatrix/ruby_constants.cpp +3 -1
- data/ext/nmatrix/ruby_constants.h +3 -1
- data/ext/nmatrix/ruby_nmatrix.c +153 -8
- data/ext/nmatrix/util/sl_list.cpp +6 -2
- data/lib/nmatrix/io/point_cloud.rb +182 -0
- data/lib/nmatrix/math.rb +35 -5
- data/lib/nmatrix/nmatrix.rb +70 -26
- data/lib/nmatrix/shortcuts.rb +18 -1
- data/lib/nmatrix/version.rb +1 -1
- data/nmatrix.gemspec +2 -2
- data/spec/00_nmatrix_spec.rb +220 -176
- data/spec/01_enum_spec.rb +29 -29
- data/spec/02_slice_spec.rb +85 -85
- data/spec/blas_spec.rb +18 -18
- data/spec/elementwise_spec.rb +44 -44
- data/spec/io_spec.rb +31 -24
- data/spec/lapack_spec.rb +34 -34
- data/spec/math_spec.rb +61 -46
- data/spec/nmatrix_yale_spec.rb +35 -35
- data/spec/rspec_spec.rb +2 -2
- data/spec/shortcuts_spec.rb +66 -48
- data/spec/slice_set_spec.rb +31 -31
- data/spec/stat_spec.rb +40 -40
- data/spec/test.pcd +20 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4052431a5f81ea9304f96fe293f6f22a47dacf3d
|
4
|
+
data.tar.gz: 777b0fd050d7f0726ea97f337d5f4ce3cf850f8b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: daabd9cebbe93674079728f254cf5429bb1d0cadfb2ddbb56061da9181508852bd919844baf7e63615237fe6f2fc864dd27515b97153ea7704185001417a2f27
|
7
|
+
data.tar.gz: b36dcd576afedd2ddc1fa57af569823fb72f98d0e11cd0e1d7ea751553e1b2de39fe32a197d0a731a308ec28a6e96aedcc0687b73853f88ab0002c6145e53e7b
|
data/.travis.yml
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
language: ruby
|
2
|
+
cache: bundler
|
2
3
|
env:
|
3
4
|
- CPLUS_INCLUDE_PATH=/usr/include/atlas C_INCLUDE_PATH=/usr/include/atlas
|
4
5
|
rvm:
|
5
6
|
- "1.9.2"
|
6
7
|
- "1.9.3"
|
7
8
|
- "2.0.0"
|
9
|
+
- "2.1.0"
|
8
10
|
before_install:
|
9
11
|
- sudo apt-get update -qq
|
10
12
|
- sudo apt-get install -qq libatlas-base-dev
|
11
13
|
script: bundle exec rake compile && bundle exec rake spec
|
12
|
-
|
14
|
+
notifications:
|
15
|
+
irc: "chat.freenode.net#sciruby"
|
data/Gemfile
CHANGED
@@ -1,12 +1,9 @@
|
|
1
|
-
|
2
|
-
source 'http://rubygems.org'
|
1
|
+
source 'https://rubygems.org'
|
3
2
|
gemspec
|
4
3
|
|
5
4
|
gem 'packable', ">= 1.3.5" # for Matlab IO
|
6
5
|
|
7
6
|
group :development do
|
8
|
-
gem 'pry'
|
9
|
-
gem 'rspec-longrun'
|
10
7
|
#gem 'narray', :path => "../narray"
|
11
8
|
#gem 'pry-debugger'
|
12
9
|
end
|
data/History.txt
CHANGED
@@ -428,7 +428,7 @@
|
|
428
428
|
* Casting from dense to Yale now properly accepts the default
|
429
429
|
value option
|
430
430
|
|
431
|
-
=== 0.1.0.rc1 /
|
431
|
+
=== 0.1.0.rc1 / 2013-12-28
|
432
432
|
|
433
433
|
* 4 major enhancements:
|
434
434
|
|
@@ -520,4 +520,66 @@
|
|
520
520
|
lazy symbol binding
|
521
521
|
|
522
522
|
* Improved LAPACK and BLAS header selection for Ubuntu/Debian
|
523
|
-
systems with ATLAS (by @mvz)
|
523
|
+
systems with ATLAS (by @mvz)
|
524
|
+
|
525
|
+
=== 0.1.0.rc2 / 2014-03-12
|
526
|
+
|
527
|
+
* No major enhancements.
|
528
|
+
|
529
|
+
* 14 minor enhancements:
|
530
|
+
|
531
|
+
* Implemented negative-index slicing (by @rajatkapoor)
|
532
|
+
|
533
|
+
* Added reader for Point Cloud Library's PCD format
|
534
|
+
|
535
|
+
* Added Ruby 2.1 support (including Travis CI testing)
|
536
|
+
|
537
|
+
* Implemented LAPACK-independent exact inverse calculation for
|
538
|
+
dense matrices of size 2x2 and 3x3, as well as
|
539
|
+
|
540
|
+
* Added NMatrix::has_clapack? method to determine whether CLAPACK
|
541
|
+
support has been compiled in
|
542
|
+
|
543
|
+
* Improved conformance of specs to RSpec best practices (by
|
544
|
+
@duggiefresh)
|
545
|
+
|
546
|
+
* Travis CI now updates the IRC channel when a check passes (by
|
547
|
+
@agarie)
|
548
|
+
|
549
|
+
* Added NMatrix#data_pointer, which returns the memory address of
|
550
|
+
the stored elements in a matrix (generally for use with FFI and
|
551
|
+
other libraries that need pointers)
|
552
|
+
|
553
|
+
* Made NMatrix#clone_structure a public method (was protected)
|
554
|
+
|
555
|
+
* Added :scale option for NMatrix::random to handle non-floating
|
556
|
+
point forms
|
557
|
+
|
558
|
+
* Added complex support to NMatrix::random
|
559
|
+
|
560
|
+
* Aliased NMatrix::random to NMatrix::rand
|
561
|
+
|
562
|
+
* Added NMatrix#reshape! for in-place reshape of dense matrices (by
|
563
|
+
@rajatkapoor)
|
564
|
+
|
565
|
+
* Implemented unary negation of matrices
|
566
|
+
|
567
|
+
* 6 bug fixes:
|
568
|
+
|
569
|
+
* Fixed dot product operation on 1-dimensional matrices (by @rve
|
570
|
+
and @cjfuller)
|
571
|
+
|
572
|
+
* Fixed segfault on 1-dimensional matrix transpose (by @cjfuller)
|
573
|
+
|
574
|
+
* Fixed compile error with Ruby 2.1 (by @diminish7)
|
575
|
+
|
576
|
+
* Fixed regression which wasn't causing any problems but was
|
577
|
+
counter to design: stride was declared prior to data storage for
|
578
|
+
dense matrix storage
|
579
|
+
|
580
|
+
* Fixed Rakefile problem which was causing specs to run twice in a
|
581
|
+
row with each call to rake spec
|
582
|
+
|
583
|
+
* NMatrix::random now raises an exception when rational matrices
|
584
|
+
are requested
|
585
|
+
|
data/Manifest.txt
CHANGED
@@ -13,6 +13,7 @@ lib/nmatrix/rspec.rb
|
|
13
13
|
lib/nmatrix/io/market.rb
|
14
14
|
lib/nmatrix/io/mat5_reader.rb
|
15
15
|
lib/nmatrix/io/mat_reader.rb
|
16
|
+
lib/nmatrix/io/point_cloud.rb
|
16
17
|
lib/nmatrix/blas.rb
|
17
18
|
lib/nmatrix/enumerate.rb
|
18
19
|
lib/nmatrix/lapack.rb
|
@@ -32,12 +33,12 @@ ext/nmatrix/data/rational.h
|
|
32
33
|
ext/nmatrix/data/ruby_object.h
|
33
34
|
ext/nmatrix/storage/common.cpp
|
34
35
|
ext/nmatrix/storage/common.h
|
35
|
-
ext/nmatrix/storage/dense.cpp
|
36
|
-
ext/nmatrix/storage/dense.h
|
37
|
-
ext/nmatrix/storage/list.cpp
|
38
|
-
ext/nmatrix/storage/list.h
|
39
36
|
ext/nmatrix/storage/storage.cpp
|
40
37
|
ext/nmatrix/storage/storage.h
|
38
|
+
ext/nmatrix/storage/dense/dense.cpp
|
39
|
+
ext/nmatrix/storage/dense/dense.h
|
40
|
+
ext/nmatrix/storage/list/list.cpp
|
41
|
+
ext/nmatrix/storage/list/list.h
|
41
42
|
ext/nmatrix/storage/yale/yale.cpp
|
42
43
|
ext/nmatrix/storage/yale/yale.h
|
43
44
|
ext/nmatrix/storage/yale/class.h
|
@@ -83,6 +84,7 @@ ext/nmatrix/ruby_constants.cpp
|
|
83
84
|
ext/nmatrix/ruby_constants.h
|
84
85
|
ext/nmatrix/ruby_nmatrix.c
|
85
86
|
ext/nmatrix/types.h
|
87
|
+
ext/nmatrix/nm_memory.h
|
86
88
|
ext/nmatrix/extconf.rb
|
87
89
|
|
88
90
|
|
data/README.rdoc
CHANGED
@@ -20,7 +20,7 @@ NMatrix was inspired by {NArray}[http://narray.rubyforge.org], by Masahiro Tanak
|
|
20
20
|
|
21
21
|
To install the latest stable version:
|
22
22
|
|
23
|
-
gem install nmatrix
|
23
|
+
gem install nmatrix --pre
|
24
24
|
|
25
25
|
However, you will need to install {ATLAS}[http://math-atlas.sourceforge.net/] with CBLAS (C interface to
|
26
26
|
{BLAS}[http://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms]) first. Detailed directions can be found
|
@@ -28,7 +28,7 @@ However, you will need to install {ATLAS}[http://math-atlas.sourceforge.net/] wi
|
|
28
28
|
|
29
29
|
* ATLAS, preferably with CLAPACK ({see here for details}[https://github.com/SciRuby/nmatrix/wiki/Installation])
|
30
30
|
* a version of GCC or clang which supports C++0x or C++11
|
31
|
-
* Ruby 1.9.
|
31
|
+
* Ruby 1.9.2+
|
32
32
|
* {packable}[http://github.com/marcandre/packable] 1.3.5 (used for I/O)
|
33
33
|
|
34
34
|
If you want to obtain the latest (development) code, you should generally do:
|
@@ -38,7 +38,7 @@ If you want to obtain the latest (development) code, you should generally do:
|
|
38
38
|
bundle install
|
39
39
|
bundle exec rake compile
|
40
40
|
bundle exec rake repackage
|
41
|
-
gem install pkg/nmatrix-0.1.0
|
41
|
+
gem install pkg/nmatrix-0.1.0.rc2.gem
|
42
42
|
|
43
43
|
Detailed instructions are available for {Mac}[https://github.com/SciRuby/nmatrix/wiki/Installation#mac-os-x] and {Linux}[https://github.com/SciRuby/nmatrix/wiki/Installation#linux].
|
44
44
|
We are currently working on Mavericks (Mac OS X) installation instructions, but in general, you'll need Homebrew and should
|
@@ -101,7 +101,10 @@ The following features exist in the current version of NMatrix (0.1.0.rc1):
|
|
101
101
|
* Matrix slicing by copy and reference (for dense, yale, and list)
|
102
102
|
* Native reading and writing of dense and yale matrices
|
103
103
|
* Optional compression for dense matrices with symmetry or triangularity: symmetric, skew, hermitian, upper, lower
|
104
|
-
*
|
104
|
+
* Input/output:
|
105
|
+
* Matlab .MAT v5 file input
|
106
|
+
* MatrixMarket file input/output
|
107
|
+
* Point Cloud Library PCD file input
|
105
108
|
* C and C++ API
|
106
109
|
* BLAS internal implementations (no library) and ATLAS (with library) access:
|
107
110
|
* Level 1: xROT, xROTG (BLAS dtypes only), xASUM, xNRM2
|
@@ -128,7 +131,7 @@ The following features exist in the current version of NMatrix (0.1.0.rc1):
|
|
128
131
|
|
129
132
|
=== Planned Features (Short-to-Medium Term)
|
130
133
|
|
131
|
-
We are
|
134
|
+
We are nearing the release of NMatrix 0.1.0, our first beta.
|
132
135
|
|
133
136
|
These are features planned for NMatrix 0.2.0:
|
134
137
|
|
data/Rakefile
CHANGED
data/ext/nmatrix/data/data.cpp
CHANGED
data/ext/nmatrix/data/data.h
CHANGED
@@ -55,7 +55,7 @@ namespace nm {
|
|
55
55
|
const int NUM_DTYPES = 13;
|
56
56
|
const int NUM_ITYPES = 4;
|
57
57
|
const int NUM_EWOPS = 12;
|
58
|
-
const int NUM_UNARYOPS =
|
58
|
+
const int NUM_UNARYOPS = 22;
|
59
59
|
const int NUM_NONCOM_EWOPS = 3;
|
60
60
|
|
61
61
|
enum ewop_t {
|
@@ -99,7 +99,8 @@ namespace nm {
|
|
99
99
|
UNARY_ERF,
|
100
100
|
UNARY_ERFC,
|
101
101
|
UNARY_CBRT,
|
102
|
-
UNARY_GAMMA
|
102
|
+
UNARY_GAMMA,
|
103
|
+
UNARY_NEGATE
|
103
104
|
};
|
104
105
|
|
105
106
|
// element-wise and scalar operators
|
data/ext/nmatrix/extconf.rb
CHANGED
@@ -70,13 +70,8 @@ def create_conf_h(file) #:nodoc:
|
|
70
70
|
hfile.puts
|
71
71
|
|
72
72
|
# FIXME: Find a better way to do this:
|
73
|
-
if RUBY_VERSION >= '2.0'
|
74
|
-
|
75
|
-
end
|
76
|
-
|
77
|
-
if RUBY_VERSION < '1.9.3'
|
78
|
-
hfile.puts "#define OLD_RB_SCAN_ARGS"
|
79
|
-
end
|
73
|
+
hfile.puts "#define RUBY_2 1" if RUBY_VERSION >= '2.0'
|
74
|
+
hfile.puts "#define OLD_RB_SCAN_ARGS" if RUBY_VERSION < '1.9.3'
|
80
75
|
|
81
76
|
for line in $defs
|
82
77
|
line =~ /^-D(.*)/
|
@@ -231,9 +226,9 @@ $libs += " -llapack -lcblas -latlas "
|
|
231
226
|
|
232
227
|
|
233
228
|
# For release, these next two should both be changed to -O3.
|
234
|
-
$CFLAGS += " -O3
|
229
|
+
$CFLAGS += " -O3" #" -O0 -g "
|
235
230
|
#$CFLAGS += " -static -O0 -g "
|
236
|
-
$CPPFLAGS += " -O3 -std=#{$CPP_STANDARD}
|
231
|
+
$CPPFLAGS += " -O3 -std=#{$CPP_STANDARD}" #" -O0 -g -std=#{$CPP_STANDARD} " #-fmax-errors=10 -save-temps
|
237
232
|
#$CPPFLAGS += " -static -O0 -g -std=#{$CPP_STANDARD} "
|
238
233
|
|
239
234
|
CONFIG['warnflags'].gsub!('-Wshorten-64-to-32', '') # doesn't work except in Mac-patched gcc (4.2)
|
data/ext/nmatrix/math.cpp
CHANGED
@@ -174,6 +174,7 @@ extern "C" {
|
|
174
174
|
static VALUE nm_cblas_syrk(VALUE self, VALUE order, VALUE uplo, VALUE trans, VALUE n, VALUE k, VALUE alpha, VALUE a,
|
175
175
|
VALUE lda, VALUE beta, VALUE c, VALUE ldc);
|
176
176
|
|
177
|
+
static VALUE nm_has_clapack(VALUE self);
|
177
178
|
static VALUE nm_clapack_getrf(VALUE self, VALUE order, VALUE m, VALUE n, VALUE a, VALUE lda);
|
178
179
|
static VALUE nm_clapack_potrf(VALUE self, VALUE order, VALUE uplo, VALUE n, VALUE a, VALUE lda);
|
179
180
|
static VALUE nm_clapack_getrs(VALUE self, VALUE order, VALUE trans, VALUE n, VALUE nrhs, VALUE a, VALUE lda, VALUE ipiv, VALUE b, VALUE ldb);
|
@@ -223,6 +224,46 @@ void det_exact(const int M, const void* A_elements, const int lda, void* result_
|
|
223
224
|
}
|
224
225
|
|
225
226
|
|
227
|
+
/*
|
228
|
+
* Calculate the inverse for a dense matrix (A [elements]) of size 2 or 3. Places the result in B_elements.
|
229
|
+
*/
|
230
|
+
template <typename DType>
|
231
|
+
void inverse_exact(const int M, const void* A_elements, const int lda, void* B_elements, const int ldb) {
|
232
|
+
const DType* A = reinterpret_cast<const DType*>(A_elements);
|
233
|
+
DType* B = reinterpret_cast<DType*>(B_elements);
|
234
|
+
|
235
|
+
if (M == 2) {
|
236
|
+
DType det = A[0] * A[lda+1] - A[1] * A[lda];
|
237
|
+
B[0] = A[lda+1] / det;
|
238
|
+
B[1] = -A[1] / det;
|
239
|
+
B[ldb] = -A[lda] / det;
|
240
|
+
B[ldb+1] = -A[0] / det;
|
241
|
+
|
242
|
+
} else if (M == 3) {
|
243
|
+
// Calculate the exact determinant.
|
244
|
+
DType det;
|
245
|
+
det_exact<DType>(M, A_elements, lda, reinterpret_cast<void*>(&det));
|
246
|
+
if (det == 0) {
|
247
|
+
rb_raise(nm_eNotInvertibleError, "matrix must have non-zero determinant to be invertible (not getting this error does not mean matrix is invertible if you're dealing with floating points)");
|
248
|
+
}
|
249
|
+
|
250
|
+
B[0] = ( A[lda+1] * A[2*lda+2] - A[lda+2] * A[2*lda+1]) / det; // A = ei - fh
|
251
|
+
B[1] = (- A[1] * A[2*lda+2] + A[2] * A[2*lda+1]) / det; // D = -bi + ch
|
252
|
+
B[2] = ( A[1] * A[lda+2] - A[2] * A[lda+1]) / det; // G = bf - ce
|
253
|
+
B[ldb] = (- A[lda] * A[2*lda+2] + A[lda+2] * A[2*lda]) / det; // B = -di + fg
|
254
|
+
B[ldb+1] = ( A[0] * A[2*lda+2] - A[2] * A[2*lda]) / det; // E = ai - cg
|
255
|
+
B[ldb+2] = (- A[0] * A[lda+2] + A[2] * A[lda]) / det; // H = -af + cd
|
256
|
+
B[2*ldb] = ( A[lda] * A[2*lda+1] - A[lda+1] * A[2*lda]) / det; // C = dh - eg
|
257
|
+
B[2*ldb+1]= ( -A[0] * A[2*lda+1] + A[1] * A[2*lda]) / det; // F = -ah + bg
|
258
|
+
B[2*ldb+2]= ( A[0] * A[lda+1] - A[1] * A[lda]) / det; // I = ae - bd
|
259
|
+
} else if (M == 1) {
|
260
|
+
B[0] = 1 / A[0];
|
261
|
+
} else {
|
262
|
+
rb_raise(rb_eNotImpError, "exact inverse calculation needed for matrices larger than 3x3");
|
263
|
+
}
|
264
|
+
}
|
265
|
+
|
266
|
+
|
226
267
|
/*
|
227
268
|
* Function signature conversion for calling CBLAS' gesvd functions as directly as possible.
|
228
269
|
*/
|
@@ -343,6 +384,8 @@ extern "C" {
|
|
343
384
|
void nm_math_init_blas() {
|
344
385
|
cNMatrix_LAPACK = rb_define_module_under(cNMatrix, "LAPACK");
|
345
386
|
|
387
|
+
rb_define_singleton_method(cNMatrix, "has_clapack?", (METHOD)nm_has_clapack, 0);
|
388
|
+
|
346
389
|
/* ATLAS-CLAPACK Functions */
|
347
390
|
rb_define_singleton_method(cNMatrix_LAPACK, "clapack_getrf", (METHOD)nm_clapack_getrf, 5);
|
348
391
|
rb_define_singleton_method(cNMatrix_LAPACK, "clapack_potrf", (METHOD)nm_clapack_potrf, 5);
|
@@ -1404,6 +1447,19 @@ static VALUE nm_clapack_potrs(VALUE self, VALUE order, VALUE uplo, VALUE n, VALU
|
|
1404
1447
|
}
|
1405
1448
|
|
1406
1449
|
|
1450
|
+
/*
|
1451
|
+
* Simple way to check from within Ruby code if clapack functions are available, without
|
1452
|
+
* having to wait around for an exception to be thrown.
|
1453
|
+
*/
|
1454
|
+
static VALUE nm_has_clapack(VALUE self) {
|
1455
|
+
#ifndef HAVE_CLAPACK_H
|
1456
|
+
return Qfalse;
|
1457
|
+
#else
|
1458
|
+
return Qtrue;
|
1459
|
+
#endif
|
1460
|
+
}
|
1461
|
+
|
1462
|
+
|
1407
1463
|
/* Call any of the clapack_xgetri functions as directly as possible.
|
1408
1464
|
*
|
1409
1465
|
* You probably don't want to call this function. Instead, why don't you try clapack_getri, which is more flexible
|
@@ -1562,6 +1618,15 @@ void nm_math_det_exact(const int M, const void* elements, const int lda, nm::dty
|
|
1562
1618
|
ttable[dtype](M, elements, lda, result);
|
1563
1619
|
}
|
1564
1620
|
|
1621
|
+
/*
|
1622
|
+
* C accessor for calculating an exact inverse.
|
1623
|
+
*/
|
1624
|
+
void nm_math_inverse_exact(const int M, const void* A_elements, const int lda, void* B_elements, const int ldb, nm::dtype_t dtype) {
|
1625
|
+
NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::math::inverse_exact, void, const int, const void*, const int, void*, const int);
|
1626
|
+
|
1627
|
+
ttable[dtype](M, A_elements, lda, B_elements, ldb);
|
1628
|
+
}
|
1629
|
+
|
1565
1630
|
|
1566
1631
|
/*
|
1567
1632
|
* Transpose an array of elements that represent a row-major dense matrix. Does not allocate anything, only does an memcpy.
|
data/ext/nmatrix/math/math.h
CHANGED
@@ -104,6 +104,7 @@ extern "C" {
|
|
104
104
|
* C accessors.
|
105
105
|
*/
|
106
106
|
void nm_math_det_exact(const int M, const void* elements, const int lda, nm::dtype_t dtype, void* result);
|
107
|
+
void nm_math_inverse_exact(const int M, const void* A_elements, const int lda, void* B_elements, const int ldb, nm::dtype_t dtype);
|
107
108
|
void nm_math_transpose_generic(const size_t M, const size_t N, const void* A, const int lda, void* B, const int ldb, size_t element_size);
|
108
109
|
void nm_math_init_blas(void);
|
109
110
|
|
data/ext/nmatrix/nmatrix.h
CHANGED
@@ -248,8 +248,8 @@ NM_DEF_STORAGE_STRUCT;
|
|
248
248
|
|
249
249
|
/* Dense Storage */
|
250
250
|
NM_DEF_STORAGE_CHILD_STRUCT_PRE(DENSE_STORAGE); // struct DENSE_STORAGE : STORAGE {
|
251
|
-
|
252
|
-
|
251
|
+
void* elements; // should go first to align with void* a in yale and NODE* first in list.
|
252
|
+
size_t* stride;
|
253
253
|
NM_DEF_STORAGE_STRUCT_POST(DENSE_STORAGE); // };
|
254
254
|
|
255
255
|
/* Yale Storage */
|
data/ext/nmatrix/ruby_nmatrix.c
CHANGED
@@ -65,6 +65,8 @@ static VALUE is_symmetric(VALUE self, bool hermitian);
|
|
65
65
|
static VALUE nm_guess_dtype(VALUE self, VALUE v);
|
66
66
|
static VALUE nm_min_dtype(VALUE self, VALUE v);
|
67
67
|
|
68
|
+
static VALUE nm_data_pointer(VALUE self);
|
69
|
+
|
68
70
|
/*
|
69
71
|
* Macro defines an element-wise accessor function for some operation.
|
70
72
|
*
|
@@ -129,6 +131,7 @@ DECL_UNARY_RUBY_ACCESSOR(erf)
|
|
129
131
|
DECL_UNARY_RUBY_ACCESSOR(erfc)
|
130
132
|
DECL_UNARY_RUBY_ACCESSOR(cbrt)
|
131
133
|
DECL_UNARY_RUBY_ACCESSOR(gamma)
|
134
|
+
DECL_UNARY_RUBY_ACCESSOR(negate)
|
132
135
|
DECL_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(atan2)
|
133
136
|
DECL_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(ldexp)
|
134
137
|
DECL_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(hypot)
|
@@ -149,7 +152,9 @@ static VALUE matrix_multiply_scalar(NMATRIX* left, VALUE scalar);
|
|
149
152
|
static VALUE matrix_multiply(NMATRIX* left, NMATRIX* right);
|
150
153
|
static VALUE nm_multiply(VALUE left_v, VALUE right_v);
|
151
154
|
static VALUE nm_det_exact(VALUE self);
|
155
|
+
static VALUE nm_inverse_exact(VALUE self, VALUE inverse);
|
152
156
|
static VALUE nm_complex_conjugate_bang(VALUE self);
|
157
|
+
static VALUE nm_reshape_bang(VALUE self, VALUE arg);
|
153
158
|
|
154
159
|
static nm::dtype_t interpret_dtype(int argc, VALUE* argv, nm::stype_t stype);
|
155
160
|
static void* interpret_initial_value(VALUE arg, nm::dtype_t dtype);
|
@@ -190,6 +195,16 @@ void Init_nmatrix() {
|
|
190
195
|
*/
|
191
196
|
nm_eStorageTypeError = rb_define_class("StorageTypeError", rb_eStandardError);
|
192
197
|
|
198
|
+
/*
|
199
|
+
* Exception raise when the matrix shape is not appropriate for a given operation.
|
200
|
+
*/
|
201
|
+
nm_eShapeError = rb_define_class("ShapeError", rb_eStandardError);
|
202
|
+
|
203
|
+
/*
|
204
|
+
* Exception raise when an inverse is requested but the matrix is not invertible.
|
205
|
+
*/
|
206
|
+
nm_eNotInvertibleError = rb_define_class("NotInvertibleError", rb_eStandardError);
|
207
|
+
|
193
208
|
/*
|
194
209
|
* Class that holds values in use by the C code.
|
195
210
|
*/
|
@@ -243,7 +258,9 @@ void Init_nmatrix() {
|
|
243
258
|
rb_define_method(cNMatrix, "supershape", (METHOD)nm_supershape, 0);
|
244
259
|
rb_define_method(cNMatrix, "offset", (METHOD)nm_offset, 0);
|
245
260
|
rb_define_method(cNMatrix, "det_exact", (METHOD)nm_det_exact, 0);
|
261
|
+
rb_define_protected_method(cNMatrix, "__inverse_exact__", (METHOD)nm_inverse_exact, 1);
|
246
262
|
rb_define_method(cNMatrix, "complex_conjugate!", (METHOD)nm_complex_conjugate_bang, 0);
|
263
|
+
rb_define_protected_method(cNMatrix, "reshape_bang", (METHOD)nm_reshape_bang, 1);
|
247
264
|
|
248
265
|
rb_define_protected_method(cNMatrix, "__dense_each__", (METHOD)nm_dense_each, 0);
|
249
266
|
rb_define_protected_method(cNMatrix, "__dense_map__", (METHOD)nm_dense_map, 0);
|
@@ -293,6 +310,7 @@ void Init_nmatrix() {
|
|
293
310
|
rb_define_method(cNMatrix, "cbrt", (METHOD)nm_unary_cbrt, 0);
|
294
311
|
rb_define_method(cNMatrix, "gamma", (METHOD)nm_unary_gamma, 0);
|
295
312
|
rb_define_method(cNMatrix, "log", (METHOD)nm_unary_log, -1);
|
313
|
+
rb_define_method(cNMatrix, "-@", (METHOD)nm_unary_negate,0);
|
296
314
|
|
297
315
|
rb_define_method(cNMatrix, "=~", (METHOD)nm_ew_eqeq, 1);
|
298
316
|
rb_define_method(cNMatrix, "!~", (METHOD)nm_ew_neq, 1);
|
@@ -316,6 +334,11 @@ void Init_nmatrix() {
|
|
316
334
|
|
317
335
|
rb_define_method(cNMatrix, "capacity", (METHOD)nm_capacity, 0);
|
318
336
|
|
337
|
+
/////////////////
|
338
|
+
// FFI Methods //
|
339
|
+
/////////////////
|
340
|
+
rb_define_method(cNMatrix, "data_pointer", (METHOD)nm_data_pointer, 0);
|
341
|
+
|
319
342
|
/////////////
|
320
343
|
// Aliases //
|
321
344
|
/////////////
|
@@ -900,6 +923,7 @@ DEF_UNARY_RUBY_ACCESSOR(ERF, erf)
|
|
900
923
|
DEF_UNARY_RUBY_ACCESSOR(ERFC, erfc)
|
901
924
|
DEF_UNARY_RUBY_ACCESSOR(CBRT, cbrt)
|
902
925
|
DEF_UNARY_RUBY_ACCESSOR(GAMMA, gamma)
|
926
|
+
DEF_UNARY_RUBY_ACCESSOR(NEGATE, negate)
|
903
927
|
|
904
928
|
DEF_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(ATAN2, atan2)
|
905
929
|
DEF_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(LDEXP, ldexp)
|
@@ -1000,6 +1024,51 @@ static VALUE nm_complex_conjugate_bang(VALUE self) {
|
|
1000
1024
|
return self;
|
1001
1025
|
}
|
1002
1026
|
|
1027
|
+
|
1028
|
+
/*
|
1029
|
+
* call-seq:
|
1030
|
+
* __reshape!__ -> NMatrix
|
1031
|
+
*
|
1032
|
+
* Reshapes the matrix (in-place) to the desired shape. Note that this function does not do a resize; the product of
|
1033
|
+
* the new and old shapes' components must be equal.
|
1034
|
+
*
|
1035
|
+
*/
|
1036
|
+
static VALUE nm_reshape_bang(VALUE self, VALUE arg){
|
1037
|
+
NMATRIX* m;
|
1038
|
+
UnwrapNMatrix(self, m);
|
1039
|
+
if(m->stype == nm::DENSE_STORE){
|
1040
|
+
DENSE_STORAGE* s = NM_STORAGE_DENSE(self);
|
1041
|
+
VALUE shape_ary = arg;
|
1042
|
+
size_t dim;
|
1043
|
+
size_t size = nm_storage_count_max_elements(s);
|
1044
|
+
size_t new_size = 1;
|
1045
|
+
size_t* shape = interpret_shape(shape_ary, &dim);
|
1046
|
+
void* elem = s->elements;
|
1047
|
+
for (size_t index = 0; index < dim; ++index){
|
1048
|
+
new_size *= shape[index];}
|
1049
|
+
|
1050
|
+
if (size == new_size){
|
1051
|
+
s->shape = shape;
|
1052
|
+
s->dim = dim;
|
1053
|
+
size_t i, j;
|
1054
|
+
size_t* stride = NM_ALLOC_N(size_t, dim);
|
1055
|
+
for (i = 0; i < dim; ++i) {
|
1056
|
+
stride[i] = 1;
|
1057
|
+
for (j = i+1; j < dim; ++j) {
|
1058
|
+
stride[i] *= shape[j];
|
1059
|
+
}
|
1060
|
+
s->stride = stride;
|
1061
|
+
}
|
1062
|
+
return self;
|
1063
|
+
}
|
1064
|
+
else
|
1065
|
+
rb_raise(rb_eArgError, "reshape cannot resize; size of new and old matrices must match");
|
1066
|
+
}
|
1067
|
+
else {
|
1068
|
+
rb_raise(rb_eNotImpError, "reshape in place only for dense stype");
|
1069
|
+
}
|
1070
|
+
}
|
1071
|
+
|
1003
1072
|
/*
|
1004
1073
|
* Helper function for creating a matrix. You have to create the storage and pass it in, but you don't
|
1005
1074
|
* need to worry about deleting it.
|
@@ -1958,6 +2027,21 @@ static VALUE nm_multiply(VALUE left_v, VALUE right_v) {
|
|
1958
2027
|
CheckNMatrixType(right_v);
|
1959
2028
|
UnwrapNMatrix( right_v, right );
|
1960
2029
|
|
2030
|
+
// work like vector dot product for 1dim
|
2031
|
+
if (left->storage->dim == 1 && right->storage->dim == 1) {
|
2032
|
+
if (left->storage->shape[0] != right->storage->shape[0]) {
|
2033
|
+
NM_CONSERVATIVE(nm_unregister_value(left_v));
|
2034
|
+
NM_CONSERVATIVE(nm_unregister_value(right_v));
|
2035
|
+
rb_raise(rb_eArgError, "The left- and right-hand sides of the operation must have the same dimensionality.");
|
2036
|
+
} else {
|
2037
|
+
VALUE result = elementwise_op(nm::EW_MUL, left_v, right_v);
|
2038
|
+
VALUE to_return = rb_funcall(result, rb_intern("sum"),0);
|
2039
|
+
NM_CONSERVATIVE(nm_unregister_value(left_v));
|
2040
|
+
NM_CONSERVATIVE(nm_unregister_value(right_v));
|
2041
|
+
return to_return;
|
2042
|
+
}
|
2043
|
+
}
|
2044
|
+
|
1961
2045
|
if (left->storage->shape[1] != right->storage->shape[0]) {
|
1962
2046
|
NM_CONSERVATIVE(nm_unregister_value(left_v));
|
1963
2047
|
NM_CONSERVATIVE(nm_unregister_value(right_v));
|
@@ -2569,8 +2653,11 @@ static SLICE* get_slice(size_t dim, int argc, VALUE* arg, size_t* shape) {
|
|
2569
2653
|
slice->lengths[r] = 1;
|
2570
2654
|
|
2571
2655
|
} else if (FIXNUM_P(v)) { // this used CLASS_OF before, which is inefficient for fixnum
|
2572
|
-
|
2573
|
-
|
2656
|
+
int v_ = FIX2INT(v);
|
2657
|
+
if (v_ < 0) // checking for negative indexes
|
2658
|
+
slice->coords[r] = shape[r]+v_;
|
2659
|
+
else
|
2660
|
+
slice->coords[r] = v_;
|
2574
2661
|
slice->lengths[r] = 1;
|
2575
2662
|
t++;
|
2576
2663
|
|
@@ -2584,8 +2671,15 @@ static SLICE* get_slice(size_t dim, int argc, VALUE* arg, size_t* shape) {
|
|
2584
2671
|
} else if (TYPE(arg[t]) == T_HASH) { // 3:5 notation (inclusive)
|
2585
2672
|
VALUE begin_end = rb_funcall(v, rb_intern("shift"), 0); // rb_hash_shift
|
2586
2673
|
nm_register_value(begin_end);
|
2587
|
-
|
2588
|
-
|
2674
|
+
|
2675
|
+
if (rb_ary_entry(begin_end, 0) >= 0)
|
2676
|
+
slice->coords[r] = FIX2INT(rb_ary_entry(begin_end, 0));
|
2677
|
+
else
|
2678
|
+
slice->coords[r] = shape[r] + FIX2INT(rb_ary_entry(begin_end, 0));
|
2679
|
+
if (rb_ary_entry(begin_end, 1) >= 0)
|
2680
|
+
slice->lengths[r] = FIX2INT(rb_ary_entry(begin_end, 1)) - slice->coords[r];
|
2681
|
+
else
|
2682
|
+
slice->lengths[r] = shape[r] + FIX2INT(rb_ary_entry(begin_end, 1)) - slice->coords[r];
|
2589
2683
|
|
2590
2684
|
if (RHASH_EMPTY_P(v)) t++; // go on to the next
|
2591
2685
|
slice->single = false;
|
@@ -2593,12 +2687,19 @@ static SLICE* get_slice(size_t dim, int argc, VALUE* arg, size_t* shape) {
|
|
2593
2687
|
|
2594
2688
|
} else if (CLASS_OF(v) == rb_cRange) {
|
2595
2689
|
rb_range_values(arg[t], &beg, &end, &excl);
|
2596
|
-
|
2690
|
+
|
2691
|
+
int begin_ = FIX2INT(beg);
|
2692
|
+
int end_ = FIX2INT(end);
|
2693
|
+
|
2694
|
+
slice->coords[r] = (begin_ < 0) ? shape[r] + begin_ : begin_;
|
2695
|
+
|
2597
2696
|
// Exclude last element for a...b range
|
2598
|
-
|
2697
|
+
if (end_ < 0)
|
2698
|
+
slice->lengths[r] = shape[r] + end_ - slice->coords[r] + (excl ? 0 : 1);
|
2699
|
+
else
|
2700
|
+
slice->lengths[r] = end_ - slice->coords[r] + (excl ? 0 : 1);
|
2599
2701
|
|
2600
2702
|
slice->single = false;
|
2601
|
-
|
2602
2703
|
t++;
|
2603
2704
|
|
2604
2705
|
} else {
|
@@ -2829,6 +2930,30 @@ static VALUE matrix_multiply(NMATRIX* left, NMATRIX* right) {
|
|
2829
2930
|
return to_return;
|
2830
2931
|
}
|
2831
2932
|
|
2933
|
+
|
2934
|
+
/*
|
2935
|
+
* Calculate the exact inverse of a 2x2 or 3x3 matrix.
|
2936
|
+
*
|
2937
|
+
* Does not test for invertibility!
|
2938
|
+
*/
|
2939
|
+
static VALUE nm_inverse_exact(VALUE self, VALUE inverse) {
|
2940
|
+
|
2941
|
+
if (NM_STYPE(self) != nm::DENSE_STORE) {
|
2942
|
+
rb_raise(rb_eNotImpError, "needs exact determinant implementation for this matrix stype");
|
2943
|
+
return Qnil;
|
2944
|
+
}
|
2945
|
+
|
2946
|
+
if (NM_DIM(self) != 2 || NM_SHAPE0(self) != NM_SHAPE1(self)) {
|
2947
|
+
rb_raise(nm_eShapeError, "matrices must be square to have an inverse defined");
|
2948
|
+
return Qnil;
|
2949
|
+
}
|
2950
|
+
|
2951
|
+
// Calculate the exact inverse.
|
2952
|
+
nm_math_inverse_exact(NM_SHAPE0(self), NM_STORAGE_DENSE(self)->elements, NM_SHAPE0(self), NM_STORAGE_DENSE(inverse)->elements, NM_SHAPE0(inverse), NM_DTYPE(self));
|
2953
|
+
|
2954
|
+
return inverse;
|
2955
|
+
}
|
2956
|
+
|
2832
2957
|
/*
|
2833
2958
|
* Calculate the exact determinant of a dense matrix.
|
2834
2959
|
*
|
@@ -2839,9 +2964,11 @@ static VALUE matrix_multiply(NMATRIX* left, NMATRIX* right) {
|
|
2839
2964
|
static VALUE nm_det_exact(VALUE self) {
|
2840
2965
|
|
2841
2966
|
if (NM_STYPE(self) != nm::DENSE_STORE) {
|
2842
|
-
rb_raise(
|
2967
|
+
rb_raise(rb_eNotImpError, "can only calculate exact determinant for dense matrices");
|
2968
|
+
return Qnil;
|
2843
2969
|
}
|
2844
2970
|
if (NM_DIM(self) != 2 || NM_SHAPE0(self) != NM_SHAPE1(self)) {
|
2971
|
+
rb_raise(nm_eShapeError, "matrices must be square to have a determinant defined");
|
2845
2972
|
return Qnil;
|
2846
2973
|
}
|
2847
2974
|
|
@@ -2864,6 +2991,24 @@ static VALUE nm_det_exact(VALUE self) {
|
|
2864
2991
|
return to_return;
|
2865
2992
|
}
|
2866
2993
|
|
2994
|
+
|
2995
|
+
|
2996
|
+
/*
|
2997
|
+
* Returns the pointer to the matrix storage's data. This is useful primarily when you are using FFI with NMatrix --
|
2998
|
+
* say, for example, you want to pass a float* to some function, and your NMatrix is a :float32 :dense matrix. Then you
|
2999
|
+
* can call this function and get that pointer directly instead of copying the data.
|
3000
|
+
*/
|
3001
|
+
static VALUE nm_data_pointer(VALUE self) {
|
3002
|
+
//if (NM_DTYPE(self) == nm::LIST_STORE)
|
3003
|
+
// rb_warn("pointer requested for list storage, which may be meaningless");
|
3004
|
+
|
3005
|
+
// This is actually pretty easy, since all of the storage types have their elements positioned in the same place
|
3006
|
+
// relative to one another. So yes, believe it or not, this should work just as well for Yale or list storage as for
|
3007
|
+
// dense.
|
3008
|
+
return INT2FIX(NM_STORAGE_DENSE(self)->elements);
|
3009
|
+
}
|
3010
|
+
|
3011
|
+
|
2867
3012
|
/////////////////
|
2868
3013
|
// Exposed API //
|
2869
3014
|
/////////////////
|