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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fbe2c20e1a86c29f525cda90fd74b6e916b8e406
4
- data.tar.gz: 0f50913f5df2579d6d7acb8986f5c5321bdf3bec
3
+ metadata.gz: 4052431a5f81ea9304f96fe293f6f22a47dacf3d
4
+ data.tar.gz: 777b0fd050d7f0726ea97f337d5f4ce3cf850f8b
5
5
  SHA512:
6
- metadata.gz: 0aa8a2a396a4c88b0ed73a7674d95ccafd63fe8394cad06319531e0465a12628851dfb796454c6c41b52d102d5688b413f114b4b3a8560de566ea69089da32e7
7
- data.tar.gz: 44ec7c62e47ccd107e584500124019a0847ab67a8f88495d376416e491745d7f8550f0aba10d9f5dea6bc90ecd3e8adc23eba5a28fea42b05c22d061a1e103a2
6
+ metadata.gz: daabd9cebbe93674079728f254cf5429bb1d0cadfb2ddbb56061da9181508852bd919844baf7e63615237fe6f2fc864dd27515b97153ea7704185001417a2f27
7
+ data.tar.gz: b36dcd576afedd2ddc1fa57af569823fb72f98d0e11cd0e1d7ea751553e1b2de39fe32a197d0a731a308ec28a6e96aedcc0687b73853f88ab0002c6145e53e7b
@@ -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
- # Gemfile
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
@@ -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 / 2014-12-28
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
+
@@ -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
 
@@ -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.3+
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-rc1.gem
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
- * Matlab .MAT v5 file input
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 nearly the release of NMatrix 0.1.0, our first beta.
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
@@ -66,8 +66,6 @@ VALGRIND_MEMORYFILL_OPTIONS = [
66
66
  GDB_OPTIONS = []
67
67
 
68
68
 
69
- RSpec::Core::RakeTask.new(:spec)
70
-
71
69
  task :console do |task|
72
70
  cmd = [ 'irb', "-r './lib/nmatrix.rb'" ]
73
71
  run *cmd
@@ -86,7 +86,8 @@ namespace nm {
86
86
  "asinh", "acosh", "atanh",
87
87
  "exp", "log2",
88
88
  "log10", "sqrt", "erf",
89
- "erfc", "cbrt", "gamma"
89
+ "erfc", "cbrt", "gamma",
90
+ "negate"
90
91
  };
91
92
 
92
93
  } // end of namespace nm
@@ -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 = 21;
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
@@ -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
- hfile.puts "#define RUBY_2 1"
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 -g" #" -O0 -g "
229
+ $CFLAGS += " -O3" #" -O0 -g "
235
230
  #$CFLAGS += " -static -O0 -g "
236
- $CPPFLAGS += " -O3 -std=#{$CPP_STANDARD} -g" #" -O0 -g -std=#{$CPP_STANDARD} " #-fmax-errors=10 -save-temps
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)
@@ -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.
@@ -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
 
@@ -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
- size_t* stride;
252
- void* elements;
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 */
@@ -94,7 +94,9 @@ VALUE cNMatrix,
94
94
 
95
95
  nm_eDataTypeError,
96
96
  nm_eConvergenceError,
97
- nm_eStorageTypeError;
97
+ nm_eStorageTypeError,
98
+ nm_eShapeError,
99
+ nm_eNotInvertibleError;
98
100
 
99
101
  /*
100
102
  * Functions
@@ -96,7 +96,9 @@ extern VALUE cNMatrix,
96
96
 
97
97
  nm_eDataTypeError,
98
98
  nm_eConvergenceError,
99
- nm_eStorageTypeError;
99
+ nm_eStorageTypeError,
100
+ nm_eShapeError,
101
+ nm_eNotInvertibleError;
100
102
 
101
103
  /*
102
104
  * Functions
@@ -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
- slice->coords[r] = FIX2UINT(v);
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
- slice->coords[r] = FIX2UINT(rb_ary_entry(begin_end, 0));
2588
- slice->lengths[r] = FIX2UINT(rb_ary_entry(begin_end, 1)) - slice->coords[r];
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
- slice->coords[r] = FIX2UINT(beg);
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
- slice->lengths[r] = FIX2UINT(end) - slice->coords[r] + (excl ? 0 : 1);
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(nm_eStorageTypeError, "can only calculate exact determinant for dense matrices");
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
  /////////////////