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 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
  /////////////////