nmatrix 0.1.0.rc1 → 0.1.0.rc2
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/.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
|
/////////////////
|