[DRE-commits] [ruby-nmatrix] 01/01: Imported Upstream version 0.1.0~rc1
Cédric Boutillier
boutil at moszumanska.debian.org
Thu Feb 6 14:25:35 UTC 2014
This is an automated email from the git hooks/post-receive script.
boutil pushed a commit to annotated tag upstream/0.1.0_rc1
in repository ruby-nmatrix.
commit 78eb5813de428b3c8917dcdd7965bae612c598b9
Author: Cédric Boutillier <boutil at debian.org>
Date: Sun Jan 12 11:02:38 2014 +0100
Imported Upstream version 0.1.0~rc1
---
Gemfile | 1 +
History.txt | 96 +-
LICENSE.txt | 4 +-
README.rdoc | 50 +-
Rakefile | 48 +-
checksums.yaml.gz | Bin 268 -> 270 bytes
ext/nmatrix/data/complex.h | 4 +-
ext/nmatrix/data/data.cpp | 78 +-
ext/nmatrix/data/data.h | 96 +-
ext/nmatrix/data/meta.h | 4 +-
ext/nmatrix/data/rational.h | 4 +-
ext/nmatrix/data/ruby_object.h | 4 +-
ext/nmatrix/extconf.rb | 173 +--
ext/nmatrix/math.cpp | 85 +-
ext/nmatrix/math/asum.h | 6 +-
ext/nmatrix/math/geev.h | 4 +-
ext/nmatrix/math/gemm.h | 8 +-
ext/nmatrix/math/gemv.h | 8 +-
ext/nmatrix/math/ger.h | 4 +-
ext/nmatrix/math/gesdd.h | 4 +-
ext/nmatrix/math/gesvd.h | 4 +-
ext/nmatrix/math/getf2.h | 4 +-
ext/nmatrix/math/getrf.h | 4 +-
ext/nmatrix/math/getri.h | 4 +-
ext/nmatrix/math/getrs.h | 10 +-
ext/nmatrix/math/idamax.h | 4 +-
ext/nmatrix/math/inc.h | 18 +-
ext/nmatrix/math/laswp.h | 4 +-
ext/nmatrix/math/long_dtype.h | 4 +-
ext/nmatrix/math/math.h | 26 +-
ext/nmatrix/math/nrm2.h | 6 +-
ext/nmatrix/math/potrs.h | 10 +-
ext/nmatrix/math/rot.h | 4 +-
ext/nmatrix/math/rotg.h | 4 +-
ext/nmatrix/math/scal.h | 4 +-
ext/nmatrix/math/swap.h | 4 +-
ext/nmatrix/math/trsm.h | 10 +-
ext/nmatrix/nm_memory.h | 60 ++
ext/nmatrix/nmatrix.cpp | 60 +-
ext/nmatrix/nmatrix.h | 49 +-
ext/nmatrix/ruby_constants.cpp | 6 +-
ext/nmatrix/ruby_constants.h | 6 +-
ext/nmatrix/ruby_nmatrix.c | 1107 +++++++++++++++++---
ext/nmatrix/storage/common.cpp | 4 +-
ext/nmatrix/storage/common.h | 4 +-
ext/nmatrix/storage/{ => dense}/dense.cpp | 353 +++++--
ext/nmatrix/storage/{ => dense}/dense.h | 11 +-
ext/nmatrix/storage/{ => list}/list.cpp | 615 +++++++++--
ext/nmatrix/storage/{ => list}/list.h | 19 +-
ext/nmatrix/storage/storage.cpp | 67 +-
ext/nmatrix/storage/storage.h | 8 +-
ext/nmatrix/storage/yale/class.h | 155 ++-
ext/nmatrix/storage/yale/iterators/base.h | 4 +-
ext/nmatrix/storage/yale/iterators/iterator.h | 4 +-
ext/nmatrix/storage/yale/iterators/row.h | 4 +-
ext/nmatrix/storage/yale/iterators/row_stored.h | 4 +-
ext/nmatrix/storage/yale/iterators/row_stored_nd.h | 7 +-
.../storage/yale/iterators/stored_diagonal.h | 4 +-
ext/nmatrix/storage/yale/math/transpose.h | 4 +-
ext/nmatrix/storage/yale/yale.cpp | 395 ++++++-
ext/nmatrix/storage/yale/yale.h | 10 +-
ext/nmatrix/types.h | 4 +-
ext/nmatrix/util/io.cpp | 10 +-
ext/nmatrix/util/io.h | 4 +-
ext/nmatrix/util/sl_list.cpp | 67 +-
ext/nmatrix/util/sl_list.h | 6 +-
ext/nmatrix/util/util.h | 4 +-
lib/nmatrix.rb | 4 +-
lib/nmatrix/blas.rb | 4 +-
lib/nmatrix/enumerate.rb | 23 +-
lib/nmatrix/io/market.rb | 5 +-
lib/nmatrix/io/mat5_reader.rb | 4 +-
lib/nmatrix/io/mat_reader.rb | 4 +-
lib/nmatrix/lapack.rb | 92 +-
lib/nmatrix/math.rb | 233 +++-
lib/nmatrix/monkeys.rb | 26 +-
lib/nmatrix/nmatrix.rb | 403 ++++++-
lib/nmatrix/nvector.rb | 66 +-
lib/nmatrix/rspec.rb | 4 +-
lib/nmatrix/shortcuts.rb | 75 +-
lib/nmatrix/version.rb | 14 +-
lib/nmatrix/yale_functions.rb | 8 +-
metadata.yml | 30 +-
nmatrix.gemspec | 9 +-
scripts/mac-brew-gcc.sh | 19 +-
scripts/mac-mavericks-brew-gcc.sh | 22 +
spec/00_nmatrix_spec.rb | 123 ++-
spec/01_enum_spec.rb | 20 +-
spec/02_slice_spec.rb | 14 +-
spec/blas_spec.rb | 7 +-
spec/elementwise_spec.rb | 7 +-
spec/io_spec.rb | 44 +-
spec/lapack_spec.rb | 166 ++-
spec/math_spec.rb | 99 +-
spec/nmatrix_yale_spec.rb | 47 +-
spec/rspec_monkeys.rb | 27 +
spec/rspec_spec.rb | 4 +-
spec/shortcuts_spec.rb | 15 +-
spec/slice_set_spec.rb | 8 +-
spec/spec_helper.rb | 5 +-
spec/stat_spec.rb | 332 +++---
101 files changed, 4398 insertions(+), 1425 deletions(-)
diff --git a/Gemfile b/Gemfile
index d9e84e7..7aac379 100644
--- a/Gemfile
+++ b/Gemfile
@@ -7,5 +7,6 @@ gem 'packable', ">= 1.3.5" # for Matlab IO
group :development do
gem 'pry'
gem 'rspec-longrun'
+ #gem 'narray', :path => "../narray"
#gem 'pry-debugger'
end
diff --git a/History.txt b/History.txt
index e36e8d3..5cba094 100644
--- a/History.txt
+++ b/History.txt
@@ -426,4 +426,98 @@
* nil values in matrices are now pretty printed as "nil"
* Casting from dense to Yale now properly accepts the default
- value option
+ value option
+
+=== 0.1.0.rc1 / 2014-12-28
+
+* 4 major enhancements:
+
+ * Improved garbage collection strategy for partial object creation
+ (i.e., when VALUEs are allocated but not registered right away),
+ which in addition to fixing numerous bugs should prevent some new
+ bugs from arising in the future (by @cjfuller)
+
+ * Implemented list storage transpose
+
+ * Implemented generic n-dimensional transpose
+
+ * Implemented == comparison between differing matrix stypes
+
+* 9 minor enhancements:
+
+ * User-friendly #gesvd and #gesdd updates (by @ryanmt)
+
+ * Added experimental #yale_row_key_intersection function for expert
+ recommendation problems
+
+ * Added additional *indgen shortcuts and changed behavior for some;
+ now, #cindgen for :complex64, #zindgen for :complex128, #findgen
+ for :float32, #dindgen for :float64, #rindgen for :rational128,
+ and #rbindgen for Ruby objects (which contain integers); also,
+ removed code repetition
+
+ * Changed #stddev to use elementwise #sqrt instead of a manual map
+ block (by @cjfuller)
+
+ * Added alias from MATLAB `load_mat` method to `load` for
+ consistency with the MatrixMarket loader
+
+ * Improved organization by moving list and yale code into storage/
+ subdirectories
+
+ * Added NMatrix#potrf! and NMatrix#getrf, which are instance methods
+ for calling CLAPACK functions (NMatrix#getrf! already existed)
+
+ * Added GCC installation instructions for Mac OS X Mavericks, and
+ updated the old installation instructions for Mac OS X (both
+ found in scripts/)
+
+ * Switched NMatrix::VERSION to work more like Rails::VERSION, with
+ support for MAJOR, MINOR, TINY, and PRE
+
+ * Added #concat, #hconcat, #vconcat, and #dconcat for joining
+ matrices together
+
+* 16 bug fixes:
+
+ * Spec revisions for lapack_gesdd and lapack_gesvd (by @ryanmt)
+
+ * Fixed two double-free problems (by @cjfuller and @mohawkjohn)
+
+ * Fixed contiguous array marking fencepost error
+
+ * Fixed C/C++ API compatibility problem preventing rb/gsl linking
+
+ * Resolved a number of compiler warnings, including one return-type
+ problem that would likely have become a garbage collection error
+ (if it wasn't already)
+
+ * Fixed -O3 optimization problems
+
+ * Restored NMatrix#asum, #nrm2, #binned_sorted_indices,
+ #sorted_indices which were inadvertantly removed by NVector
+ deprecation; have not tested
+
+ * Experimental #yale_nd_row and functions which call it now checks
+ range of argument to prevent segfault
+
+ * Fixed :* shortcut for a full list dimension (by @cjfuller)
+
+ * Fixed list construction problem which occurred when an initial
+ value array was provided (by @cjfuller)
+
+ * Fixed #inject issue with list and yale matrices of two dimensions
+ (by @cjfuller)
+
+ * Fixed several garbage collection problems (also listed under
+ enhancements) (by @cjfuller)
+
+ * Updated object cleaning target in extconf.rb
+
+ * Fixed possible compilation problem on Mavericks with Xcode 5.02
+
+ * Fixed errors involving undefined symbols, unresolved symbols, and
+ lazy symbol binding
+
+ * Improved LAPACK and BLAS header selection for Ubuntu/Debian
+ systems with ATLAS (by @mvz)
\ No newline at end of file
diff --git a/LICENSE.txt b/LICENSE.txt
index b697436..95ff794 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,4 +1,4 @@
-This version of SciRuby is licensed under the BSD 2-clause license.
+This version of NMatrix is licensed under the BSD 2-clause license.
* http://sciruby.com
* http://github.com/sciruby/sciruby/wiki/License
@@ -9,7 +9,7 @@ You *must* read the Contributor Agreement before contributing code to the SciRub
-----
-Copyright (c) 2010 - 2013, Ruby Science Foundation
+Copyright (c) 2010 - 2014, Ruby Science Foundation
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
diff --git a/README.rdoc b/README.rdoc
index e0b962c..03a527f 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -7,6 +7,8 @@ Fast Numerical Linear Algebra Library for Ruby
* {NMatrix Installation wiki}[https://github.com/SciRuby/nmatrix/wiki/Installation]
* {SciRuby Installation guide}[http://sciruby.com/docs#installation]
+{<img src=https://travis-ci.org/SciRuby/nmatrix.png>}[https://travis-ci.org/SciRuby/nmatrix]
+
== Description
NMatrix is a fast numerical linear algebra library for Ruby, with dense and sparse matrices, written mostly in C and
@@ -24,8 +26,7 @@ However, you will need to install {ATLAS}[http://math-atlas.sourceforge.net/] wi
{BLAS}[http://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms]) first. Detailed directions can be found
{here}[https://github.com/SciRuby/nmatrix/wiki/Installation]. The requirements for NMatrix are:
-* ATLAS
-* LAPACK, probably ({see here for details}[https://github.com/SciRuby/nmatrix/wiki/Installation])
+* ATLAS, preferably with CLAPACK ({see here for details}[https://github.com/SciRuby/nmatrix/wiki/Installation])
* a version of GCC or clang which supports C++0x or C++11
* Ruby 1.9.3+
* {packable}[http://github.com/marcandre/packable] 1.3.5 (used for I/O)
@@ -37,9 +38,11 @@ If you want to obtain the latest (development) code, you should generally do:
bundle install
bundle exec rake compile
bundle exec rake repackage
- gem install pkg/nmatrix-0.1.0.gem
+ gem install pkg/nmatrix-0.1.0-rc1.gem
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].
+We are currently working on Mavericks (Mac OS X) installation instructions, but in general, you'll need Homebrew and should
+probably use +brew install gcc48+ instead of using the install script.
== Documentation
@@ -52,21 +55,27 @@ You can find the complete API documentation {on our website}[http://sciruby.com/
== Examples
-Create a new NMatrix from a ruby array:
+Create a new NMatrix from a ruby Array:
>> require 'nmatrix'
- >> NMatrix.new([2, 3], [0, 1, 2, 3, 4, 5], dtype: :int64).pp
- [0, 1, 2]
- [3, 4, 5]
- => nil
+ >> NMatrix.new([2, 3], [0, 1, 2, 3, 4, 5], dtype: :int64)
+ => [
+ [0, 1, 2],
+ [3, 4, 5]
+ ]
Create a new NMatrix using the +N+ shortcut:
>> m = N[ [2, 3, 4], [7, 8, 9] ]
+ => [
+ [2, 3, 4],
+ [7, 8, 9]
+ ]
+ >> m.inspect
=> #<NMatrix:0x007f8e121b6cf8shape:[2,3] dtype:int32 stype:dense>
- >> m.pp
- [2, 3, 4]
- [7, 8, 9]
+
+The above output requires that you have a pretty-print-enabled console such as Pry; otherwise, you'll
+see the output given by +inspect+.
If you want to learn more about how to create a
matrix, {read the guide in our wiki}[https://github.com/SciRuby/nmatrix/wiki/How-to-create-a-NMatrix].
@@ -79,7 +88,7 @@ Read the instructions in +CONTRIBUTING.md+ if you want to help NMatrix.
== Features
-The following features exist in the current version of NMatrix (0.0.8):
+The following features exist in the current version of NMatrix (0.1.0.rc1):
* Matrix and vector storage containers: dense, yale, list (more to come)
* Data types: byte (uint8), int8, int16, int32, int64, float32, float64, complex64, complex128, rational64, rational128,
@@ -114,6 +123,8 @@ The following features exist in the current version of NMatrix (0.0.8):
* Determinant calculation for BLAS dtypes
* Vector 2-norms
* Ruby/GSL interoperability (requires {SciRuby's fork of rb-gsl}(http://github.com/SciRuby/rb-gsl))
+* slice assignments, e.g.,
+ x[1..3,0..4] = some_other_matrix
=== Planned Features (Short-to-Medium Term)
@@ -121,29 +132,16 @@ We are nearly the release of NMatrix 0.1.0, our first beta.
These are features planned for NMatrix 0.2.0:
-* slice assignments, e.g.,
- x[1..3,0..4] = some_other_matrix
* LAPACK-free calculation of determinant, trace, and eigenvalues (characteristic polynomial)
* LAPACK-free matrix inversions
* tensor products
-* principal component analysis (PCA)
* improved file I/O
* compression of yale symmetries in I/O
* optimization of non-BLAS data types on BLAS-like operations (e.g., matrix multiplication for rational numbers)
-=== Warning
-
-Please be aware that SciRuby and NMatrix are *alpha* status. If you're thinking of using SciRuby/NMatrix to write
-mission-critical code, such as for driving a car or flying a space shuttle, you may wish to choose other software for
-now.
-
-You should also be aware that NMatrix and NArray are incompatible with one another; you should not try to require both
-at the same time. Unfortunately, that causes problems with Ruby/GSL, which currently depends upon NArray. As such, we
-have a {fork of Ruby/GSL}[https://github.com/SciRuby/rb-gsl].
-
== License
-Copyright (c) 2010--13, The Ruby Science Foundation.
+Copyright (c) 2012--14, John Woods and the Ruby Science Foundation.
All rights reserved.
diff --git a/Rakefile b/Rakefile
index 90e40e9..6652048 100644
--- a/Rakefile
+++ b/Rakefile
@@ -41,18 +41,26 @@ BASEDIR = Pathname( __FILE__ ).dirname.relative_path_from( Pathname.pwd )
SPECDIR = BASEDIR + 'spec'
VALGRIND_OPTIONS = [
- "--tool=memcheck",
- #"--leak-check=yes",
- "--num-callers=15",
- #"--error-limit=no",
- "--partial-loads-ok=yes",
- "--undef-value-errors=no" #,
- #"--dsymutil=yes"
+ "--tool=memcheck",
+ #"--leak-check=yes",
+ "--num-callers=15",
+ #"--error-limit=no",
+ "--partial-loads-ok=yes",
+ "--undef-value-errors=no" #,
+ #"--dsymutil=yes"
]
+
+CALLGRIND_OPTIONS = [
+ "--tool=callgrind",
+ "--dump-instr=yes",
+ "--simulate-cache=yes",
+ "--collect-jumps=yes"
+]
+
VALGRIND_MEMORYFILL_OPTIONS = [
- "--freelist-vol=100000000",
- "--malloc-fill=6D",
- "--free-fill=66 ",
+ "--freelist-vol=100000000",
+ "--malloc-fill=6D",
+ "--free-fill=66 ",
]
GDB_OPTIONS = []
@@ -125,17 +133,25 @@ namespace :spec do
desc "Run specs under cgdb."
task :cgdb => [ :compile ] do |task|
cmd = [ 'cgdb' ] + GDB_OPTIONS
- cmd += [ '--args' ]
- cmd += RSPEC_CMD
- run( *cmd )
+ cmd += [ '--args' ]
+ cmd += RSPEC_CMD
+ run( *cmd )
end
desc "Run specs under Valgrind."
task :valgrind => [ :compile ] do |task|
- cmd = [ 'valgrind' ] + VALGRIND_OPTIONS
- cmd += RSPEC_CMD
- run( *cmd )
+ cmd = [ 'valgrind' ] + VALGRIND_OPTIONS
+ cmd += RSPEC_CMD
+ run( *cmd )
+ end
+
+ desc "Run specs under Callgrind."
+ task :callgrind => [ :compile ] do |task|
+ cmd = [ 'valgrind' ] + CALLGRIND_OPTIONS
+ cmd += RSPEC_CMD
+ run( *cmd )
end
+
end
diff --git a/checksums.yaml.gz b/checksums.yaml.gz
index d274cb6..9142bae 100644
Binary files a/checksums.yaml.gz and b/checksums.yaml.gz differ
diff --git a/ext/nmatrix/data/complex.h b/ext/nmatrix/data/complex.h
index 614d7bf..b463373 100644
--- a/ext/nmatrix/data/complex.h
+++ b/ext/nmatrix/data/complex.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/data/data.cpp b/ext/nmatrix/data/data.cpp
index 5497c16..22bfdd9 100644
--- a/ext/nmatrix/data/data.cpp
+++ b/ext/nmatrix/data/data.cpp
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -73,50 +73,24 @@ namespace nm {
"geq"
};
+ const std::string NONCOM_EWOP_NAMES[nm::NUM_NONCOM_EWOPS] = {
+ "atan2",
+ "ldexp",
+ "hypot"
+ };
- template <typename Type>
- Complex<Type>::Complex(const RubyObject& other) {
- switch(TYPE(other.rval)) {
- case T_COMPLEX:
- r = NUM2DBL(rb_funcall(other.rval, rb_intern("real"), 0));
- i = NUM2DBL(rb_funcall(other.rval, rb_intern("imag"), 0));
- break;
- case T_FLOAT:
- case T_RATIONAL:
- case T_FIXNUM:
- case T_BIGNUM:
- r = NUM2DBL(other.rval);
- i = 0.0;
- break;
- default:
- rb_raise(rb_eTypeError, "not sure how to convert this type of VALUE to a complex");
- }
- }
-
-
- template <typename Type>
- Rational<Type>::Rational(const RubyObject& other) {
- switch (TYPE(other.rval)) {
- case T_RATIONAL:
- n = NUM2LONG(rb_funcall(other.rval, rb_intern("numerator"), 0));
- d = NUM2LONG(rb_funcall(other.rval, rb_intern("denominator"), 0));
- break;
- case T_FIXNUM:
- case T_BIGNUM:
- n = NUM2LONG(other.rval);
- d = 1;
- break;
- case T_COMPLEX:
- case T_FLOAT:
- rb_raise(rb_eTypeError, "cannot convert float to a rational");
- break;
- default:
- rb_raise(rb_eTypeError, "not sure how to convert this type of VALUE to a rational");
- }
- }
-
+ const std::string UNARYOPS[nm::NUM_UNARYOPS] = {
+ "sin", "cos", "tan",
+ "asin", "acos", "atan",
+ "sinh", "cosh", "tanh",
+ "asinh", "acosh", "atanh",
+ "exp", "log2",
+ "log10", "sqrt", "erf",
+ "erfc", "cbrt", "gamma"
+ };
} // end of namespace nm
+
extern "C" {
const char* const DTYPE_NAMES[nm::NUM_DTYPES] = {
@@ -301,21 +275,23 @@ nm::RubyObject rubyobj_from_cval(void* val, nm::dtype_t dtype) {
*/
void* rubyobj_to_cval(VALUE val, nm::dtype_t dtype) {
size_t size = DTYPE_SIZES[dtype];
- void* ret_val = ALLOC_N(char, size);
+ NM_CONSERVATIVE(nm_register_value(val));
+ void* ret_val = NM_ALLOC_N(char, size);
rubyval_to_cval(val, dtype, ret_val);
-
+ NM_CONSERVATIVE(nm_unregister_value(val));
return ret_val;
}
void nm_init_data() {
- nm::RubyObject obj(INT2FIX(1));
- nm::Rational32 x(obj);
- nm::Rational64 y(obj);
- nm::Rational128 z(obj);
- nm::Complex64 a(obj);
- nm::Complex128 b(obj);
+ volatile VALUE t = INT2FIX(1);
+ volatile nm::RubyObject obj(t);
+ volatile nm::Rational32 x(const_cast<nm::RubyObject&>(obj));
+ volatile nm::Rational64 y(const_cast<nm::RubyObject&>(obj));
+ volatile nm::Rational128 z(const_cast<nm::RubyObject&>(obj));
+ volatile nm::Complex64 a(const_cast<nm::RubyObject&>(obj));
+ volatile nm::Complex128 b(const_cast<nm::RubyObject&>(obj));
}
diff --git a/ext/nmatrix/data/data.h b/ext/nmatrix/data/data.h
index ed13f36..9bc8ee1 100644
--- a/ext/nmatrix/data/data.h
+++ b/ext/nmatrix/data/data.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -55,7 +55,8 @@ namespace nm {
const int NUM_DTYPES = 13;
const int NUM_ITYPES = 4;
const int NUM_EWOPS = 12;
- const int NUM_NONCOMP_EWOPS = 6;
+ const int NUM_UNARYOPS = 21;
+ const int NUM_NONCOM_EWOPS = 3;
enum ewop_t {
EW_ADD,
@@ -69,12 +70,85 @@ namespace nm {
EW_LT,
EW_GT,
EW_LEQ,
- EW_GEQ
+ EW_GEQ,
+ };
+
+ enum noncom_ewop_t {
+ NONCOM_EW_ATAN2,
+ NONCOM_EW_LDEXP,
+ NONCOM_EW_HYPOT
+ };
+
+ enum unaryop_t {
+ UNARY_SIN,
+ UNARY_COS,
+ UNARY_TAN,
+ UNARY_ASIN,
+ UNARY_ACOS,
+ UNARY_ATAN,
+ UNARY_SINH,
+ UNARY_COSH,
+ UNARY_TANH,
+ UNARY_ASINH,
+ UNARY_ACOSH,
+ UNARY_ATANH,
+ UNARY_EXP,
+ UNARY_LOG2,
+ UNARY_LOG10,
+ UNARY_SQRT,
+ UNARY_ERF,
+ UNARY_ERFC,
+ UNARY_CBRT,
+ UNARY_GAMMA
};
// element-wise and scalar operators
extern const char* const EWOP_OPS[nm::NUM_EWOPS];
extern const std::string EWOP_NAMES[nm::NUM_EWOPS];
+ extern const std::string UNARYOPS[nm::NUM_UNARYOPS];
+ extern const std::string NONCOM_EWOP_NAMES[nm::NUM_NONCOM_EWOPS];
+
+
+ template <typename Type>
+ Complex<Type>::Complex(const RubyObject& other) {
+ switch(TYPE(other.rval)) {
+ case T_COMPLEX:
+ r = NUM2DBL(rb_funcall(other.rval, rb_intern("real"), 0));
+ i = NUM2DBL(rb_funcall(other.rval, rb_intern("imag"), 0));
+ break;
+ case T_FLOAT:
+ case T_RATIONAL:
+ case T_FIXNUM:
+ case T_BIGNUM:
+ r = NUM2DBL(other.rval);
+ i = 0.0;
+ break;
+ default:
+ rb_raise(rb_eTypeError, "not sure how to convert this type of VALUE to a complex");
+ }
+ }
+
+
+ template <typename Type>
+ Rational<Type>::Rational(const RubyObject& other) {
+ switch (TYPE(other.rval)) {
+ case T_RATIONAL:
+ n = NUM2LONG(rb_funcall(other.rval, rb_intern("numerator"), 0));
+ d = NUM2LONG(rb_funcall(other.rval, rb_intern("denominator"), 0));
+ break;
+ case T_FIXNUM:
+ case T_BIGNUM:
+ n = NUM2LONG(other.rval);
+ d = 1;
+ break;
+ case T_COMPLEX:
+ case T_FLOAT:
+ rb_raise(rb_eTypeError, "cannot convert float to a rational");
+ break;
+ default:
+ rb_raise(rb_eTypeError, "not sure how to convert this type of VALUE to a rational");
+ }
+ }
} // end of namespace nm
@@ -90,6 +164,20 @@ namespace nm {
nm_yale_storage_mark \
};
+#define STYPE_REGISTER_TABLE(name) \
+ static void (*(name)[nm::NUM_STYPES])(const STORAGE*) = { \
+ nm_dense_storage_register, \
+ nm_list_storage_register, \
+ nm_yale_storage_register \
+ };
+
+#define STYPE_UNREGISTER_TABLE(name) \
+ static void (*(name)[nm::NUM_STYPES])(const STORAGE*) = { \
+ nm_dense_storage_unregister, \
+ nm_list_storage_unregister, \
+ nm_yale_storage_unregister \
+ };
+
#define CAST_TABLE(name) \
static STORAGE* (*(name)[nm::NUM_STYPES][nm::NUM_STYPES])(const STORAGE*, nm::dtype_t, void*) = { \
{ nm_dense_storage_cast_copy, nm_dense_storage_from_list, nm_dense_storage_from_yale }, \
diff --git a/ext/nmatrix/data/meta.h b/ext/nmatrix/data/meta.h
index b13082a..4260ae1 100644
--- a/ext/nmatrix/data/meta.h
+++ b/ext/nmatrix/data/meta.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/data/rational.h b/ext/nmatrix/data/rational.h
index 3615523..1a1e74a 100644
--- a/ext/nmatrix/data/rational.h
+++ b/ext/nmatrix/data/rational.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/data/ruby_object.h b/ext/nmatrix/data/ruby_object.h
index d26ba86..9632751 100644
--- a/ext/nmatrix/data/ruby_object.h
+++ b/ext/nmatrix/data/ruby_object.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/extconf.rb b/ext/nmatrix/extconf.rb
index 98c4f8c..0489fdf 100644
--- a/ext/nmatrix/extconf.rb
+++ b/ext/nmatrix/extconf.rb
@@ -8,8 +8,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-# NMatrix is Copyright (c) 2013, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -102,22 +102,65 @@ if /cygwin|mingw/ =~ RUBY_PLATFORM
end
$DEBUG = true
-$CFLAGS = ["-Wall ",$CFLAGS].join(" ")
-
-$srcs = [
- 'nmatrix.cpp',
- 'ruby_constants.cpp',
-
- 'data/data.cpp',
- 'math.cpp',
- 'util/sl_list.cpp',
- 'util/io.cpp',
- 'storage/common.cpp',
- 'storage/storage.cpp',
- 'storage/dense.cpp',
- 'storage/yale/yale.cpp',
- 'storage/list.cpp'
- ]
+$CFLAGS = ["-Wall -Werror=return-type",$CFLAGS].join(" ")
+$CXXFLAGS = ["-Wall -Werror=return-type",$CXXFLAGS].join(" ")
+$CPPFLAGS = ["-Wall -Werror=return-type",$CPPFLAGS].join(" ")
+
+# When adding objects here, make sure their directories are included in CLEANOBJS down at the bottom of extconf.rb.
+basenames = %w{nmatrix ruby_constants data/data util/io math util/sl_list storage/common storage/storage storage/dense/dense storage/yale/yale storage/list/list}
+$objs = basenames.map { |b| "#{b}.o" }
+$srcs = basenames.map { |b| "#{b}.cpp" }
+
+#CONFIG['CXX'] = 'clang++'
+CONFIG['CXX'] = 'g++'
+
+def find_newer_gplusplus #:nodoc:
+ print "checking for apparent GNU g++ binary with C++0x/C++11 support... "
+ [9,8,7,6,5,4,3].each do |minor|
+ ver = "4.#{minor}"
+ gpp = "g++-#{ver}"
+ result = `which #{gpp}`
+ next if result.empty?
+ CONFIG['CXX'] = gpp
+ puts ver
+ return CONFIG['CXX']
+ end
+ false
+end
+
+def gplusplus_version #:nodoc:
+ cxxvar = proc { |n| `#{CONFIG['CXX']} -E -dM - </dev/null | grep #{n}`.chomp.split(' ')[2] }
+ major = cxxvar.call('__GNUC__')
+ minor = cxxvar.call('__GNUC_MINOR__')
+ patch = cxxvar.call('__GNUC_PATCHLEVEL__')
+
+ raise("unable to determine g++ version (match to get version was nil)") if major.nil? || minor.nil? || patch.nil?
+
+ "#{major}.#{minor}.#{patch}"
+end
+
+
+if CONFIG['CXX'] == 'clang++'
+ $CPP_STANDARD = 'c++11'
+
+else
+ version = gplusplus_version
+ if version < '4.3.0' && CONFIG['CXX'] == 'g++' # see if we can find a newer G++, unless it's been overridden by user
+ if !find_newer_gplusplus
+ raise("You need a version of g++ which supports -std=c++0x or -std=c++11. If you're on a Mac and using Homebrew, we recommend using mac-brew-gcc.sh to install a more recent g++.")
+ end
+ version = gplusplus_version
+ end
+
+ if version < '4.7.0'
+ $CPP_STANDARD = 'c++0x'
+ else
+ $CPP_STANDARD = 'c++11'
+ end
+ puts "using C++ standard... #{$CPP_STANDARD}"
+ puts "g++ reports version... " + `#{CONFIG['CXX']} --version|head -n 1|cut -f 3 -d " "`
+end
+
# add smmp in to get generic transp; remove smmp2 to eliminate funcptr transp
# The next line allows the user to supply --with-atlas-dir=/usr/local/atlas,
@@ -138,9 +181,16 @@ idefaults = {lapack: ["/usr/include/atlas"],
cblas: ["/usr/local/atlas/include", "/usr/include/atlas"],
atlas: ["/usr/local/atlas/include", "/usr/include/atlas"]}
-ldefaults = {lapack: ["/usr/local/lib", "/usr/local/atlas/lib"],
- cblas: ["/usr/local/lib", "/usr/local/atlas/lib"],
- atlas: ["/usr/local/atlas/lib", "/usr/local/lib", "/usr/lib"]}
+# For some reason, if we try to look for /usr/lib64/atlas on a Mac OS X Mavericks system, and the directory does not
+# exist, it will give a linker error -- even if the lib dir is already correctly included with -L. So we need to check
+# that Dir.exists?(d) for each.
+ldefaults = {lapack: ["/usr/local/lib", "/usr/local/atlas/lib", "/usr/lib64/atlas"].delete_if { |d| !Dir.exists?(d) },
+ cblas: ["/usr/local/lib", "/usr/local/atlas/lib", "/usr/lib64/atlas"].delete_if { |d| !Dir.exists?(d) },
+ atlas: ["/usr/local/lib", "/usr/local/atlas/lib", "/usr/lib", "/usr/lib64/atlas"].delete_if { |d| !Dir.exists?(d) }}
+
+if have_library("clapack") # Usually only applies for Mac OS X
+ $libs += " -lclapack "
+end
unless have_library("lapack")
dir_config("lapack", idefaults[:lapack], ldefaults[:lapack])
@@ -154,22 +204,19 @@ unless have_library("atlas")
dir_config("atlas", idefaults[:atlas], ldefaults[:atlas])
end
-# this needs to go before cblas.h checks -- on Ubuntu, the clapack in the
-# include path found for cblas.h doesn't seem to contain all the necessary
-# functions
-have_header("clapack.h")
-
-# this ensures that we find the header on Ubuntu, where by default the library
-# can be found but not the header
-unless have_header("cblas.h")
- find_header("cblas.h", *idefaults[:cblas])
+# If BLAS and LAPACK headers are in an atlas directory, prefer those. Otherwise,
+# we try our luck with the default location.
+if have_header("atlas/cblas.h")
+ have_header("atlas/clapack.h")
+else
+ have_header("cblas.h")
+ have_header("clapack.h")
end
-have_header("cblas.h")
have_func("clapack_dgetrf", ["cblas.h", "clapack.h"])
have_func("clapack_dgetri", ["cblas.h", "clapack.h"])
-have_func("dgesvd_", "clapack.h")
+have_func("dgesvd_", "clapack.h") # This may not do anything. dgesvd_ seems to be in LAPACK, not CLAPACK.
have_func("cblas_dgemm", "cblas.h")
@@ -178,59 +225,15 @@ have_func("cblas_dgemm", "cblas.h")
#find_library("lapack", "clapack_dgetrf")
#find_library("cblas", "cblas_dgemm")
#find_library("atlas", "ATL_dgemmNN")
-
# Order matters here: ATLAS has to go after LAPACK: http://mail.scipy.org/pipermail/scipy-user/2007-January/010717.html
$libs += " -llapack -lcblas -latlas "
+#$libs += " -lprofiler "
-$objs = %w{nmatrix ruby_constants data/data util/io math util/sl_list storage/common storage/storage storage/dense storage/yale/yale storage/list}.map { |i| i + ".o" }
-
-#CONFIG['CXX'] = 'clang++'
-CONFIG['CXX'] = 'g++'
-
-def find_newer_gplusplus #:nodoc:
- print "checking for apparent GNU g++ binary with C++0x/C++11 support... "
- [9,8,7,6,5,4,3].each do |minor|
- ver = "4.#{minor}"
- gpp = "g++-#{ver}"
- result = `which #{gpp}`
- next if result.empty?
- CONFIG['CXX'] = gpp
- puts ver
- return CONFIG['CXX']
- end
- false
-end
-
-def gplusplus_version #:nodoc:
- `LANG="en_US" #{CONFIG['CXX']} -v 2>&1`.lines.to_a.last.match(/gcc\sversion\s(\d\.\d.\d)/).captures.first
-end
-
-
-if CONFIG['CXX'] == 'clang++'
- $CPP_STANDARD = 'c++11'
-
-else
- version = gplusplus_version
- if version < '4.3.0' && CONFIG['CXX'] == 'g++' # see if we can find a newer G++, unless it's been overridden by user
- if !find_newer_gplusplus
- raise("You need a version of g++ which supports -std=c++0x or -std=c++11. If you're on a Mac and using Homebrew, we recommend using mac-brew-gcc.sh to install a more recent g++.")
- end
- version = gplusplus_version
- end
-
- if version < '4.7.0'
- $CPP_STANDARD = 'c++0x'
- else
- $CPP_STANDARD = 'c++11'
- end
- puts "using C++ standard... #{$CPP_STANDARD}"
- puts "g++ reports version... " + `#{CONFIG['CXX']} --version|head -n 1|cut -f 3 -d " "`
-end
# For release, these next two should both be changed to -O3.
-$CFLAGS += " -O3 " #" -O0 -g "
+$CFLAGS += " -O3 -g" #" -O0 -g "
#$CFLAGS += " -static -O0 -g "
-$CPPFLAGS += " -O3 -std=#{$CPP_STANDARD} " #" -O0 -g -std=#{$CPP_STANDARD} " #-fmax-errors=10 -save-temps
+$CPPFLAGS += " -O3 -std=#{$CPP_STANDARD} -g" #" -O0 -g -std=#{$CPP_STANDARD} " #-fmax-errors=10 -save-temps
#$CPPFLAGS += " -static -O0 -g -std=#{$CPP_STANDARD} "
CONFIG['warnflags'].gsub!('-Wshorten-64-to-32', '') # doesn't work except in Mac-patched gcc (4.2)
@@ -244,15 +247,13 @@ Dir.mkdir("data") unless Dir.exists?("data")
Dir.mkdir("util") unless Dir.exists?("util")
Dir.mkdir("storage") unless Dir.exists?("storage")
Dir.chdir("storage") do
- Dir.mkdir("yale") unless Dir.exists?("yale")
- Dir.chdir("yale") do
- Dir.mkdir("iterators") unless Dir.exists?("iterators")
- end
+ Dir.mkdir("yale") unless Dir.exists?("yale")
+ Dir.mkdir("list") unless Dir.exists?("list")
+ Dir.mkdir("dense") unless Dir.exists?("dense")
end
# to clean up object files in subdirectories:
open('Makefile', 'a') do |f|
- f.write <<EOS
-CLEANOBJS := $(CLEANOBJS) data/*.#{CONFIG["OBJEXT"]} storage/*.#{CONFIG["OBJEXT"]} util/*.#{CONFIG["OBJEXT"]}
-EOS
+ clean_objs_paths = %w{data storage storage/dense storage/yale storage/list util}.map { |d| "#{d}/*.#{CONFIG["OBJEXT"]}" }
+ f.write("CLEANOBJS := $(CLEANOBJS) #{clean_objs_paths.join(' ')}")
end
diff --git a/ext/nmatrix/math.cpp b/ext/nmatrix/math.cpp
index e838a7f..6c371e3 100644
--- a/ext/nmatrix/math.cpp
+++ b/ext/nmatrix/math.cpp
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -140,7 +140,7 @@
#include "math/rot.h"
#include "math/rotg.h"
#include "math/math.h"
-#include "storage/dense.h"
+#include "storage/dense/dense.h"
#include "nmatrix.h"
#include "ruby_constants.h"
@@ -150,8 +150,10 @@
*/
extern "C" {
-#ifdef HAVE_CLAPACK_H
+#if defined HAVE_CLAPACK_H
#include <clapack.h>
+#elif defined HAVE_ATLAS_CLAPACK_H
+ #include <atlas/clapack.h>
#endif
static VALUE nm_cblas_nrm2(VALUE self, VALUE n, VALUE x, VALUE incx);
@@ -503,8 +505,10 @@ static VALUE nm_cblas_rotg(VALUE self, VALUE ab) {
return Qnil;
} else {
- void *pC = ALLOCA_N(char, DTYPE_SIZES[dtype]),
- *pS = ALLOCA_N(char, DTYPE_SIZES[dtype]);
+ NM_CONSERVATIVE(nm_register_value(self));
+ NM_CONSERVATIVE(nm_register_value(ab));
+ void *pC = NM_ALLOCA_N(char, DTYPE_SIZES[dtype]),
+ *pS = NM_ALLOCA_N(char, DTYPE_SIZES[dtype]);
// extract A and B from the NVector (first two elements)
void* pA = NM_STORAGE_DENSE(ab)->elements;
@@ -522,7 +526,8 @@ static VALUE nm_cblas_rotg(VALUE self, VALUE ab) {
rb_ary_store(result, 0, rubyobj_from_cval(pC, dtype).rval);
rb_ary_store(result, 1, rubyobj_from_cval(pS, dtype).rval);
}
-
+ NM_CONSERVATIVE(nm_unregister_value(ab));
+ NM_CONSERVATIVE(nm_unregister_value(self));
return result;
}
}
@@ -575,18 +580,18 @@ static VALUE nm_cblas_rot(VALUE self, VALUE n, VALUE x, VALUE incx, VALUE y, VAL
// We need to ensure the cosine and sine arguments are the correct dtype -- which may differ from the actual dtype.
if (dtype == nm::COMPLEX64) {
- pC = ALLOCA_N(float,1);
- pS = ALLOCA_N(float,1);
+ pC = NM_ALLOCA_N(float,1);
+ pS = NM_ALLOCA_N(float,1);
rubyval_to_cval(c, nm::FLOAT32, pC);
rubyval_to_cval(s, nm::FLOAT32, pS);
} else if (dtype == nm::COMPLEX128) {
- pC = ALLOCA_N(double,1);
- pS = ALLOCA_N(double,1);
+ pC = NM_ALLOCA_N(double,1);
+ pS = NM_ALLOCA_N(double,1);
rubyval_to_cval(c, nm::FLOAT64, pC);
rubyval_to_cval(s, nm::FLOAT64, pS);
} else {
- pC = ALLOCA_N(char, DTYPE_SIZES[dtype]);
- pS = ALLOCA_N(char, DTYPE_SIZES[dtype]);
+ pC = NM_ALLOCA_N(char, DTYPE_SIZES[dtype]);
+ pS = NM_ALLOCA_N(char, DTYPE_SIZES[dtype]);
rubyval_to_cval(c, dtype, pC);
rubyval_to_cval(s, dtype, pS);
}
@@ -646,7 +651,7 @@ static VALUE nm_cblas_nrm2(VALUE self, VALUE n, VALUE x, VALUE incx) {
if (dtype == nm::COMPLEX64) rdtype = nm::FLOAT32;
else if (dtype == nm::COMPLEX128) rdtype = nm::FLOAT64;
- void *Result = ALLOCA_N(char, DTYPE_SIZES[rdtype]);
+ void *Result = NM_ALLOCA_N(char, DTYPE_SIZES[rdtype]);
ttable[dtype](FIX2INT(n), NM_STORAGE_DENSE(x)->elements, FIX2INT(incx), Result);
@@ -698,7 +703,7 @@ static VALUE nm_cblas_asum(VALUE self, VALUE n, VALUE x, VALUE incx) {
if (dtype == nm::COMPLEX64) rdtype = nm::FLOAT32;
else if (dtype == nm::COMPLEX128) rdtype = nm::FLOAT64;
- void *Result = ALLOCA_N(char, DTYPE_SIZES[rdtype]);
+ void *Result = NM_ALLOCA_N(char, DTYPE_SIZES[rdtype]);
ttable[dtype](FIX2INT(n), NM_STORAGE_DENSE(x)->elements, FIX2INT(incx), Result);
@@ -743,8 +748,8 @@ static VALUE nm_cblas_gemm(VALUE self,
nm::dtype_t dtype = NM_DTYPE(a);
- void *pAlpha = ALLOCA_N(char, DTYPE_SIZES[dtype]),
- *pBeta = ALLOCA_N(char, DTYPE_SIZES[dtype]);
+ void *pAlpha = NM_ALLOCA_N(char, DTYPE_SIZES[dtype]),
+ *pBeta = NM_ALLOCA_N(char, DTYPE_SIZES[dtype]);
rubyval_to_cval(alpha, dtype, pAlpha);
rubyval_to_cval(beta, dtype, pBeta);
@@ -788,8 +793,8 @@ static VALUE nm_cblas_gemv(VALUE self,
nm::dtype_t dtype = NM_DTYPE(a);
- void *pAlpha = ALLOCA_N(char, DTYPE_SIZES[dtype]),
- *pBeta = ALLOCA_N(char, DTYPE_SIZES[dtype]);
+ void *pAlpha = NM_ALLOCA_N(char, DTYPE_SIZES[dtype]),
+ *pBeta = NM_ALLOCA_N(char, DTYPE_SIZES[dtype]);
rubyval_to_cval(alpha, dtype, pAlpha);
rubyval_to_cval(beta, dtype, pBeta);
@@ -825,7 +830,7 @@ static VALUE nm_cblas_trsm(VALUE self,
if (!ttable[dtype]) {
rb_raise(nm_eDataTypeError, "this matrix operation undefined for integer matrices");
} else {
- void *pAlpha = ALLOCA_N(char, DTYPE_SIZES[dtype]);
+ void *pAlpha = NM_ALLOCA_N(char, DTYPE_SIZES[dtype]);
rubyval_to_cval(alpha, dtype, pAlpha);
ttable[dtype](blas_order_sym(order), blas_side_sym(side), blas_uplo_sym(uplo), blas_transpose_sym(trans_a), blas_diag_sym(diag), FIX2INT(m), FIX2INT(n), pAlpha, NM_STORAGE_DENSE(a)->elements, FIX2INT(lda), NM_STORAGE_DENSE(b)->elements, FIX2INT(ldb));
@@ -865,7 +870,7 @@ static VALUE nm_cblas_trmm(VALUE self,
if (!ttable[dtype]) {
rb_raise(nm_eDataTypeError, "this matrix operation not yet defined for non-BLAS dtypes");
} else {
- void *pAlpha = ALLOCA_N(char, DTYPE_SIZES[dtype]);
+ void *pAlpha = NM_ALLOCA_N(char, DTYPE_SIZES[dtype]);
rubyval_to_cval(alpha, dtype, pAlpha);
ttable[dtype](blas_order_sym(order), blas_side_sym(side), blas_uplo_sym(uplo), blas_transpose_sym(trans_a), blas_diag_sym(diag), FIX2INT(m), FIX2INT(n), pAlpha, NM_STORAGE_DENSE(a)->elements, FIX2INT(lda), NM_STORAGE_DENSE(b)->elements, FIX2INT(ldb));
@@ -903,8 +908,8 @@ static VALUE nm_cblas_syrk(VALUE self,
if (!ttable[dtype]) {
rb_raise(nm_eDataTypeError, "this matrix operation undefined for integer matrices");
} else {
- void *pAlpha = ALLOCA_N(char, DTYPE_SIZES[dtype]),
- *pBeta = ALLOCA_N(char, DTYPE_SIZES[dtype]);
+ void *pAlpha = NM_ALLOCA_N(char, DTYPE_SIZES[dtype]),
+ *pBeta = NM_ALLOCA_N(char, DTYPE_SIZES[dtype]);
rubyval_to_cval(alpha, dtype, pAlpha);
rubyval_to_cval(beta, dtype, pBeta);
@@ -984,12 +989,12 @@ static VALUE nm_lapack_gesvd(VALUE self, VALUE jobu, VALUE jobvt, VALUE m, VALUE
// only need rwork for complex matrices
int rwork_size = (dtype == nm::COMPLEX64 || dtype == nm::COMPLEX128) ? 5 * min_mn : 0;
- void* rwork = rwork_size > 0 ? ALLOCA_N(char, DTYPE_SIZES[dtype] * rwork_size) : NULL;
+ void* rwork = rwork_size > 0 ? NM_ALLOCA_N(char, DTYPE_SIZES[dtype] * rwork_size) : NULL;
int work_size = FIX2INT(lwork);
// ignore user argument for lwork if it's too small.
work_size = NM_MAX((dtype == nm::COMPLEX64 || dtype == nm::COMPLEX128 ? 2 * min_mn + max_mn : NM_MAX(3*min_mn + max_mn, 5*min_mn)), work_size);
- void* work = ALLOCA_N(char, DTYPE_SIZES[dtype] * work_size);
+ void* work = NM_ALLOCA_N(char, DTYPE_SIZES[dtype] * work_size);
int info = gesvd_table[dtype](JOBU, JOBVT, M, N, NM_STORAGE_DENSE(a)->elements, FIX2INT(lda),
NM_STORAGE_DENSE(s)->elements, NM_STORAGE_DENSE(u)->elements, FIX2INT(ldu), NM_STORAGE_DENSE(vt)->elements, FIX2INT(ldvt),
@@ -1046,7 +1051,7 @@ static VALUE nm_lapack_gesdd(VALUE self, VALUE jobz, VALUE m, VALUE n, VALUE a,
int work_size = FIX2INT(lwork); // Make sure we allocate enough work, regardless of the user request.
if (dtype == nm::COMPLEX64 || dtype == nm::COMPLEX128) {
int rwork_size = min_mn * (JOBZ == 'N' ? 5 : NM_MAX(5*min_mn + 7, 2*max_mn + 2*min_mn + 1));
- rwork = ALLOCA_N(char, DTYPE_SIZES[dtype] * rwork_size);
+ rwork = NM_ALLOCA_N(char, DTYPE_SIZES[dtype] * rwork_size);
if (JOBZ == 'N') work_size = NM_MAX(work_size, 3*min_mn + NM_MAX(max_mn, 6*min_mn));
else if (JOBZ == 'O') work_size = NM_MAX(work_size, 3*min_mn*min_mn + NM_MAX(max_mn, 5*min_mn*min_mn + 4*min_mn));
@@ -1056,8 +1061,8 @@ static VALUE nm_lapack_gesdd(VALUE self, VALUE jobz, VALUE m, VALUE n, VALUE a,
else if (JOBZ == 'O') work_size = NM_MAX(work_size, 2*min_mn*min_mn + max_mn + 2*min_mn);
else work_size = NM_MAX(work_size, min_mn*min_mn + max_mn + 2*min_mn);
}
- void* work = ALLOCA_N(char, DTYPE_SIZES[dtype] * work_size);
- int* iwork = ALLOCA_N(int, 8*min_mn);
+ void* work = NM_ALLOCA_N(char, DTYPE_SIZES[dtype] * work_size);
+ int* iwork = NM_ALLOCA_N(int, 8*min_mn);
int info = gesdd_table[dtype](JOBZ, M, N, NM_STORAGE_DENSE(a)->elements, FIX2INT(lda),
NM_STORAGE_DENSE(s)->elements, NM_STORAGE_DENSE(u)->elements, FIX2INT(ldu), NM_STORAGE_DENSE(vt)->elements, FIX2INT(ldvt),
@@ -1114,7 +1119,7 @@ static VALUE nm_lapack_geev(VALUE self, VALUE compute_left, VALUE compute_right,
// only need rwork for complex matrices (wi == Qnil for complex)
int rwork_size = dtype == nm::COMPLEX64 || dtype == nm::COMPLEX128 ? N * DTYPE_SIZES[dtype] : 0; // 2*N*floattype for complex only, otherwise 0
- void* rwork = rwork_size > 0 ? ALLOCA_N(char, rwork_size) : NULL;
+ void* rwork = rwork_size > 0 ? NM_ALLOCA_N(char, rwork_size) : NULL;
int work_size = FIX2INT(lwork);
void* work;
@@ -1123,11 +1128,11 @@ static VALUE nm_lapack_geev(VALUE self, VALUE compute_left, VALUE compute_right,
// if work size is 0 or -1, query.
if (work_size <= 0) {
work_size = -1;
- work = ALLOC_N(char, DTYPE_SIZES[dtype]); //2*N * DTYPE_SIZES[dtype]);
+ work = NM_ALLOC_N(char, DTYPE_SIZES[dtype]); //2*N * DTYPE_SIZES[dtype]);
info = geev_table[dtype](JOBVL, JOBVR, N, A, FIX2INT(lda), WR, WI, VL, FIX2INT(ldvl), VR, FIX2INT(ldvr), work, work_size, rwork);
work_size = (int)(dtype == nm::COMPLEX64 || dtype == nm::FLOAT32 ? reinterpret_cast<float*>(work)[0] : reinterpret_cast<double*>(work)[0]);
// line above is basically: work_size = (int)(work[0]); // now have new work_size
- xfree(work);
+ NM_FREE(work);
if (info == 0)
rb_warn("geev: calculated optimal lwork of %d; to eliminate this message, use a positive value for lwork (at least 2*shape[i])", work_size);
else return INT2FIX(info); // error of some kind on query!
@@ -1140,7 +1145,7 @@ static VALUE nm_lapack_geev(VALUE self, VALUE compute_left, VALUE compute_right,
}
// Allocate work array for actual run
- work = ALLOCA_N(char, work_size * DTYPE_SIZES[dtype]);
+ work = NM_ALLOCA_N(char, work_size * DTYPE_SIZES[dtype]);
// Perform the actual calculation.
info = geev_table[dtype](JOBVL, JOBVR, N, A, FIX2INT(lda), WR, WI, VL, FIX2INT(ldvl), VR, FIX2INT(ldvr), work, work_size, rwork);
@@ -1158,7 +1163,7 @@ static VALUE nm_lapack_geev(VALUE self, VALUE compute_left, VALUE compute_right,
static VALUE nm_clapack_scal(VALUE self, VALUE n, VALUE scale, VALUE vector, VALUE incx) {
nm::dtype_t dtype = NM_DTYPE(vector);
- void* da = ALLOCA_N(char, DTYPE_SIZES[dtype]);
+ void* da = NM_ALLOCA_N(char, DTYPE_SIZES[dtype]);
rubyval_to_cval(scale, dtype, da);
NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::math::clapack_scal, void, const int n, const void* da, void* dx, const int incx);
@@ -1251,7 +1256,7 @@ static VALUE nm_clapack_getrf(VALUE self, VALUE order, VALUE m, VALUE n, VALUE a
// Allocate the pivot index array, which is of size MIN(M, N).
size_t ipiv_size = std::min(M,N);
- int* ipiv = ALLOCA_N(int, ipiv_size);
+ int* ipiv = NM_ALLOCA_N(int, ipiv_size);
if (!ttable[NM_DTYPE(a)]) {
rb_raise(nm_eDataTypeError, "this matrix operation undefined for integer matrices");
@@ -1282,7 +1287,7 @@ static VALUE nm_clapack_getrf(VALUE self, VALUE order, VALUE m, VALUE n, VALUE a
*/
static VALUE nm_clapack_potrf(VALUE self, VALUE order, VALUE uplo, VALUE n, VALUE a, VALUE lda) {
#ifndef HAVE_CLAPACK_H
- rb_raise(rb_eNotImpError, "potrf currently requires LAPACK");
+ rb_raise(rb_eNotImpError, "potrf currently requires CLAPACK");
#endif
static int (*ttable[nm::NUM_DTYPES])(const enum CBLAS_ORDER, const enum CBLAS_UPLO, const int n, void* a, const int lda) = {
@@ -1343,7 +1348,7 @@ static VALUE nm_clapack_getrs(VALUE self, VALUE order, VALUE trans, VALUE n, VAL
if (TYPE(ipiv) != T_ARRAY) {
rb_raise(rb_eArgError, "ipiv must be of type Array");
} else {
- ipiv_ = ALLOCA_N(int, RARRAY_LEN(ipiv));
+ ipiv_ = NM_ALLOCA_N(int, RARRAY_LEN(ipiv));
for (int index = 0; index < RARRAY_LEN(ipiv); ++index) {
ipiv_[index] = FIX2INT( RARRAY_PTR(ipiv)[index] );
}
@@ -1411,7 +1416,7 @@ static VALUE nm_clapack_potrs(VALUE self, VALUE order, VALUE uplo, VALUE n, VALU
*/
static VALUE nm_clapack_getri(VALUE self, VALUE order, VALUE n, VALUE a, VALUE lda, VALUE ipiv) {
#ifndef HAVE_CLAPACK_H
- rb_raise(rb_eNotImpError, "getri currently requires LAPACK");
+ rb_raise(rb_eNotImpError, "getri currently requires CLAPACK");
#endif
static int (*ttable[nm::NUM_DTYPES])(const enum CBLAS_ORDER, const int n, void* a, const int lda, const int* ipiv) = {
@@ -1437,7 +1442,7 @@ static VALUE nm_clapack_getri(VALUE self, VALUE order, VALUE n, VALUE a, VALUE l
if (TYPE(ipiv) != T_ARRAY) {
rb_raise(rb_eArgError, "ipiv must be of type Array");
} else {
- ipiv_ = ALLOCA_N(int, RARRAY_LEN(ipiv));
+ ipiv_ = NM_ALLOCA_N(int, RARRAY_LEN(ipiv));
for (int index = 0; index < RARRAY_LEN(ipiv); ++index) {
ipiv_[index] = FIX2INT( RARRAY_PTR(ipiv)[index] );
}
@@ -1468,7 +1473,7 @@ static VALUE nm_clapack_getri(VALUE self, VALUE order, VALUE n, VALUE a, VALUE l
*/
static VALUE nm_clapack_potri(VALUE self, VALUE order, VALUE uplo, VALUE n, VALUE a, VALUE lda) {
#ifndef HAVE_CLAPACK_H
- rb_raise(rb_eNotImpError, "getri currently requires LAPACK");
+ rb_raise(rb_eNotImpError, "getri currently requires CLAPACK");
#endif
static int (*ttable[nm::NUM_DTYPES])(const enum CBLAS_ORDER, const enum CBLAS_UPLO, const int n, void* a, const int lda) = {
@@ -1534,7 +1539,7 @@ static VALUE nm_clapack_laswp(VALUE self, VALUE n, VALUE a, VALUE lda, VALUE k1,
if (TYPE(ipiv) != T_ARRAY) {
rb_raise(rb_eArgError, "ipiv must be of type Array");
} else {
- ipiv_ = ALLOCA_N(int, RARRAY_LEN(ipiv));
+ ipiv_ = NM_ALLOCA_N(int, RARRAY_LEN(ipiv));
for (int index = 0; index < RARRAY_LEN(ipiv); ++index) {
ipiv_[index] = FIX2INT( RARRAY_PTR(ipiv)[index] );
}
diff --git a/ext/nmatrix/math/asum.h b/ext/nmatrix/math/asum.h
index 3b4e95e..3a77a8b 100644
--- a/ext/nmatrix/math/asum.h
+++ b/ext/nmatrix/math/asum.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -86,7 +86,7 @@ inline ReturnDType asum(const int N, const DType* X, const int incX) {
}
-#ifdef HAVE_CBLAS_H
+#if defined HAVE_CBLAS_H || defined HAVE_ATLAS_CBLAS_H
template <>
inline float asum(const int N, const float* X, const int incX) {
return cblas_sasum(N, X, incX);
diff --git a/ext/nmatrix/math/geev.h b/ext/nmatrix/math/geev.h
index 130a24a..3c89e10 100644
--- a/ext/nmatrix/math/geev.h
+++ b/ext/nmatrix/math/geev.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/math/gemm.h b/ext/nmatrix/math/gemm.h
index 0520b90..b26961e 100644
--- a/ext/nmatrix/math/gemm.h
+++ b/ext/nmatrix/math/gemm.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -31,7 +31,11 @@
# define GEMM_H
extern "C" { // These need to be in an extern "C" block or you'll get all kinds of undefined symbol errors.
+#if defined HAVE_CBLAS_H
#include <cblas.h>
+#elif defined HAVE_ATLAS_CBLAS_H
+ #include <atlas/cblas.h>
+#endif
}
diff --git a/ext/nmatrix/math/gemv.h b/ext/nmatrix/math/gemv.h
index 158952c..e24a45c 100644
--- a/ext/nmatrix/math/gemv.h
+++ b/ext/nmatrix/math/gemv.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -31,7 +31,11 @@
# define GEMV_H
extern "C" { // These need to be in an extern "C" block or you'll get all kinds of undefined symbol errors.
+#if defined HAVE_CBLAS_H
#include <cblas.h>
+#elif defined HAVE_ATLAS_CBLAS_H
+ #include <atlas/cblas.h>
+#endif
}
diff --git a/ext/nmatrix/math/ger.h b/ext/nmatrix/math/ger.h
index a769e91..9e6a18f 100644
--- a/ext/nmatrix/math/ger.h
+++ b/ext/nmatrix/math/ger.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/math/gesdd.h b/ext/nmatrix/math/gesdd.h
index 25fafe2..046701d 100644
--- a/ext/nmatrix/math/gesdd.h
+++ b/ext/nmatrix/math/gesdd.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/math/gesvd.h b/ext/nmatrix/math/gesvd.h
index 65af306..181df08 100644
--- a/ext/nmatrix/math/gesvd.h
+++ b/ext/nmatrix/math/gesvd.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/math/getf2.h b/ext/nmatrix/math/getf2.h
index f8a3728..98240fe 100644
--- a/ext/nmatrix/math/getf2.h
+++ b/ext/nmatrix/math/getf2.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/math/getrf.h b/ext/nmatrix/math/getrf.h
index 644f9a0..68b7455 100644
--- a/ext/nmatrix/math/getrf.h
+++ b/ext/nmatrix/math/getrf.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/math/getri.h b/ext/nmatrix/math/getri.h
index 125f94e..f0c1614 100644
--- a/ext/nmatrix/math/getri.h
+++ b/ext/nmatrix/math/getri.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/math/getrs.h b/ext/nmatrix/math/getrs.h
index 22f1af4..8a6ddb2 100644
--- a/ext/nmatrix/math/getrs.h
+++ b/ext/nmatrix/math/getrs.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -60,7 +60,11 @@
#define GETRS_H
extern "C" {
+#if defined HAVE_CBLAS_H
#include <cblas.h>
+#elif defined HAVE_ATLAS_CBLAS_H
+ #include <atlas/cblas.h>
+#endif
}
namespace nm { namespace math {
@@ -122,4 +126,4 @@ inline int clapack_getrs(const enum CBLAS_ORDER order, const enum CBLAS_TRANSPOS
} } // end nm::math
-#endif // GETRS_H
\ No newline at end of file
+#endif // GETRS_H
diff --git a/ext/nmatrix/math/idamax.h b/ext/nmatrix/math/idamax.h
index d8b48d0..679b3a0 100644
--- a/ext/nmatrix/math/idamax.h
+++ b/ext/nmatrix/math/idamax.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/math/inc.h b/ext/nmatrix/math/inc.h
index 3f778e9..11a9594 100644
--- a/ext/nmatrix/math/inc.h
+++ b/ext/nmatrix/math/inc.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -31,11 +31,17 @@
extern "C" { // These need to be in an extern "C" block or you'll get all kinds of undefined symbol errors.
+#if defined HAVE_CBLAS_H
#include <cblas.h>
+#elif defined HAVE_ATLAS_CBLAS_H
+ #include <atlas/cblas.h>
+#endif
- #ifdef HAVE_CLAPACK_H
- #include <clapack.h>
- #endif
+#if defined HAVE_CLAPACK_H
+ #include <clapack.h>
+#elif defined HAVE_ATLAS_CLAPACK_H
+ #include <atlas/clapack.h>
+#endif
}
-#endif // INC_H
\ No newline at end of file
+#endif // INC_H
diff --git a/ext/nmatrix/math/laswp.h b/ext/nmatrix/math/laswp.h
index 3887db3..235f76a 100644
--- a/ext/nmatrix/math/laswp.h
+++ b/ext/nmatrix/math/laswp.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/math/long_dtype.h b/ext/nmatrix/math/long_dtype.h
index 3993786..18341ec 100644
--- a/ext/nmatrix/math/long_dtype.h
+++ b/ext/nmatrix/math/long_dtype.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/math/math.h b/ext/nmatrix/math/math.h
index 742b38c..fc329cd 100644
--- a/ext/nmatrix/math/math.h
+++ b/ext/nmatrix/math/math.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -69,11 +69,17 @@
*/
extern "C" { // These need to be in an extern "C" block or you'll get all kinds of undefined symbol errors.
+#if defined HAVE_CBLAS_H
#include <cblas.h>
+#elif defined HAVE_ATLAS_CBLAS_H
+ #include <atlas/cblas.h>
+#endif
- #ifdef HAVE_CLAPACK_H
- #include <clapack.h>
- #endif
+#if defined HAVE_CLAPACK_H
+ #include <clapack.h>
+#elif defined HAVE_ATLAS_CLAPACK_H
+ #include <atlas/clapack.h>
+#endif
}
#include <algorithm> // std::min, std::max
@@ -531,15 +537,15 @@ inline void smmp_sort_columns(const size_t n, const IType* ia, IType* ja, DType*
*/
template <typename DType>
inline int potrf(const enum CBLAS_ORDER order, const enum CBLAS_UPLO uplo, const int N, DType* A, const int lda) {
-#ifdef HAVE_CLAPACK_H
+#if defined HAVE_CLAPACK_H || defined HAVE_ATLAS_CLAPACK_H
rb_raise(rb_eNotImpError, "not yet implemented for non-BLAS dtypes");
#else
- rb_raise(rb_eNotImpError, "only LAPACK version implemented thus far");
+ rb_raise(rb_eNotImpError, "only CLAPACK version implemented thus far");
#endif
return 0;
}
-#ifdef HAVE_CLAPACK_H
+#if defined HAVE_CLAPACK_H || defined HAVE_ATLAS_CLAPACK_H
template <>
inline int potrf(const enum CBLAS_ORDER order, const enum CBLAS_UPLO uplo, const int N, float* A, const int lda) {
return clapack_spotrf(order, uplo, N, A, lda);
@@ -928,7 +934,7 @@ inline void lauum(const enum CBLAS_ORDER order, const enum CBLAS_UPLO uplo, cons
}
-#ifdef HAVE_CLAPACK_H
+#if defined HAVE_CLAPACK_H || defined HAVE_ATLAS_CLAPACK_H
template <bool is_complex>
inline void lauum(const enum CBLAS_ORDER order, const enum CBLAS_UPLO uplo, const int N, float* A, const int lda) {
clapack_slauum(order, uplo, N, A, lda);
@@ -1019,7 +1025,7 @@ inline int potri(const enum CBLAS_ORDER order, const enum CBLAS_UPLO uplo, const
}
-#ifdef HAVE_CLAPACK_H
+#if defined HAVE_CLAPACK_H || defined HAVE_ATLAS_CLAPACK_H
template <>
inline int potri(const enum CBLAS_ORDER order, const enum CBLAS_UPLO uplo, const int n, float* a, const int lda) {
return clapack_spotri(order, uplo, n, a, lda);
diff --git a/ext/nmatrix/math/nrm2.h b/ext/nmatrix/math/nrm2.h
index 7463677..68e563b 100644
--- a/ext/nmatrix/math/nrm2.h
+++ b/ext/nmatrix/math/nrm2.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -98,7 +98,7 @@ ReturnDType nrm2(const int N, const DType* X, const int incX) {
}
-#ifdef HAVE_CBLAS_H
+#if defined HAVE_CBLAS_H || defined HAVE_ATLAS_CBLAS_H
template <>
inline float nrm2(const int N, const float* X, const int incX) {
return cblas_snrm2(N, X, incX);
diff --git a/ext/nmatrix/math/potrs.h b/ext/nmatrix/math/potrs.h
index 9bae514..6bc62ce 100644
--- a/ext/nmatrix/math/potrs.h
+++ b/ext/nmatrix/math/potrs.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -60,7 +60,11 @@
#define POTRS_H
extern "C" {
+#if defined HAVE_CBLAS_H
#include <cblas.h>
+#elif defined HAVE_ATLAS_CBLAS_H
+ #include <atlas/cblas.h>
+#endif
}
namespace nm { namespace math {
@@ -122,4 +126,4 @@ inline int clapack_potrs(const enum CBLAS_ORDER order, const enum CBLAS_UPLO upl
} } // end nm::math
-#endif // POTRS_H
\ No newline at end of file
+#endif // POTRS_H
diff --git a/ext/nmatrix/math/rot.h b/ext/nmatrix/math/rot.h
index ae839b8..c9906da 100644
--- a/ext/nmatrix/math/rot.h
+++ b/ext/nmatrix/math/rot.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/math/rotg.h b/ext/nmatrix/math/rotg.h
index 9f2fbf4..98cc158 100644
--- a/ext/nmatrix/math/rotg.h
+++ b/ext/nmatrix/math/rotg.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/math/scal.h b/ext/nmatrix/math/scal.h
index 3a4a527..1189091 100644
--- a/ext/nmatrix/math/scal.h
+++ b/ext/nmatrix/math/scal.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/math/swap.h b/ext/nmatrix/math/swap.h
index bd37801..2a5d266 100644
--- a/ext/nmatrix/math/swap.h
+++ b/ext/nmatrix/math/swap.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/math/trsm.h b/ext/nmatrix/math/trsm.h
index bccda3d..1f880b8 100644
--- a/ext/nmatrix/math/trsm.h
+++ b/ext/nmatrix/math/trsm.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -60,7 +60,11 @@
extern "C" {
+#if defined HAVE_CBLAS_H
#include <cblas.h>
+#elif defined HAVE_ATLAS_CBLAS_H
+ #include <atlas/cblas.h>
+#endif
}
namespace nm { namespace math {
@@ -380,4 +384,4 @@ inline void trsm(const enum CBLAS_ORDER order, const enum CBLAS_SIDE side, const
} } // namespace nm::math
-#endif // TRSM_H
\ No newline at end of file
+#endif // TRSM_H
diff --git a/ext/nmatrix/nm_memory.h b/ext/nmatrix/nm_memory.h
new file mode 100644
index 0000000..8ef608f
--- /dev/null
+++ b/ext/nmatrix/nm_memory.h
@@ -0,0 +1,60 @@
+/////////////////////////////////////////////////////////////////////
+// = NMatrix
+//
+// A linear algebra library for scientific computation in Ruby.
+// NMatrix is part of SciRuby.
+//
+// NMatrix was originally inspired by and derived from NArray, by
+// Masahiro Tanaka: http://narray.rubyforge.org
+//
+// == Copyright Information
+//
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
+//
+// Please see LICENSE.txt for additional copyright notices.
+//
+// == Contributing
+//
+// By contributing source code to SciRuby, you agree to be bound by
+// our Contributor Agreement:
+//
+// * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
+//
+// == nm_memory.h
+//
+// Macros for memory allocation and freeing
+
+/**
+ * We define these macros, which just call the ruby ones, as this makes
+ * debugging memory issues (particularly those involving interaction with
+ * the ruby GC) easier, as it's posssible to add debugging code temporarily.
+ */
+#ifndef __NM_MEMORY_H__
+#define __NM_MEMORY_H__
+
+#include <ruby.h>
+
+#define NM_ALLOC(type) (ALLOC(type))
+
+#define NM_ALLOC_N(type, n) (ALLOC_N(type, n))
+
+#define NM_REALLOC_N(var, type, n) (REALLOC_N(var, type, n))
+
+#define NM_ALLOCA_N(type, n) (ALLOCA_N(type, n))
+
+#define NM_FREE(var) (xfree(var))
+
+#define NM_ALLOC_NONRUBY(type) ((type*) malloc(sizeof(type)))
+
+//Defines whether to do conservative gc registrations, i.e. those
+//registrations that we're not that sure are necessary.
+//#define NM_GC_CONSERVATIVE
+
+#ifdef NM_GC_CONSERVATIVE
+#define NM_CONSERVATIVE(statement) (statement)
+#else
+#define NM_CONSERVATIVE(statement)
+#endif //NM_GC_CONSERVATIVE
+
+#endif
diff --git a/ext/nmatrix/nmatrix.cpp b/ext/nmatrix/nmatrix.cpp
index 992a8eb..ca57a2b 100644
--- a/ext/nmatrix/nmatrix.cpp
+++ b/ext/nmatrix/nmatrix.cpp
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -31,12 +31,19 @@
* Standard Includes
*/
-#include <cblas.h>
-#ifdef HAVE_CLAPACK_H
extern "C" {
+#if defined HAVE_CBLAS_H
+ #include <cblas.h>
+#elif defined HAVE_ATLAS_CBLAS_H
+ #include <atlas/cblas.h>
+#endif
+
+#if defined HAVE_CLAPACK_H
#include <clapack.h>
-}
+#elif defined HAVE_ATLAS_CLAPACK_H
+ #include <atlas/clapack.h>
#endif
+}
#include <ruby.h>
#include <algorithm> // std::min
@@ -52,7 +59,7 @@ extern "C" {
#include "math/math.h"
#include "util/io.h"
#include "storage/storage.h"
-#include "storage/list.h"
+#include "storage/list/list.h"
#include "storage/yale/yale.h"
#include "nmatrix.h"
@@ -76,47 +83,6 @@ extern "C" {
namespace nm {
/*
- * Read the shape from a matrix storage file, and ignore any padding.
- *
- * shape should already be allocated before calling this.
- */
- void read_padded_shape(std::ifstream& f, size_t dim, size_t* shape) {
- size_t bytes_read = 0;
-
- // Read shape
- for (size_t i = 0; i < dim; ++i) {
- IType s;
- f.read(reinterpret_cast<char*>(&s), sizeof(IType));
- shape[i] = s;
-
- bytes_read += sizeof(IType);
- }
-
- // Ignore padding
- f.ignore(bytes_read % 8);
- }
-
- void write_padded_shape(std::ofstream& f, size_t dim, size_t* shape) {
- size_t bytes_written = 0;
-
- // Write shape
- for (size_t i = 0; i < dim; ++i) {
- IType s = shape[i];
- f.write(reinterpret_cast<const char*>(&s), sizeof(IType));
-
- bytes_written += sizeof(IType);
- }
-
- // Pad with zeros
- while (bytes_written % 8) {
- IType zero = 0;
- f.write(reinterpret_cast<const char*>(&zero), sizeof(IType));
-
- bytes_written += sizeof(IType);
- }
- }
-
- /*
* This function is pulled out separately so it can be called for hermitian matrix writing, which also uses it.
*/
template <typename DType>
diff --git a/ext/nmatrix/nmatrix.h b/ext/nmatrix/nmatrix.h
index eb423d1..3ab92d3 100644
--- a/ext/nmatrix/nmatrix.h
+++ b/ext/nmatrix/nmatrix.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -53,6 +53,8 @@
#endif
#endif
+#include "nm_memory.h"
+
/*
* Macros
*/
@@ -113,21 +115,27 @@
* return enumerator_init(enumerator_allocate(rb_cEnumerator), obj, meth, argc, argv);
* }
*/
+
+//opening portion -- this allows unregistering any objects in use before returning
+ #define RETURN_SIZED_ENUMERATOR_PRE do { \
+ if (!rb_block_given_p()) {
+
+//remaining portion
#ifdef RUBY_2
#ifndef RETURN_SIZED_ENUMERATOR
#undef RETURN_SIZED_ENUMERATOR
// Ruby 2.0 and higher has rb_enumeratorize_with_size instead of rb_enumeratorize.
// We want to support both in the simplest way possible.
- #define RETURN_SIZED_ENUMERATOR(obj, argc, argv, size_fn) do { \
- if (!rb_block_given_p()) \
- return rb_enumeratorize_with_size((obj), ID2SYM(rb_frame_this_func()), (argc), (argv), (size_fn)); \
+ #define RETURN_SIZED_ENUMERATOR(obj, argc, argv, size_fn) \
+ return rb_enumeratorize_with_size((obj), ID2SYM(rb_frame_this_func()), (argc), (argv), (size_fn)); \
+ } \
} while (0)
#endif
#else
#undef RETURN_SIZED_ENUMERATOR
- #define RETURN_SIZED_ENUMERATOR(obj, argc, argv, size_fn) do { \
- if (!rb_block_given_p()) \
- return rb_enumeratorize((obj), ID2SYM(rb_frame_this_func()), (argc), (argv)); \
+ #define RETURN_SIZED_ENUMERATOR(obj, argc, argv, size_fn) \
+ return rb_enumeratorize((obj), ID2SYM(rb_frame_this_func()), (argc), (argv)); \
+ } \
} while (0)
#endif
@@ -278,6 +286,18 @@ NM_DEF_STRUCT_PRE(NMATRIX); // struct NMATRIX {
NM_DECL_STRUCT(STORAGE*, storage); // STORAGE* storage; // Pointer to storage struct.
NM_DEF_STRUCT_POST(NMATRIX); // };
+/* Structs for dealing with VALUEs in use so that they don't get GC'd */
+
+typedef struct __NM_GC_LL_NODE {
+ VALUE* val;
+ size_t n;
+ __NM_GC_LL_NODE* next;
+} nm_gc_ll_node;
+
+typedef struct __NM_GC_HOLDER {
+ __NM_GC_LL_NODE* start;
+} nm_gc_holder;
+
#define NM_MAX_RANK 15
#define UnwrapNMatrix(obj,var) Data_Get_Struct(obj, NMATRIX, var)
@@ -355,16 +375,21 @@ extern "C" {
NM_DECL_ENUM(dtype_t, nm_dtype_min(VALUE));
// Non-API functions needed by other cpp files.
- NMATRIX* nm_create(nm::stype_t stype, STORAGE* storage);
- NMATRIX* nm_cast_with_ctype_args(NMATRIX* self, nm::stype_t new_stype, nm::dtype_t new_dtype, void* init_ptr);
+ NMATRIX* nm_create(NM_DECL_ENUM(stype_t, stype), STORAGE* storage);
+ NMATRIX* nm_cast_with_ctype_args(NMATRIX* self, NM_DECL_ENUM(stype_t, new_stype), NM_DECL_ENUM(dtype_t, new_dtype), void* init_ptr);
VALUE nm_cast(VALUE self, VALUE new_stype_symbol, VALUE new_dtype_symbol, VALUE init);
void nm_mark(NMATRIX* mat);
void nm_delete(NMATRIX* mat);
void nm_delete_ref(NMATRIX* mat);
- void nm_mark(NMATRIX* mat);
void nm_register_values(VALUE* vals, size_t n);
void nm_unregister_values(VALUE* vals, size_t n);
-
+ void nm_register_value(VALUE& val);
+ void nm_unregister_value(VALUE& val);
+ void nm_register_storage(nm::stype_t stype, const STORAGE* storage);
+ void nm_unregister_storage(nm::stype_t stype, const STORAGE* storage);
+ void nm_register_nmatrix(NMATRIX* nmatrix);
+ void nm_unregister_nmatrix(NMATRIX* nmatrix);
+ void nm_completely_unregister_value(VALUE& val);
#ifdef __cplusplus
}
#endif
diff --git a/ext/nmatrix/ruby_constants.cpp b/ext/nmatrix/ruby_constants.cpp
index 04c442c..20691e7 100644
--- a/ext/nmatrix/ruby_constants.cpp
+++ b/ext/nmatrix/ruby_constants.cpp
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-// NMatrix is Copyright (c) 2012, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -89,6 +89,8 @@ VALUE cNMatrix,
cNMatrix_YaleFunctions,
cNMatrix_BLAS,
cNMatrix_LAPACK,
+
+ cNMatrix_GC_holder,
nm_eDataTypeError,
nm_eConvergenceError,
diff --git a/ext/nmatrix/ruby_constants.h b/ext/nmatrix/ruby_constants.h
index 7cd8a01..5f2eecb 100644
--- a/ext/nmatrix/ruby_constants.h
+++ b/ext/nmatrix/ruby_constants.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -91,6 +91,8 @@ extern VALUE cNMatrix,
cNMatrix_YaleFunctions,
cNMatrix_BLAS,
cNMatrix_LAPACK,
+
+ cNMatrix_GC_holder,
nm_eDataTypeError,
nm_eConvergenceError,
diff --git a/ext/nmatrix/ruby_nmatrix.c b/ext/nmatrix/ruby_nmatrix.c
index 18b84f4..13d2dc3 100644
--- a/ext/nmatrix/ruby_nmatrix.c
+++ b/ext/nmatrix/ruby_nmatrix.c
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -51,6 +51,7 @@ static VALUE nm_capacity(VALUE self);
static VALUE nm_each_with_indices(VALUE nmatrix);
static VALUE nm_each_stored_with_indices(VALUE nmatrix);
static VALUE nm_each_ordered_stored_with_indices(VALUE nmatrix);
+static VALUE nm_map_stored(VALUE nmatrix);
static SLICE* get_slice(size_t dim, int argc, VALUE* arg, size_t* shape);
static VALUE nm_xslice(int argc, VALUE* argv, void* (*slice_func)(const STORAGE*, SLICE*), void (*delete_func)(NMATRIX*), VALUE self);
@@ -74,10 +75,27 @@ static VALUE nm_ew_##name(VALUE left_val, VALUE right_val) { \
return elementwise_op(nm::EW_##oper, left_val, right_val); \
}
+#define DEF_UNARY_RUBY_ACCESSOR(oper, name) \
+static VALUE nm_unary_##name(VALUE self) { \
+ return unary_op(nm::UNARY_##oper, self); \
+}
+
+#define DEF_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(oper, name) \
+static VALUE nm_noncom_ew_##name(int argc, VALUE* argv, VALUE self) { \
+ if (argc > 1) { \
+ return noncom_elementwise_op(nm::NONCOM_EW_##oper, self, argv[0], argv[1]); \
+ } else { \
+ return noncom_elementwise_op(nm::NONCOM_EW_##oper, self, argv[0], Qfalse); \
+ } \
+}
+
+
/*
* Macro declares a corresponding accessor function prototype for some element-wise operation.
*/
#define DECL_ELEMENTWISE_RUBY_ACCESSOR(name) static VALUE nm_ew_##name(VALUE left_val, VALUE right_val);
+#define DECL_UNARY_RUBY_ACCESSOR(name) static VALUE nm_unary_##name(VALUE self);
+#define DECL_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(name) static VALUE nm_noncom_ew_##name(int argc, VALUE* argv, VALUE self);
DECL_ELEMENTWISE_RUBY_ACCESSOR(add)
DECL_ELEMENTWISE_RUBY_ACCESSOR(subtract)
@@ -91,8 +109,36 @@ DECL_ELEMENTWISE_RUBY_ACCESSOR(lt)
DECL_ELEMENTWISE_RUBY_ACCESSOR(gt)
DECL_ELEMENTWISE_RUBY_ACCESSOR(leq)
DECL_ELEMENTWISE_RUBY_ACCESSOR(geq)
+DECL_UNARY_RUBY_ACCESSOR(sin)
+DECL_UNARY_RUBY_ACCESSOR(cos)
+DECL_UNARY_RUBY_ACCESSOR(tan)
+DECL_UNARY_RUBY_ACCESSOR(asin)
+DECL_UNARY_RUBY_ACCESSOR(acos)
+DECL_UNARY_RUBY_ACCESSOR(atan)
+DECL_UNARY_RUBY_ACCESSOR(sinh)
+DECL_UNARY_RUBY_ACCESSOR(cosh)
+DECL_UNARY_RUBY_ACCESSOR(tanh)
+DECL_UNARY_RUBY_ACCESSOR(asinh)
+DECL_UNARY_RUBY_ACCESSOR(acosh)
+DECL_UNARY_RUBY_ACCESSOR(atanh)
+DECL_UNARY_RUBY_ACCESSOR(exp)
+DECL_UNARY_RUBY_ACCESSOR(log2)
+DECL_UNARY_RUBY_ACCESSOR(log10)
+DECL_UNARY_RUBY_ACCESSOR(sqrt)
+DECL_UNARY_RUBY_ACCESSOR(erf)
+DECL_UNARY_RUBY_ACCESSOR(erfc)
+DECL_UNARY_RUBY_ACCESSOR(cbrt)
+DECL_UNARY_RUBY_ACCESSOR(gamma)
+DECL_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(atan2)
+DECL_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(ldexp)
+DECL_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(hypot)
+
+//log can be unary, but also take a base argument, as with Math.log
+static VALUE nm_unary_log(int argc, VALUE* argv, VALUE self);
static VALUE elementwise_op(nm::ewop_t op, VALUE left_val, VALUE right_val);
+static VALUE unary_op(nm::unaryop_t op, VALUE self);
+static VALUE noncom_elementwise_op(nm::noncom_ewop_t op, VALUE self, VALUE other, VALUE orderflip);
static VALUE nm_symmetric(VALUE self);
static VALUE nm_hermitian(VALUE self);
@@ -144,6 +190,12 @@ void Init_nmatrix() {
*/
nm_eStorageTypeError = rb_define_class("StorageTypeError", rb_eStandardError);
+ /*
+ * Class that holds values in use by the C code.
+ */
+ cNMatrix_GC_holder = rb_define_class("NMGCHolder", rb_cObject);
+
+
///////////////////
// Class Methods //
///////////////////
@@ -169,7 +221,7 @@ void Init_nmatrix() {
rb_define_method(cNMatrix, "write", (METHOD)nm_write, -1);
// Technically, the following function is a copy constructor.
- rb_define_method(cNMatrix, "transpose", (METHOD)nm_init_transposed, 0);
+ rb_define_protected_method(cNMatrix, "clone_transpose", (METHOD)nm_init_transposed, 0);
rb_define_method(cNMatrix, "dtype", (METHOD)nm_dtype, 0);
rb_define_method(cNMatrix, "stype", (METHOD)nm_stype, 0);
@@ -198,8 +250,10 @@ void Init_nmatrix() {
rb_define_protected_method(cNMatrix, "__dense_map_pair__", (METHOD)nm_dense_map_pair, 1);
rb_define_method(cNMatrix, "each_with_indices", (METHOD)nm_each_with_indices, 0);
rb_define_method(cNMatrix, "each_stored_with_indices", (METHOD)nm_each_stored_with_indices, 0);
+ rb_define_method(cNMatrix, "map_stored", (METHOD)nm_map_stored, 0);
rb_define_method(cNMatrix, "each_ordered_stored_with_indices", (METHOD)nm_each_ordered_stored_with_indices, 0);
rb_define_protected_method(cNMatrix, "__list_map_merged_stored__", (METHOD)nm_list_map_merged_stored, 2);
+ rb_define_protected_method(cNMatrix, "__list_map_stored__", (METHOD)nm_list_map_stored, 1);
rb_define_protected_method(cNMatrix, "__yale_map_merged_stored__", (METHOD)nm_yale_map_merged_stored, 2);
rb_define_protected_method(cNMatrix, "__yale_map_stored__", (METHOD)nm_yale_map_stored, 0);
rb_define_protected_method(cNMatrix, "__yale_stored_diagonal_each_with_indices__", (METHOD)nm_yale_stored_diagonal_each_with_indices, 0);
@@ -214,6 +268,32 @@ void Init_nmatrix() {
rb_define_method(cNMatrix, "**", (METHOD)nm_ew_power, 1);
rb_define_method(cNMatrix, "%", (METHOD)nm_ew_mod, 1);
+ rb_define_method(cNMatrix, "atan2", (METHOD)nm_noncom_ew_atan2, -1);
+ rb_define_method(cNMatrix, "ldexp", (METHOD)nm_noncom_ew_ldexp, -1);
+ rb_define_method(cNMatrix, "hypot", (METHOD)nm_noncom_ew_hypot, -1);
+
+ rb_define_method(cNMatrix, "sin", (METHOD)nm_unary_sin, 0);
+ rb_define_method(cNMatrix, "cos", (METHOD)nm_unary_cos, 0);
+ rb_define_method(cNMatrix, "tan", (METHOD)nm_unary_tan, 0);
+ rb_define_method(cNMatrix, "asin", (METHOD)nm_unary_asin, 0);
+ rb_define_method(cNMatrix, "acos", (METHOD)nm_unary_acos, 0);
+ rb_define_method(cNMatrix, "atan", (METHOD)nm_unary_atan, 0);
+ rb_define_method(cNMatrix, "sinh", (METHOD)nm_unary_sinh, 0);
+ rb_define_method(cNMatrix, "cosh", (METHOD)nm_unary_cosh, 0);
+ rb_define_method(cNMatrix, "tanh", (METHOD)nm_unary_tanh, 0);
+ rb_define_method(cNMatrix, "asinh", (METHOD)nm_unary_asinh, 0);
+ rb_define_method(cNMatrix, "acosh", (METHOD)nm_unary_acosh, 0);
+ rb_define_method(cNMatrix, "atanh", (METHOD)nm_unary_atanh, 0);
+ rb_define_method(cNMatrix, "exp", (METHOD)nm_unary_exp, 0);
+ rb_define_method(cNMatrix, "log2", (METHOD)nm_unary_log2, 0);
+ rb_define_method(cNMatrix, "log10", (METHOD)nm_unary_log10, 0);
+ rb_define_method(cNMatrix, "sqrt", (METHOD)nm_unary_sqrt, 0);
+ rb_define_method(cNMatrix, "erf", (METHOD)nm_unary_erf, 0);
+ rb_define_method(cNMatrix, "erfc", (METHOD)nm_unary_erfc, 0);
+ rb_define_method(cNMatrix, "cbrt", (METHOD)nm_unary_cbrt, 0);
+ rb_define_method(cNMatrix, "gamma", (METHOD)nm_unary_gamma, 0);
+ rb_define_method(cNMatrix, "log", (METHOD)nm_unary_log, -1);
+
rb_define_method(cNMatrix, "=~", (METHOD)nm_ew_eqeq, 1);
rb_define_method(cNMatrix, "!~", (METHOD)nm_ew_neq, 1);
rb_define_method(cNMatrix, "<=", (METHOD)nm_ew_leq, 1);
@@ -283,9 +363,9 @@ void Init_nmatrix() {
* Slice constructor.
*/
static SLICE* alloc_slice(size_t dim) {
- SLICE* slice = ALLOC(SLICE);
- slice->coords = ALLOC_N(size_t, dim);
- slice->lengths = ALLOC_N(size_t, dim);
+ SLICE* slice = NM_ALLOC(SLICE);
+ slice->coords = NM_ALLOC_N(size_t, dim);
+ slice->lengths = NM_ALLOC_N(size_t, dim);
return slice;
}
@@ -294,9 +374,9 @@ static SLICE* alloc_slice(size_t dim) {
* Slice destructor.
*/
static void free_slice(SLICE* slice) {
- xfree(slice->coords);
- xfree(slice->lengths);
- xfree(slice);
+ NM_FREE(slice->coords);
+ NM_FREE(slice->lengths);
+ NM_FREE(slice);
}
@@ -304,7 +384,7 @@ static void free_slice(SLICE* slice) {
* Allocator.
*/
static VALUE nm_alloc(VALUE klass) {
- NMATRIX* mat = ALLOC(NMATRIX);
+ NMATRIX* mat = NM_ALLOC(NMATRIX);
mat->storage = NULL;
// DO NOT MARK This STRUCT. It has no storage allocated, and no stype, so mark will do an invalid something.
@@ -320,6 +400,7 @@ static VALUE nm_alloc(VALUE klass) {
* just return the original matrix's capacity.
*/
static VALUE nm_capacity(VALUE self) {
+ NM_CONSERVATIVE(nm_register_value(self));
VALUE cap;
switch(NM_STYPE(self)) {
@@ -336,9 +417,11 @@ static VALUE nm_capacity(VALUE self) {
break;
default:
+ NM_CONSERVATIVE(nm_unregister_value(self));
rb_raise(nm_eStorageTypeError, "unrecognized stype in nm_capacity()");
}
+ NM_CONSERVATIVE(nm_unregister_value(self));
return cap;
}
@@ -363,7 +446,7 @@ void nm_delete(NMATRIX* mat) {
};
ttable[mat->stype](mat->storage);
- xfree(mat);
+ NM_FREE(mat);
}
/*
@@ -377,33 +460,186 @@ void nm_delete_ref(NMATRIX* mat) {
};
ttable[mat->stype](mat->storage);
- xfree(mat);
+ NM_FREE(mat);
+}
+
+
+/**
+ * These variables hold a linked list of VALUEs that are registered to be in
+ * use by nmatrix so that they can be marked when GC runs.
+ */
+static VALUE* gc_value_holder = NULL;
+static nm_gc_holder* gc_value_holder_struct = NULL;
+static nm_gc_holder* allocated_pool = NULL; // an object pool for linked list nodes; using pooling is in some cases a substantial performance improvement
+
+/**
+ * GC Marking function for the values that have been registered.
+ */
+static void __nm_mark_value_container(nm_gc_holder* gc_value_holder_struct) {
+ if (gc_value_holder_struct && gc_value_holder_struct->start) {
+ nm_gc_ll_node* curr = gc_value_holder_struct->start;
+ while (curr) {
+ rb_gc_mark_locations(curr->val, curr->val + curr->n);
+ curr = curr->next;
+ }
+ }
+}
+
+/**
+ * Initilalizes the linked list of in-use VALUEs if it hasn't been done
+ * already.
+ */
+static void __nm_initialize_value_container() {
+ if (gc_value_holder == NULL) {
+ gc_value_holder_struct = NM_ALLOC_NONRUBY(nm_gc_holder);
+ allocated_pool = NM_ALLOC_NONRUBY(nm_gc_holder);
+ gc_value_holder = NM_ALLOC_NONRUBY(VALUE);
+ gc_value_holder_struct->start = NULL;
+ allocated_pool->start = NULL;
+ *gc_value_holder = Data_Wrap_Struct(cNMatrix_GC_holder, __nm_mark_value_container, NULL, gc_value_holder_struct);
+ rb_global_variable(gc_value_holder);
+ }
}
/*
- * Register the addresses of an array of VALUEs with the gc to avoid collection
+ * Register an array of VALUEs to avoid their collection
* while using them internally.
*/
void nm_register_values(VALUE* values, size_t n) {
+ if (!gc_value_holder_struct)
+ __nm_initialize_value_container();
if (values) {
- for (size_t i = n; i-- > 0;) {
- rb_gc_register_address(values + i);
+ nm_gc_ll_node* to_insert = NULL;
+ if (allocated_pool->start) {
+ to_insert = allocated_pool->start;
+ allocated_pool->start = to_insert->next;
+ } else {
+ to_insert = NM_ALLOC_NONRUBY(nm_gc_ll_node);
}
+ to_insert->val = values;
+ to_insert->n = n;
+ to_insert->next = gc_value_holder_struct->start;
+ gc_value_holder_struct->start = to_insert;
}
}
/*
- * Unregister the addresses of an array of VALUEs with the gc to allow normal
+ * Unregister an array of VALUEs with the gc to allow normal
* garbage collection to occur again.
*/
void nm_unregister_values(VALUE* values, size_t n) {
if (values) {
- for (size_t i = n; i-- > 0;) {
- rb_gc_unregister_address(values + i);
+ if (gc_value_holder_struct) {
+ nm_gc_ll_node* curr = gc_value_holder_struct->start;
+ nm_gc_ll_node* last = NULL;
+ while (curr) {
+ if (curr->val == values) {
+ if (last) {
+ last->next = curr->next;
+ } else {
+ gc_value_holder_struct->start = curr->next;
+ }
+ curr->next = allocated_pool->start;
+ curr->val = NULL;
+ curr->n = 0;
+ allocated_pool->start = curr;
+ break;
+ }
+ last = curr;
+ curr = curr->next;
+ }
+ }
+ }
+}
+
+/**
+ * Register a single VALUE as in use to avoid garbage collection.
+ */
+void nm_register_value(VALUE& val) {
+ nm_register_values(&val, 1);
+}
+
+/**
+ * Unregister a single VALUE to allow normal garbage collection.
+ */
+void nm_unregister_value(VALUE& val) {
+ nm_unregister_values(&val, 1);
+}
+
+/**
+ * Removes all instances of a single VALUE in the gc list. This can be
+ * dangerous. Primarily used when something is about to be
+ * freed and replaced so that and residual registrations won't access after
+ * free.
+ **/
+void nm_completely_unregister_value(VALUE& val) {
+ if (gc_value_holder_struct) {
+ nm_gc_ll_node* curr = gc_value_holder_struct->start;
+ nm_gc_ll_node* last = NULL;
+ while (curr) {
+ if (curr->val == &val) {
+ if (last) {
+ last->next = curr->next;
+ } else {
+ gc_value_holder_struct->start = curr->next;
+ }
+ nm_gc_ll_node* temp_next = curr->next;
+ curr->next = allocated_pool->start;
+ curr->val = NULL;
+ curr->n = 0;
+ allocated_pool->start = curr;
+ curr = temp_next;
+ } else {
+ last = curr;
+ curr = curr->next;
+ }
}
}
}
+
+
+/**
+ * Register a STORAGE struct of the supplied stype to avoid garbage collection
+ * of its internals.
+ *
+ * Delegates to the storage-specific methods. They will check dtype and ignore
+ * non-rubyobject dtypes, so it's safe to pass any storage in.
+ */
+void nm_register_storage(nm::stype_t stype, const STORAGE* storage) {
+ STYPE_REGISTER_TABLE(ttable);
+ ttable[stype](storage);
+}
+
+/**
+ * Unregister a STORAGE struct of the supplied stype to allow normal garbage collection
+ * of its internals.
+ *
+ * Delegates to the storage-specific methods. They will check dtype and ignore
+ * non-rubyobject dtypes, so it's safe to pass any storage in.
+ *
+ */
+void nm_unregister_storage(nm::stype_t stype, const STORAGE* storage) {
+ STYPE_UNREGISTER_TABLE(ttable);
+ ttable[stype](storage);
+}
+
+/**
+ * Registers an NMATRIX struct to avoid garbage collection of its internals.
+ */
+void nm_register_nmatrix(NMATRIX* nmatrix) {
+ if (nmatrix)
+ nm_register_storage(nmatrix->stype, nmatrix->storage);
+}
+
+/**
+ * Unregisters an NMATRIX struct to avoid garbage collection of its internals.
+ */
+void nm_unregister_nmatrix(NMATRIX* nmatrix) {
+ if (nmatrix)
+ nm_unregister_storage(nmatrix->stype, nmatrix->storage);
+}
+
/*
* call-seq:
* dtype -> Symbol
@@ -427,7 +663,6 @@ static VALUE nm_dtype(VALUE self) {
* This is a singleton method on NMatrix, e.g., NMatrix.upcast(:int32, :int64)
*/
static VALUE nm_upcast(VALUE self, VALUE t1, VALUE t2) {
-
nm::dtype_t d1 = nm_dtype_from_rbsymbol(t1),
d2 = nm_dtype_from_rbsymbol(t2);
@@ -462,18 +697,26 @@ static VALUE nm_default_value(VALUE self) {
* Iterate over all entries of any matrix in standard storage order (as with #each), and include the indices.
*/
static VALUE nm_each_with_indices(VALUE nmatrix) {
- volatile VALUE nm = nmatrix;
+ NM_CONSERVATIVE(nm_register_value(nmatrix));
+ VALUE to_return = Qnil;
- switch(NM_STYPE(nm)) {
+ switch(NM_STYPE(nmatrix)) {
case nm::YALE_STORE:
- return nm_yale_each_with_indices(nm);
+ to_return = nm_yale_each_with_indices(nmatrix);
+ break;
case nm::DENSE_STORE:
- return nm_dense_each_with_indices(nm);
+ to_return = nm_dense_each_with_indices(nmatrix);
+ break;
case nm::LIST_STORE:
- return nm_list_each_with_indices(nm, false);
+ to_return = nm_list_each_with_indices(nmatrix, false);
+ break;
default:
+ NM_CONSERVATIVE(nm_unregister_value(nmatrix));
rb_raise(nm_eDataTypeError, "Not a proper storage type");
}
+
+ NM_CONSERVATIVE(nm_unregister_value(nmatrix));
+ return to_return;
}
/*
@@ -485,41 +728,88 @@ static VALUE nm_each_with_indices(VALUE nmatrix) {
* i, j, ..., and the entry itself.
*/
static VALUE nm_each_stored_with_indices(VALUE nmatrix) {
- volatile VALUE nm = nmatrix;
+ NM_CONSERVATIVE(nm_register_value(nmatrix));
+ VALUE to_return = Qnil;
- switch(NM_STYPE(nm)) {
+ switch(NM_STYPE(nmatrix)) {
case nm::YALE_STORE:
- return nm_yale_each_stored_with_indices(nm);
+ to_return = nm_yale_each_stored_with_indices(nmatrix);
+ break;
case nm::DENSE_STORE:
- return nm_dense_each_with_indices(nm);
+ to_return = nm_dense_each_with_indices(nmatrix);
+ break;
case nm::LIST_STORE:
- return nm_list_each_with_indices(nm, true);
+ to_return = nm_list_each_with_indices(nmatrix, true);
+ break;
default:
+ NM_CONSERVATIVE(nm_unregister_value(nmatrix));
rb_raise(nm_eDataTypeError, "Not a proper storage type");
}
+
+ NM_CONSERVATIVE(nm_unregister_value(nmatrix));
+ return to_return;
}
/*
* call-seq:
+ * map_stored -> Enumerator
+ *
+ * Iterate over the stored entries of any matrix. For dense and yale, this iterates over non-zero
+ * entries; for list, this iterates over non-default entries. Yields dim+1 values for each entry:
+ * i, j, ..., and the entry itself.
+ */
+static VALUE nm_map_stored(VALUE nmatrix) {
+ NM_CONSERVATIVE(nm_register_value(nmatrix));
+ VALUE to_return = Qnil;
+
+ switch(NM_STYPE(nmatrix)) {
+ case nm::YALE_STORE:
+ to_return = nm_yale_map_stored(nmatrix);
+ break;
+ case nm::DENSE_STORE:
+ to_return = nm_dense_map(nmatrix);
+ break;
+ case nm::LIST_STORE:
+ to_return = nm_list_map_stored(nmatrix, Qnil);
+ break;
+ default:
+ NM_CONSERVATIVE(nm_unregister_value(nmatrix));
+ rb_raise(nm_eDataTypeError, "Not a proper storage type");
+ }
+
+ NM_CONSERVATIVE(nm_unregister_value(nmatrix));
+ return to_return;
+}
+
+/*
+ * call-seq:
* each_ordered_stored_with_indices -> Enumerator
*
* Very similar to #each_stored_with_indices. The key difference is that it enforces matrix ordering rather
* than storage ordering, which only matters if your matrix is Yale.
*/
static VALUE nm_each_ordered_stored_with_indices(VALUE nmatrix) {
- volatile VALUE nm = nmatrix;
+ NM_CONSERVATIVE(nm_register_value(nmatrix));
+ VALUE to_return = Qnil;
- switch(NM_STYPE(nm)) {
+ switch(NM_STYPE(nmatrix)) {
case nm::YALE_STORE:
- return nm_yale_each_ordered_stored_with_indices(nm);
+ to_return = nm_yale_each_ordered_stored_with_indices(nmatrix);
+ break;
case nm::DENSE_STORE:
- return nm_dense_each_with_indices(nm);
+ to_return = nm_dense_each_with_indices(nmatrix);
+ break;
case nm::LIST_STORE:
- return nm_list_each_with_indices(nm, true);
+ to_return = nm_list_each_with_indices(nmatrix, true);
+ break;
default:
+ NM_CONSERVATIVE(nm_unregister_value(nmatrix));
rb_raise(nm_eDataTypeError, "Not a proper storage type");
}
+
+ NM_CONSERVATIVE(nm_unregister_value(nmatrix));
+ return to_return;
}
@@ -530,8 +820,13 @@ static VALUE nm_each_ordered_stored_with_indices(VALUE nmatrix) {
* For elementwise, use =~ instead.
*
* This method will raise an exception if dimensions do not match.
+ *
+ * When stypes differ, this function calls a protected Ruby method.
*/
static VALUE nm_eqeq(VALUE left, VALUE right) {
+ NM_CONSERVATIVE(nm_register_value(left));
+ NM_CONSERVATIVE(nm_register_value(right));
+
NMATRIX *l, *r;
CheckNMatrixType(left);
@@ -540,23 +835,35 @@ static VALUE nm_eqeq(VALUE left, VALUE right) {
UnwrapNMatrix(left, l);
UnwrapNMatrix(right, r);
- if (l->stype != r->stype)
- rb_raise(rb_eNotImpError, "comparison between different matrix stypes not yet implemented");
-
bool result = false;
- switch(l->stype) {
- case nm::DENSE_STORE:
- result = nm_dense_storage_eqeq(l->storage, r->storage);
- break;
- case nm::LIST_STORE:
- result = nm_list_storage_eqeq(l->storage, r->storage);
- break;
- case nm::YALE_STORE:
- result = nm_yale_storage_eqeq(l->storage, r->storage);
- break;
+ if (l->stype != r->stype) { // DIFFERENT STYPES
+
+ if (l->stype == nm::DENSE_STORE)
+ result = rb_funcall(left, rb_intern("dense_eql_sparse?"), 1, right);
+ else if (r->stype == nm::DENSE_STORE)
+ result = rb_funcall(right, rb_intern("dense_eql_sparse?"), 1, left);
+ else
+ result = rb_funcall(left, rb_intern("sparse_eql_sparse?"), 1, right);
+
+ } else {
+
+ switch(l->stype) { // SAME STYPES
+ case nm::DENSE_STORE:
+ result = nm_dense_storage_eqeq(l->storage, r->storage);
+ break;
+ case nm::LIST_STORE:
+ result = nm_list_storage_eqeq(l->storage, r->storage);
+ break;
+ case nm::YALE_STORE:
+ result = nm_yale_storage_eqeq(l->storage, r->storage);
+ break;
+ }
}
+ NM_CONSERVATIVE(nm_unregister_value(left));
+ NM_CONSERVATIVE(nm_unregister_value(right));
+
return result ? Qtrue : Qfalse;
}
@@ -573,6 +880,60 @@ DEF_ELEMENTWISE_RUBY_ACCESSOR(GEQ, geq)
DEF_ELEMENTWISE_RUBY_ACCESSOR(LT, lt)
DEF_ELEMENTWISE_RUBY_ACCESSOR(GT, gt)
+DEF_UNARY_RUBY_ACCESSOR(SIN, sin)
+DEF_UNARY_RUBY_ACCESSOR(COS, cos)
+DEF_UNARY_RUBY_ACCESSOR(TAN, tan)
+DEF_UNARY_RUBY_ACCESSOR(ASIN, asin)
+DEF_UNARY_RUBY_ACCESSOR(ACOS, acos)
+DEF_UNARY_RUBY_ACCESSOR(ATAN, atan)
+DEF_UNARY_RUBY_ACCESSOR(SINH, sinh)
+DEF_UNARY_RUBY_ACCESSOR(COSH, cosh)
+DEF_UNARY_RUBY_ACCESSOR(TANH, tanh)
+DEF_UNARY_RUBY_ACCESSOR(ASINH, asinh)
+DEF_UNARY_RUBY_ACCESSOR(ACOSH, acosh)
+DEF_UNARY_RUBY_ACCESSOR(ATANH, atanh)
+DEF_UNARY_RUBY_ACCESSOR(EXP, exp)
+DEF_UNARY_RUBY_ACCESSOR(LOG2, log2)
+DEF_UNARY_RUBY_ACCESSOR(LOG10, log10)
+DEF_UNARY_RUBY_ACCESSOR(SQRT, sqrt)
+DEF_UNARY_RUBY_ACCESSOR(ERF, erf)
+DEF_UNARY_RUBY_ACCESSOR(ERFC, erfc)
+DEF_UNARY_RUBY_ACCESSOR(CBRT, cbrt)
+DEF_UNARY_RUBY_ACCESSOR(GAMMA, gamma)
+
+DEF_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(ATAN2, atan2)
+DEF_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(LDEXP, ldexp)
+DEF_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(HYPOT, hypot)
+
+static VALUE nm_unary_log(int argc, VALUE* argv, VALUE self) {
+ NM_CONSERVATIVE(nm_register_values(argv, argc));
+ const double default_log_base = exp(1.0);
+ NMATRIX* left;
+ UnwrapNMatrix(self, left);
+ std::string sym;
+
+ switch(left->stype) {
+ case nm::DENSE_STORE:
+ sym = "__dense_unary_log__";
+ break;
+ case nm::YALE_STORE:
+ sym = "__yale_unary_log__";
+ break;
+ case nm::LIST_STORE:
+ sym = "__list_unary_log__";
+ break;
+ }
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
+ if (argc > 0) { //supplied a base
+ return rb_funcall(self, rb_intern(sym.c_str()), 1, argv[0]);
+ }
+ return rb_funcall(self, rb_intern(sym.c_str()), 1, nm::RubyObject(default_log_base).rval);
+}
+
+//DEF_ELEMENTWISE_RUBY_ACCESSOR(ATAN2, atan2)
+//DEF_ELEMENTWISE_RUBY_ACCESSOR(LDEXP, ldexp)
+//DEF_ELEMENTWISE_RUBY_ACCESSOR(HYPOT, hypot)
+
/*
* call-seq:
* hermitian? -> Boolean
@@ -598,6 +959,7 @@ static VALUE nm_hermitian(VALUE self) {
* Bang should imply that no copy is being made, even temporarily.
*/
static VALUE nm_complex_conjugate_bang(VALUE self) {
+
NMATRIX* m;
void* elem;
size_t size, p;
@@ -643,11 +1005,13 @@ static VALUE nm_complex_conjugate_bang(VALUE self) {
* need to worry about deleting it.
*/
NMATRIX* nm_create(nm::stype_t stype, STORAGE* storage) {
- NMATRIX* mat = ALLOC(NMATRIX);
+ nm_register_storage(stype, storage);
+ NMATRIX* mat = NM_ALLOC(NMATRIX);
mat->stype = stype;
mat->storage = storage;
+ nm_unregister_storage(stype, storage);
return mat;
}
@@ -655,6 +1019,8 @@ NMATRIX* nm_create(nm::stype_t stype, STORAGE* storage) {
* @see nm_init
*/
static VALUE nm_init_new_version(int argc, VALUE* argv, VALUE self) {
+ NM_CONSERVATIVE(nm_register_values(argv, argc));
+ NM_CONSERVATIVE(nm_register_value(self));
VALUE shape_ary, initial_ary, hash;
//VALUE shape_ary, default_val, capacity, initial_ary, dtype_sym, stype_sym;
// Mandatory args: shape, dtype, stype
@@ -676,7 +1042,9 @@ static VALUE nm_init_new_version(int argc, VALUE* argv, VALUE self) {
}
}
#endif
-
+ NM_CONSERVATIVE(nm_register_value(shape_ary));
+ NM_CONSERVATIVE(nm_register_value(initial_ary));
+ NM_CONSERVATIVE(nm_register_value(hash));
// Get the shape.
size_t dim;
size_t* shape = interpret_shape(shape_ary, &dim);
@@ -692,7 +1060,9 @@ static VALUE nm_init_new_version(int argc, VALUE* argv, VALUE self) {
dtype_sym = rb_hash_aref(hash, ID2SYM(nm_rb_dtype));
stype_sym = rb_hash_aref(hash, ID2SYM(nm_rb_stype));
capacity_num = rb_hash_aref(hash, ID2SYM(nm_rb_capacity));
+ NM_CONSERVATIVE(nm_register_value(capacity_num));
default_val_num = rb_hash_aref(hash, ID2SYM(nm_rb_default));
+ NM_CONSERVATIVE(nm_register_value(default_val_num));
}
// stype ||= :dense
@@ -724,6 +1094,10 @@ static VALUE nm_init_new_version(int argc, VALUE* argv, VALUE self) {
init = RARRAY_LEN(initial_ary) == 1 ? rubyobj_to_cval(rb_ary_entry(initial_ary, 0), dtype) : NULL;
else
init = rubyobj_to_cval(initial_ary, dtype);
+
+ if (dtype == nm::RUBYOBJ) {
+ nm_register_values(reinterpret_cast<VALUE*>(init), 1);
+ }
}
// capacity = h[:capacity] || 0
@@ -732,47 +1106,55 @@ static VALUE nm_init_new_version(int argc, VALUE* argv, VALUE self) {
}
if (!NIL_P(initial_ary)) {
- v = interpret_initial_value(initial_ary, dtype);
-
+
if (TYPE(initial_ary) == T_ARRAY) v_size = RARRAY_LEN(initial_ary);
else v_size = 1;
+
+ v = interpret_initial_value(initial_ary, dtype);
+
+ if (dtype == nm::RUBYOBJ) {
+ nm_register_values(reinterpret_cast<VALUE*>(v), v_size);
+ }
}
// :object matrices MUST be initialized.
else if (stype == nm::DENSE_STORE && dtype == nm::RUBYOBJ) {
// Pretend [nil] was passed for RUBYOBJ.
- v = ALLOC(VALUE);
+ v = NM_ALLOC(VALUE);
*(VALUE*)v = Qnil;
v_size = 1;
}
- NMATRIX* nmatrix;
+ NMATRIX* nmatrix;
UnwrapNMatrix(self, nmatrix);
nmatrix->stype = stype;
switch (stype) {
- case nm::DENSE_STORE:
- nmatrix->storage = (STORAGE*)nm_dense_storage_create(dtype, shape, dim, v, v_size);
- break;
+ case nm::DENSE_STORE:
+ nmatrix->storage = (STORAGE*)nm_dense_storage_create(dtype, shape, dim, v, v_size);
+ break;
- case nm::LIST_STORE:
- nmatrix->storage = (STORAGE*)nm_list_storage_create(dtype, shape, dim, init);
- break;
+ case nm::LIST_STORE:
+ nmatrix->storage = (STORAGE*)nm_list_storage_create(dtype, shape, dim, init);
+ break;
- case nm::YALE_STORE:
- nmatrix->storage = (STORAGE*)nm_yale_storage_create(dtype, shape, dim, capacity);
- nm_yale_storage_init((YALE_STORAGE*)(nmatrix->storage), init);
- break;
+ case nm::YALE_STORE:
+ nmatrix->storage = (STORAGE*)nm_yale_storage_create(dtype, shape, dim, capacity);
+ nm_yale_storage_init((YALE_STORAGE*)(nmatrix->storage), init);
+ break;
}
+ nm_register_storage(stype, nmatrix->storage);
+
// If we're not creating a dense, and an initial array was provided, use that and multi-slice-set
// to set the contents of the matrix right now.
if (stype != nm::DENSE_STORE && v_size > 1) {
- VALUE* slice_argv = ALLOCA_N(VALUE, dim);
- size_t* tmp_shape = ALLOC_N(size_t, dim);
+ VALUE* slice_argv = NM_ALLOCA_N(VALUE, dim);
+ nm_register_values(slice_argv, dim);
+ size_t* tmp_shape = NM_ALLOC_N(size_t, dim);
for (size_t m = 0; m < dim; ++m) {
slice_argv[m] = ID2SYM(nm_rb_mul); // :* -- full range
tmp_shape[m] = shape[m];
@@ -780,20 +1162,47 @@ static VALUE nm_init_new_version(int argc, VALUE* argv, VALUE self) {
SLICE* slice = get_slice(dim, dim, slice_argv, shape);
// Create a temporary dense matrix and use it to do a slice assignment on self.
- NMATRIX* tmp = nm_create(nm::DENSE_STORE, (STORAGE*)nm_dense_storage_create(dtype, tmp_shape, dim, v, v_size));
- volatile VALUE rb_tmp = Data_Wrap_Struct(CLASS_OF(self), nm_mark, nm_delete, tmp);
+ NMATRIX* tmp = nm_create(nm::DENSE_STORE, (STORAGE*)nm_dense_storage_create(dtype, tmp_shape, dim, v, v_size));
+ nm_register_nmatrix(tmp);
+ VALUE rb_tmp = Data_Wrap_Struct(CLASS_OF(self), nm_mark, nm_delete, tmp);
+ nm_unregister_nmatrix(tmp);
+ nm_register_value(rb_tmp);
if (stype == nm::YALE_STORE) nm_yale_storage_set(self, slice, rb_tmp);
else nm_list_storage_set(self, slice, rb_tmp);
free_slice(slice);
// We need to free v if it's not the same size as tmp -- because tmp will have made a copy instead.
- if (nm_storage_count_max_elements(tmp->storage) != v_size)
- xfree(v);
+ //if (nm_storage_count_max_elements(tmp->storage) != v_size)
+ // NM_FREE(v);
// nm_delete(tmp); // This seems to enrage the garbage collector (because rb_tmp is still available). It'd be better if we could force it to free immediately, but no sweat.
+
+ nm_unregister_value(rb_tmp);
+ nm_unregister_values(slice_argv, dim);
+ }
+
+ if (!NIL_P(initial_ary) && dtype == nm::RUBYOBJ) {
+ nm_unregister_values(reinterpret_cast<VALUE*>(v), v_size);
}
+ if (stype != nm::DENSE_STORE && dtype == nm::RUBYOBJ) {
+ nm_unregister_values(reinterpret_cast<VALUE*>(init), 1);
+ }
+
+ if (!NIL_P(hash)) {
+ NM_CONSERVATIVE(nm_unregister_value(capacity_num));
+ NM_CONSERVATIVE(nm_unregister_value(default_val_num));
+ }
+
+ NM_CONSERVATIVE(nm_unregister_value(shape_ary));
+ NM_CONSERVATIVE(nm_unregister_value(initial_ary));
+ NM_CONSERVATIVE(nm_unregister_value(hash));
+
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
+ nm_unregister_storage(stype, nmatrix->storage);
+
return self;
}
@@ -834,8 +1243,12 @@ static VALUE nm_init_new_version(int argc, VALUE* argv, VALUE self) {
* shortcuts.rb.
*/
static VALUE nm_init(int argc, VALUE* argv, VALUE nm) {
-
+ NM_CONSERVATIVE(nm_register_value(nm));
+ NM_CONSERVATIVE(nm_register_values(argv, argc));
+
if (argc <= 3) { // Call the new constructor unless all four arguments are given (or the 7-arg version is given)
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
+ NM_CONSERVATIVE(nm_unregister_value(nm));
return nm_init_new_version(argc, argv, nm);
}
@@ -854,16 +1267,20 @@ static VALUE nm_init(int argc, VALUE* argv, VALUE nm) {
// If there are 7 arguments and Yale, refer to a different init function with fewer sanity checks.
if (argc == 7) {
- if (stype == nm::YALE_STORE) {
- return nm_init_yale_from_old_yale(argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], nm);
+ if (stype == nm::YALE_STORE) {
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
+ NM_CONSERVATIVE(nm_unregister_value(nm));
+ return nm_init_yale_from_old_yale(argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], nm);
- } else {
- rb_raise(rb_eArgError, "Expected 2-4 arguments (or 7 for internal Yale creation)");
- }
+ } else {
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
+ NM_CONSERVATIVE(nm_unregister_value(nm));
+ rb_raise(rb_eArgError, "Expected 2-4 arguments (or 7 for internal Yale creation)");
+ }
}
- // 1: Array or Fixnum
- size_t dim;
+ // 1: Array or Fixnum
+ size_t dim;
size_t* shape = interpret_shape(argv[offset], &dim);
// 2-3: dtype
@@ -895,7 +1312,7 @@ static VALUE nm_init(int argc, VALUE* argv, VALUE nm) {
*/
if (dtype == nm::RUBYOBJ) {
// Pretend [nil] was passed for RUBYOBJ.
- init_val = ALLOC(VALUE);
+ init_val = NM_ALLOC(VALUE);
*(VALUE*)init_val = Qnil;
init_val_len = 1;
@@ -904,32 +1321,43 @@ static VALUE nm_init(int argc, VALUE* argv, VALUE nm) {
init_val = NULL;
}
} else if (stype == nm::LIST_STORE) {
- init_val = ALLOC_N(char, DTYPE_SIZES[dtype]);
+ init_val = NM_ALLOC_N(char, DTYPE_SIZES[dtype]);
std::memset(init_val, 0, DTYPE_SIZES[dtype]);
}
}
+ if (dtype == nm::RUBYOBJ) {
+ nm_register_values(reinterpret_cast<VALUE*>(init_val), init_val_len);
+ }
+
// TODO: Update to allow an array as the initial value.
- NMATRIX* nmatrix;
+ NMATRIX* nmatrix;
UnwrapNMatrix(nm, nmatrix);
nmatrix->stype = stype;
switch (stype) {
- case nm::DENSE_STORE:
- nmatrix->storage = (STORAGE*)nm_dense_storage_create(dtype, shape, dim, init_val, init_val_len);
- break;
+ case nm::DENSE_STORE:
+ nmatrix->storage = (STORAGE*)nm_dense_storage_create(dtype, shape, dim, init_val, init_val_len);
+ break;
- case nm::LIST_STORE:
- nmatrix->storage = (STORAGE*)nm_list_storage_create(dtype, shape, dim, init_val);
- break;
+ case nm::LIST_STORE:
+ nmatrix->storage = (STORAGE*)nm_list_storage_create(dtype, shape, dim, init_val);
+ break;
- case nm::YALE_STORE:
- nmatrix->storage = (STORAGE*)nm_yale_storage_create(dtype, shape, dim, init_cap);
- nm_yale_storage_init((YALE_STORAGE*)(nmatrix->storage), NULL);
- break;
+ case nm::YALE_STORE:
+ nmatrix->storage = (STORAGE*)nm_yale_storage_create(dtype, shape, dim, init_cap);
+ nm_yale_storage_init((YALE_STORAGE*)(nmatrix->storage), NULL);
+ break;
+ }
+
+ if (dtype == nm::RUBYOBJ) {
+ nm_unregister_values(reinterpret_cast<VALUE*>(init_val), init_val_len);
}
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
+ NM_CONSERVATIVE(nm_unregister_value(nm));
+
return nm;
}
@@ -938,13 +1366,18 @@ static VALUE nm_init(int argc, VALUE* argv, VALUE nm) {
* Helper for nm_cast which uses the C types instead of the Ruby objects. Called by nm_cast.
*/
NMATRIX* nm_cast_with_ctype_args(NMATRIX* self, nm::stype_t new_stype, nm::dtype_t new_dtype, void* init_ptr) {
- NMATRIX* lhs = ALLOC(NMATRIX);
+
+ nm_register_nmatrix(self);
+
+ NMATRIX* lhs = NM_ALLOC(NMATRIX);
lhs->stype = new_stype;
// Copy the storage
CAST_TABLE(cast_copy);
lhs->storage = cast_copy[lhs->stype][self->stype](self->storage, new_dtype, init_ptr);
+ nm_unregister_nmatrix(self);
+
return lhs;
}
@@ -957,6 +1390,9 @@ NMATRIX* nm_cast_with_ctype_args(NMATRIX* self, nm::stype_t new_stype, nm::dtype
* Copy constructor for changing dtypes and stypes.
*/
VALUE nm_cast(VALUE self, VALUE new_stype_symbol, VALUE new_dtype_symbol, VALUE init) {
+ NM_CONSERVATIVE(nm_register_value(self));
+ NM_CONSERVATIVE(nm_register_value(init));
+
nm::dtype_t new_dtype = nm_dtype_from_rbsymbol(new_dtype_symbol);
nm::stype_t new_stype = nm_stype_from_rbsymbol(new_stype_symbol);
@@ -965,16 +1401,27 @@ VALUE nm_cast(VALUE self, VALUE new_stype_symbol, VALUE new_dtype_symbol, VALUE
UnwrapNMatrix( self, rhs );
- void* init_ptr = ALLOCA_N(char, DTYPE_SIZES[new_dtype]);
+ void* init_ptr = NM_ALLOCA_N(char, DTYPE_SIZES[new_dtype]);
rubyval_to_cval(init, new_dtype, init_ptr);
- return Data_Wrap_Struct(CLASS_OF(self), nm_mark, nm_delete, nm_cast_with_ctype_args(rhs, new_stype, new_dtype, init_ptr));
+ NMATRIX* m = nm_cast_with_ctype_args(rhs, new_stype, new_dtype, init_ptr);
+ nm_register_nmatrix(m);
+
+ VALUE to_return = Data_Wrap_Struct(CLASS_OF(self), nm_mark, nm_delete, m);
+
+ nm_unregister_nmatrix(m);
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ NM_CONSERVATIVE(nm_unregister_value(init));
+ return to_return;
+
}
/*
* Copy constructor for transposing.
*/
static VALUE nm_init_transposed(VALUE self) {
+ NM_CONSERVATIVE(nm_register_value(self));
+
static STORAGE* (*storage_copy_transposed[nm::NUM_STYPES])(const STORAGE* rhs_base) = {
nm_dense_storage_copy_transposed,
nm_list_storage_copy_transposed,
@@ -984,19 +1431,30 @@ static VALUE nm_init_transposed(VALUE self) {
NMATRIX* lhs = nm_create( NM_STYPE(self),
storage_copy_transposed[NM_STYPE(self)]( NM_STORAGE(self) )
);
+ nm_register_nmatrix(lhs);
+ VALUE to_return = Data_Wrap_Struct(CLASS_OF(self), nm_mark, nm_delete, lhs);
- return Data_Wrap_Struct(CLASS_OF(self), nm_mark, nm_delete, lhs);
+ nm_unregister_nmatrix(lhs);
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ return to_return;
}
/*
* Copy constructor for no change of dtype or stype (used for #initialize_copy hook).
*/
static VALUE nm_init_copy(VALUE copy, VALUE original) {
+ NM_CONSERVATIVE(nm_register_value(copy));
+ NM_CONSERVATIVE(nm_register_value(original));
+
NMATRIX *lhs, *rhs;
CheckNMatrixType(original);
- if (copy == original) return copy;
+ if (copy == original) {
+ NM_CONSERVATIVE(nm_unregister_value(copy));
+ NM_CONSERVATIVE(nm_unregister_value(original));
+ return copy;
+ }
UnwrapNMatrix( original, rhs );
UnwrapNMatrix( copy, lhs );
@@ -1007,25 +1465,24 @@ static VALUE nm_init_copy(VALUE copy, VALUE original) {
CAST_TABLE(ttable);
lhs->storage = ttable[lhs->stype][rhs->stype](rhs->storage, rhs->storage->dtype, NULL);
+ NM_CONSERVATIVE(nm_unregister_value(copy));
+ NM_CONSERVATIVE(nm_unregister_value(original));
+
return copy;
}
/*
- * Get major, minor, and release components of NMatrix::VERSION. Store in function parameters.
+ * Get major, minor, and release components of NMatrix::VERSION. Store in function parameters. Doesn't get
+ * the "pre" field currently (beta1/rc1/etc).
*/
static void get_version_info(uint16_t& major, uint16_t& minor, uint16_t& release) {
// Get VERSION and split it on periods. Result is an Array.
- VALUE version = rb_funcall(rb_const_get(cNMatrix, rb_intern("VERSION")), rb_intern("split"), 1, rb_str_new_cstr("."));
- VALUE* ary = RARRAY_PTR(version); // major, minor, and release
+ VALUE cVersion = rb_const_get(cNMatrix, rb_intern("VERSION"));
// Convert each to an integer
- VALUE maj = rb_funcall(ary[0], rb_intern("to_i"), 0);
- VALUE min = rb_funcall(ary[1], rb_intern("to_i"), 0);
- VALUE rel = rb_funcall(ary[2], rb_intern("to_i"), 0);
-
- major = static_cast<uint16_t>(nm::RubyObject(maj));
- minor = static_cast<uint16_t>(nm::RubyObject(min));
- release = static_cast<uint16_t>(nm::RubyObject(rel));
+ major = FIX2INT(rb_const_get(cVersion, rb_intern("MAJOR")));
+ minor = FIX2INT(rb_const_get(cVersion, rb_intern("MINOR")));
+ release = FIX2INT(rb_const_get(cVersion, rb_intern("TINY")));
}
@@ -1055,12 +1512,40 @@ static nm::symm_t interpret_symm(VALUE symm) {
void read_padded_shape(std::ifstream& f, size_t dim, size_t* shape) {
- nm::read_padded_shape(f, dim, shape);
+ size_t bytes_read = 0;
+
+ // Read shape
+ for (size_t i = 0; i < dim; ++i) {
+ size_t s;
+ f.read(reinterpret_cast<char*>(&s), sizeof(size_t));
+ shape[i] = s;
+
+ bytes_read += sizeof(size_t);
+ }
+
+ // Ignore padding
+ f.ignore(bytes_read % 8);
}
void write_padded_shape(std::ofstream& f, size_t dim, size_t* shape) {
- nm::write_padded_shape(f, dim, shape);
+ size_t bytes_written = 0;
+
+ // Write shape
+ for (size_t i = 0; i < dim; ++i) {
+ size_t s = shape[i];
+ f.write(reinterpret_cast<const char*>(&s), sizeof(size_t));
+
+ bytes_written += sizeof(size_t);
+ }
+
+ // Pad with zeros
+ size_t zero = 0;
+ while (bytes_written % 8) {
+ f.write(reinterpret_cast<const char*>(&zero), sizeof(size_t));
+
+ bytes_written += sizeof(IType);
+ }
}
@@ -1116,6 +1601,10 @@ static VALUE nm_write(int argc, VALUE* argv, VALUE self) {
if (argc < 1 || argc > 2) {
rb_raise(rb_eArgError, "Expected one or two arguments");
}
+
+ NM_CONSERVATIVE(nm_register_values(argv, argc));
+ NM_CONSERVATIVE(nm_register_value(self));
+
VALUE file = argv[0],
symm = argc == 1 ? Qnil : argv[1];
@@ -1125,6 +1614,8 @@ static VALUE nm_write(int argc, VALUE* argv, VALUE self) {
nm::symm_t symm_ = interpret_symm(symm);
if (nmatrix->storage->dtype == nm::RUBYOBJ) {
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
+ NM_CONSERVATIVE(nm_unregister_value(self));
rb_raise(rb_eNotImpError, "Ruby Object writing is not implemented yet");
}
@@ -1137,8 +1628,15 @@ static VALUE nm_write(int argc, VALUE* argv, VALUE self) {
//FIXME: Cast the matrix to the smallest possible index type. Write that in the place of IType.
// Check arguments before starting to write.
- if (nmatrix->stype == nm::LIST_STORE) rb_raise(nm_eStorageTypeError, "cannot save list matrix; cast to yale or dense first");
+ if (nmatrix->stype == nm::LIST_STORE) {
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ rb_raise(nm_eStorageTypeError, "cannot save list matrix; cast to yale or dense first");
+ }
if (symm_ != nm::NONSYMM) {
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
+ NM_CONSERVATIVE(nm_unregister_value(self));
+
if (dim != 2) rb_raise(rb_eArgError, "symmetry/triangularity not defined for a non-2D matrix");
if (nmatrix->storage->shape[0] != nmatrix->storage->shape[1])
rb_raise(rb_eArgError, "symmetry/triangularity not defined for a non-square matrix");
@@ -1185,6 +1683,9 @@ static VALUE nm_write(int argc, VALUE* argv, VALUE self) {
f.close();
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
+ NM_CONSERVATIVE(nm_unregister_value(self));
+
return Qtrue;
}
@@ -1202,6 +1703,9 @@ static VALUE nm_write(int argc, VALUE* argv, VALUE self) {
static VALUE nm_read(int argc, VALUE* argv, VALUE self) {
using std::ifstream;
+ NM_CONSERVATIVE(nm_register_values(argv, argc));
+ NM_CONSERVATIVE(nm_register_value(self));
+
VALUE file, force_;
// Read the arguments
@@ -1210,6 +1714,8 @@ static VALUE nm_read(int argc, VALUE* argv, VALUE self) {
if (!RB_FILE_EXISTS(file)) { // FIXME: Errno::ENOENT
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
+ NM_CONSERVATIVE(nm_unregister_value(self));
rb_raise(rb_get_errno_exc("ENOENT"), "%s", RSTRING_PTR(file));
}
@@ -1230,9 +1736,11 @@ static VALUE nm_read(int argc, VALUE* argv, VALUE self) {
int ver = major * 10000 + minor * 100 + release,
fver = fmajor * 10000 + fminor * 100 + release;
if (fver > ver && force == false) {
- rb_raise(rb_eIOError, "File was created in newer version of NMatrix than current");
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ rb_raise(rb_eIOError, "File was created in newer version of NMatrix than current (%u.%u.%u)", fmajor, fminor, frelease);
}
- if (null16 != 0) fprintf(stderr, "Warning: Expected zero padding was not zero\n");
+ if (null16 != 0) rb_warn("nm_read: Expected zero padding was not zero (0)\n");
uint8_t dt, st, it, sm;
uint16_t dim;
@@ -1245,19 +1753,20 @@ static VALUE nm_read(int argc, VALUE* argv, VALUE self) {
f.read(reinterpret_cast<char*>(&null16), sizeof(uint16_t));
f.read(reinterpret_cast<char*>(&dim), sizeof(uint16_t));
- if (null16 != 0) fprintf(stderr, "Warning: Expected zero padding was not zero\n");
+ if (null16 != 0) rb_warn("nm_read: Expected zero padding was not zero (1)");
nm::stype_t stype = static_cast<nm::stype_t>(st);
nm::dtype_t dtype = static_cast<nm::dtype_t>(dt);
nm::symm_t symm = static_cast<nm::symm_t>(sm);
//nm::itype_t itype = static_cast<nm::itype_t>(it);
// READ NEXT FEW 64-BIT BLOCKS
- size_t* shape = ALLOC_N(size_t, dim);
+ size_t* shape = NM_ALLOC_N(size_t, dim);
read_padded_shape(f, dim, shape);
STORAGE* s;
if (stype == nm::DENSE_STORE) {
s = nm_dense_storage_create(dtype, shape, dim, NULL, 0);
+ nm_register_storage(stype, s);
read_padded_dense_elements(f, reinterpret_cast<DENSE_STORAGE*>(s), symm, dtype);
@@ -1270,8 +1779,12 @@ static VALUE nm_read(int argc, VALUE* argv, VALUE self) {
s = nm_yale_storage_create(dtype, shape, dim, length); // set length as init capacity
+ nm_register_storage(stype, s);
+
read_padded_yale_elements(f, reinterpret_cast<YALE_STORAGE*>(s), length, symm, dtype);
} else {
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
+ NM_CONSERVATIVE(nm_unregister_value(self));
rb_raise(nm_eStorageTypeError, "please convert to yale or dense before saving");
}
@@ -1280,10 +1793,18 @@ static VALUE nm_read(int argc, VALUE* argv, VALUE self) {
// Return the appropriate matrix object (Ruby VALUE)
// FIXME: This should probably return CLASS_OF(self) instead of cNMatrix, but I don't know how that works for
// FIXME: class methods.
+ nm_register_nmatrix(nm);
+ VALUE to_return = Data_Wrap_Struct(cNMatrix, nm_mark, nm_delete, nm);
+
+ nm_unregister_nmatrix(nm);
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ nm_unregister_storage(stype, s);
+
switch(stype) {
case nm::DENSE_STORE:
case nm::YALE_STORE:
- return Data_Wrap_Struct(cNMatrix, nm_mark, nm_delete, nm);
+ return to_return;
default: // this case never occurs (due to earlier rb_raise)
return Qnil;
}
@@ -1321,7 +1842,7 @@ static VALUE nm_init_yale_from_old_yale(VALUE shape, VALUE dtype, VALUE ia, VALU
*/
static VALUE nm_is_ref(VALUE self) {
if (NM_SRC(self) == NM_STORAGE(self)) return Qfalse;
- else return Qtrue;
+ return Qtrue;
}
/*
@@ -1340,7 +1861,8 @@ static VALUE nm_mget(int argc, VALUE* argv, VALUE self) {
nm_list_storage_get,
nm_yale_storage_get
};
- return nm_xslice(argc, argv, ttable[NM_STYPE(self)], nm_delete, self);
+ nm::stype_t stype = NM_STYPE(self);
+ return nm_xslice(argc, argv, ttable[stype], nm_delete, self);
}
/*
@@ -1359,7 +1881,8 @@ static VALUE nm_mref(int argc, VALUE* argv, VALUE self) {
nm_list_storage_ref,
nm_yale_storage_ref
};
- return nm_xslice(argc, argv, ttable[NM_STYPE(self)], nm_delete_ref, self);
+ nm::stype_t stype = NM_STYPE(self);
+ return nm_xslice(argc, argv, ttable[stype], nm_delete_ref, self);
}
/*
@@ -1372,11 +1895,17 @@ static VALUE nm_mref(int argc, VALUE* argv, VALUE self) {
* n[3,3] = n[2,3] = 5.0
*/
static VALUE nm_mset(int argc, VALUE* argv, VALUE self) {
+
size_t dim = NM_DIM(self); // last arg is the value
+ VALUE to_return = Qnil;
+
if ((size_t)(argc) > NM_DIM(self)+1) {
- rb_raise(rb_eArgError, "wrong number of arguments (%d for %u)", argc, effective_dim(NM_STORAGE(self))+1);
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %lu)", argc, effective_dim(NM_STORAGE(self))+1);
} else {
+ NM_CONSERVATIVE(nm_register_value(self));
+ NM_CONSERVATIVE(nm_register_values(argv, argc));
+
SLICE* slice = get_slice(dim, argc-1, argv, NM_STORAGE(self)->shape);
static void (*ttable[nm::NUM_STYPES])(VALUE, SLICE*, VALUE) = {
@@ -1389,9 +1918,13 @@ static VALUE nm_mset(int argc, VALUE* argv, VALUE self) {
free_slice(slice);
- return argv[argc-1];
+ to_return = argv[argc-1];
+
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
}
- return Qnil;
+
+ return to_return;
}
/*
@@ -1402,30 +1935,50 @@ static VALUE nm_mset(int argc, VALUE* argv, VALUE self) {
* The two matrices must be of the same stype (for now). If dtype differs, an upcast will occur.
*/
static VALUE nm_multiply(VALUE left_v, VALUE right_v) {
+ NM_CONSERVATIVE(nm_register_value(left_v));
+ NM_CONSERVATIVE(nm_register_value(right_v));
+
NMATRIX *left, *right;
UnwrapNMatrix( left_v, left );
- if (NM_RUBYVAL_IS_NUMERIC(right_v))
+ if (NM_RUBYVAL_IS_NUMERIC(right_v)) {
+ NM_CONSERVATIVE(nm_unregister_value(left_v));
+ NM_CONSERVATIVE(nm_unregister_value(right_v));
return matrix_multiply_scalar(left, right_v);
+ }
- else if (TYPE(right_v) == T_ARRAY)
+ else if (TYPE(right_v) == T_ARRAY) {
+ NM_CONSERVATIVE(nm_unregister_value(left_v));
+ NM_CONSERVATIVE(nm_unregister_value(right_v));
rb_raise(rb_eNotImpError, "please convert array to nx1 or 1xn NMatrix first");
+ }
else { // both are matrices (probably)
CheckNMatrixType(right_v);
UnwrapNMatrix( right_v, right );
- if (left->storage->shape[1] != right->storage->shape[0])
+ if (left->storage->shape[1] != right->storage->shape[0]) {
+ NM_CONSERVATIVE(nm_unregister_value(left_v));
+ NM_CONSERVATIVE(nm_unregister_value(right_v));
rb_raise(rb_eArgError, "incompatible dimensions");
+ }
- if (left->stype != right->stype)
+ if (left->stype != right->stype) {
+ NM_CONSERVATIVE(nm_unregister_value(left_v));
+ NM_CONSERVATIVE(nm_unregister_value(right_v));
rb_raise(rb_eNotImpError, "matrices must have same stype");
+ }
+ NM_CONSERVATIVE(nm_unregister_value(left_v));
+ NM_CONSERVATIVE(nm_unregister_value(right_v));
return matrix_multiply(left, right);
}
+ NM_CONSERVATIVE(nm_unregister_value(left_v));
+ NM_CONSERVATIVE(nm_unregister_value(right_v));
+
return Qnil;
}
@@ -1452,13 +2005,17 @@ static VALUE nm_dim(VALUE self) {
* Get the shape (dimensions) of a matrix.
*/
static VALUE nm_shape(VALUE self) {
+ NM_CONSERVATIVE(nm_register_value(self));
STORAGE* s = NM_STORAGE(self);
// Copy elements into a VALUE array and then use those to create a Ruby array with rb_ary_new4.
- VALUE* shape = ALLOCA_N(VALUE, s->dim);
+ VALUE* shape = NM_ALLOCA_N(VALUE, s->dim);
+ nm_register_values(shape, s->dim);
for (size_t index = 0; index < s->dim; ++index)
shape[index] = INT2FIX(s->shape[index]);
-
+
+ nm_unregister_values(shape, s->dim);
+ NM_CONSERVATIVE(nm_unregister_value(self));
return rb_ary_new4(s->dim, shape);
}
@@ -1470,13 +2027,17 @@ static VALUE nm_shape(VALUE self) {
* Get the offset (slice position) of a matrix. Typically all zeros, unless you have a reference slice.
*/
static VALUE nm_offset(VALUE self) {
+ NM_CONSERVATIVE(nm_register_value(self));
STORAGE* s = NM_STORAGE(self);
// Copy elements into a VALUE array and then use those to create a Ruby array with rb_ary_new4.
- VALUE* offset = ALLOCA_N(VALUE, s->dim);
+ VALUE* offset = NM_ALLOCA_N(VALUE, s->dim);
+ nm_register_values(offset, s->dim);
for (size_t index = 0; index < s->dim; ++index)
offset[index] = INT2FIX(s->offset[index]);
+ nm_unregister_values(offset, s->dim);
+ NM_CONSERVATIVE(nm_unregister_value(self));
return rb_ary_new4(s->dim, offset);
}
@@ -1490,13 +2051,20 @@ static VALUE nm_offset(VALUE self) {
static VALUE nm_supershape(VALUE self) {
STORAGE* s = NM_STORAGE(self);
- if (s->src == s) return nm_shape(self); // easy case (not a slice)
+ if (s->src == s) {
+ return nm_shape(self); // easy case (not a slice)
+ }
else s = s->src;
- VALUE* shape = ALLOCA_N(VALUE, s->dim);
+ NM_CONSERVATIVE(nm_register_value(self));
+
+ VALUE* shape = NM_ALLOCA_N(VALUE, s->dim);
+ nm_register_values(shape, s->dim);
for (size_t index = 0; index < s->dim; ++index)
shape[index] = INT2FIX(s->shape[index]);
+ nm_unregister_values(shape, s->dim);
+ NM_CONSERVATIVE(nm_unregister_value(self));
return rb_ary_new4(s->dim, shape);
}
@@ -1507,8 +2075,10 @@ static VALUE nm_supershape(VALUE self) {
* Get the storage type (stype) of a matrix, e.g., :yale, :dense, or :list.
*/
static VALUE nm_stype(VALUE self) {
- ID stype = rb_intern(STYPE_NAMES[NM_STYPE(self)]);
- return ID2SYM(stype);
+ NM_CONSERVATIVE(nm_register_value(self));
+ VALUE stype = ID2SYM(rb_intern(STYPE_NAMES[NM_STYPE(self)]));
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ return stype;
}
/*
@@ -1550,11 +2120,18 @@ static VALUE nm_effective_dim(VALUE self) {
*/
static VALUE nm_xslice(int argc, VALUE* argv, void* (*slice_func)(const STORAGE*, SLICE*), void (*delete_func)(NMATRIX*), VALUE self) {
VALUE result = Qnil;
+
STORAGE* s = NM_STORAGE(self);
if (NM_DIM(self) < (size_t)(argc)) {
- rb_raise(rb_eArgError, "wrong number of arguments (%d for %u)", argc, effective_dim(s));
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %lu)", argc, effective_dim(s));
} else {
+
+ NM_CONSERVATIVE(nm_register_values(argv, argc));
+ NM_CONSERVATIVE(nm_register_value(self));
+
+ nm_register_value(result);
+
SLICE* slice = get_slice(NM_DIM(self), argc, argv, s->shape);
if (slice->single) {
@@ -1569,16 +2146,21 @@ static VALUE nm_xslice(int argc, VALUE* argv, void* (*slice_func)(const STORAGE*
} else {
- NMATRIX* mat = ALLOC(NMATRIX);
+ NMATRIX* mat = NM_ALLOC(NMATRIX);
mat->stype = NM_STYPE(self);
mat->storage = (STORAGE*)((*slice_func)( s, slice ));
-
+ nm_register_nmatrix(mat);
result = Data_Wrap_Struct(CLASS_OF(self), nm_mark, delete_func, mat);
+ nm_unregister_nmatrix(mat);
}
free_slice(slice);
}
+ nm_unregister_value(result);
+ NM_CONSERVATIVE(nm_unregister_values(argv, argc));
+ NM_CONSERVATIVE(nm_unregister_value(self));
+
return result;
}
@@ -1586,13 +2168,49 @@ static VALUE nm_xslice(int argc, VALUE* argv, void* (*slice_func)(const STORAGE*
// Helper Functions //
//////////////////////
+static VALUE unary_op(nm::unaryop_t op, VALUE self) {
+ NM_CONSERVATIVE(nm_register_value(self));
+ NMATRIX* left;
+ UnwrapNMatrix(self, left);
+ std::string sym;
+
+ switch(left->stype) {
+ case nm::DENSE_STORE:
+ sym = "__dense_unary_" + nm::UNARYOPS[op] + "__";
+ break;
+ case nm::YALE_STORE:
+ sym = "__yale_unary_" + nm::UNARYOPS[op] + "__";
+ break;
+ case nm::LIST_STORE:
+ sym = "__list_unary_" + nm::UNARYOPS[op] + "__";
+ break;
+ }
+
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ return rb_funcall(self, rb_intern(sym.c_str()), 0);
+}
+
+static void check_dims_and_shape(VALUE left_val, VALUE right_val) {
+ // Check that the left- and right-hand sides have the same dimensionality.
+ if (NM_DIM(left_val) != NM_DIM(right_val)) {
+ rb_raise(rb_eArgError, "The left- and right-hand sides of the operation must have the same dimensionality.");
+ }
+ // Check that the left- and right-hand sides have the same shape.
+ if (memcmp(&NM_SHAPE(left_val, 0), &NM_SHAPE(right_val, 0), sizeof(size_t) * NM_DIM(left_val)) != 0) {
+ rb_raise(rb_eArgError, "The left- and right-hand sides of the operation must have the same shape.");
+ }
+}
+
static VALUE elementwise_op(nm::ewop_t op, VALUE left_val, VALUE right_val) {
- NMATRIX* left;
- NMATRIX* result;
+ NM_CONSERVATIVE(nm_register_value(left_val));
+ NM_CONSERVATIVE(nm_register_value(right_val));
- CheckNMatrixType(left_val);
- UnwrapNMatrix(left_val, left);
+ NMATRIX* left;
+ NMATRIX* result;
+
+ CheckNMatrixType(left_val);
+ UnwrapNMatrix(left_val, left);
if (TYPE(right_val) != T_DATA || (RDATA(right_val)->dfree != (RUBY_DATA_FUNC)nm_delete && RDATA(right_val)->dfree != (RUBY_DATA_FUNC)nm_delete_ref)) {
// This is a matrix-scalar element-wise operation.
@@ -1608,21 +2226,18 @@ static VALUE elementwise_op(nm::ewop_t op, VALUE left_val, VALUE right_val) {
sym = "__list_scalar_" + nm::EWOP_NAMES[op] + "__";
break;
default:
+ NM_CONSERVATIVE(nm_unregister_value(left_val));
+ NM_CONSERVATIVE(nm_unregister_value(right_val));
rb_raise(rb_eNotImpError, "unknown storage type requested scalar element-wise operation");
}
- return rb_funcall(left_val, rb_intern(sym.c_str()), 1, right_val);
+ VALUE symv = rb_intern(sym.c_str());
+ NM_CONSERVATIVE(nm_unregister_value(left_val));
+ NM_CONSERVATIVE(nm_unregister_value(right_val));
+ return rb_funcall(left_val, symv, 1, right_val);
} else {
- // Check that the left- and right-hand sides have the same dimensionality.
- if (NM_DIM(left_val) != NM_DIM(right_val)) {
- rb_raise(rb_eArgError, "The left- and right-hand sides of the operation must have the same dimensionality.");
- }
-
- // Check that the left- and right-hand sides have the same shape.
- if (memcmp(&NM_SHAPE(left_val, 0), &NM_SHAPE(right_val, 0), sizeof(size_t) * NM_DIM(left_val)) != 0) {
- rb_raise(rb_eArgError, "The left- and right-hand sides of the operation must have the same shape.");
- }
+ check_dims_and_shape(left_val, right_val);
NMATRIX* right;
UnwrapNMatrix(right_val, right);
@@ -1641,16 +2256,99 @@ static VALUE elementwise_op(nm::ewop_t op, VALUE left_val, VALUE right_val) {
sym = "__list_elementwise_" + nm::EWOP_NAMES[op] + "__";
break;
default:
+ NM_CONSERVATIVE(nm_unregister_value(left_val));
+ NM_CONSERVATIVE(nm_unregister_value(right_val));
rb_raise(rb_eNotImpError, "unknown storage type requested element-wise operation");
}
- return rb_funcall(left_val, rb_intern(sym.c_str()), 1, right_val);
+
+ VALUE symv = rb_intern(sym.c_str());
+ NM_CONSERVATIVE(nm_unregister_value(left_val));
+ NM_CONSERVATIVE(nm_unregister_value(right_val));
+ return rb_funcall(left_val, symv, 1, right_val);
} else {
+ NM_CONSERVATIVE(nm_unregister_value(left_val));
+ NM_CONSERVATIVE(nm_unregister_value(right_val));
rb_raise(rb_eArgError, "Element-wise operations are not currently supported between matrices with differing stypes.");
}
}
- return Data_Wrap_Struct(CLASS_OF(left_val), nm_mark, nm_delete, result);
+ NM_CONSERVATIVE(nm_unregister_value(left_val));
+ NM_CONSERVATIVE(nm_unregister_value(right_val));
+ return Data_Wrap_Struct(CLASS_OF(left_val), nm_mark, nm_delete, result);
+}
+
+static VALUE noncom_elementwise_op(nm::noncom_ewop_t op, VALUE self, VALUE other, VALUE flip) {
+
+ NM_CONSERVATIVE(nm_register_value(self));
+ NM_CONSERVATIVE(nm_register_value(other));
+
+ NMATRIX* self_nm;
+ NMATRIX* result;
+
+ CheckNMatrixType(self);
+ UnwrapNMatrix(self, self_nm);
+
+ if (TYPE(other) != T_DATA || (RDATA(other)->dfree != (RUBY_DATA_FUNC)nm_delete && RDATA(other)->dfree != (RUBY_DATA_FUNC)nm_delete_ref)) {
+ // This is a matrix-scalar element-wise operation.
+ std::string sym;
+ switch(self_nm->stype) {
+ case nm::DENSE_STORE:
+ sym = "__dense_scalar_" + nm::NONCOM_EWOP_NAMES[op] + "__";
+ break;
+ case nm::YALE_STORE:
+ sym = "__yale_scalar_" + nm::NONCOM_EWOP_NAMES[op] + "__";
+ break;
+ case nm::LIST_STORE:
+ sym = "__list_scalar_" + nm::NONCOM_EWOP_NAMES[op] + "__";
+ break;
+ default:
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ NM_CONSERVATIVE(nm_unregister_value(other));
+ rb_raise(rb_eNotImpError, "unknown storage type requested scalar element-wise operation");
+ }
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ NM_CONSERVATIVE(nm_unregister_value(other));
+ return rb_funcall(self, rb_intern(sym.c_str()), 2, other, flip);
+
+ } else {
+
+ check_dims_and_shape(self, other);
+
+ NMATRIX* other_nm;
+ UnwrapNMatrix(other, other_nm);
+
+ if (self_nm->stype == other_nm->stype) {
+ std::string sym;
+
+ switch(self_nm->stype) {
+ case nm::DENSE_STORE:
+ sym = "__dense_elementwise_" + nm::NONCOM_EWOP_NAMES[op] + "__";
+ break;
+ case nm::YALE_STORE:
+ sym = "__yale_elementwise_" + nm::NONCOM_EWOP_NAMES[op] + "__";
+ break;
+ case nm::LIST_STORE:
+ sym = "__list_elementwise_" + nm::NONCOM_EWOP_NAMES[op] + "__";
+ break;
+ default:
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ NM_CONSERVATIVE(nm_unregister_value(other));
+ rb_raise(rb_eNotImpError, "unknown storage type requested element-wise operation");
+ }
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ NM_CONSERVATIVE(nm_unregister_value(other));
+ return rb_funcall(self, rb_intern(sym.c_str()), 2, other, flip);
+
+ } else {
+ nm_unregister_value(self);
+ nm_unregister_value(other);
+ rb_raise(rb_eArgError, "Element-wise operations are not currently supported between matrices with differing stypes.");
+ }
+ }
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ NM_CONSERVATIVE(nm_unregister_value(other));
+ return Data_Wrap_Struct(CLASS_OF(self), nm_mark, nm_delete, result);
}
/*
@@ -1664,11 +2362,13 @@ bool is_ref(const NMATRIX* matrix) {
* Helper function for nm_symmetric and nm_hermitian.
*/
static VALUE is_symmetric(VALUE self, bool hermitian) {
+ NM_CONSERVATIVE(nm_register_value(self));
+
NMATRIX* m;
UnwrapNMatrix(self, m);
if (m->storage->shape[0] == m->storage->shape[1] and m->storage->dim == 2) {
- if (NM_STYPE(self) == nm::DENSE_STORE) {
+ if (NM_STYPE(self) == nm::DENSE_STORE) {
if (hermitian) {
nm_dense_storage_is_hermitian((DENSE_STORAGE*)(m->storage), m->storage->shape[0]);
@@ -1678,11 +2378,12 @@ static VALUE is_symmetric(VALUE self, bool hermitian) {
} else {
// TODO: Implement, at the very least, yale_is_symmetric. Model it after yale/transp.template.c.
+ NM_CONSERVATIVE(nm_unregister_value(self));
rb_raise(rb_eNotImpError, "symmetric? and hermitian? only implemented for dense currently");
}
}
-
+ NM_CONSERVATIVE(nm_unregister_value(self));
return Qfalse;
}
@@ -1724,9 +2425,10 @@ nm::dtype_t nm_dtype_min_fixnum(int64_t v) {
* Helper for nm_dtype_min(), handling rationals.
*/
nm::dtype_t nm_dtype_min_rational(VALUE vv) {
- nm::Rational128* v = ALLOCA_N(nm::Rational128, 1);
+ NM_CONSERVATIVE(nm_register_value(vv));
+ nm::Rational128* v = NM_ALLOCA_N(nm::Rational128, 1);
rubyval_to_cval(vv, nm::RATIONAL128, v);
-
+ NM_CONSERVATIVE(nm_unregister_value(vv));
int64_t i = std::max(std::abs(v->n), v->d);
if (i <= SHRT_MAX) return nm::INT16;
else if (i <= INT_MAX) return nm::INT32;
@@ -1848,6 +2550,8 @@ nm::dtype_t nm_dtype_guess(VALUE v) {
* accessing some part of a matrix.
*/
static SLICE* get_slice(size_t dim, int argc, VALUE* arg, size_t* shape) {
+ NM_CONSERVATIVE(nm_register_values(arg, argc));
+
VALUE beg, end;
int excl;
@@ -1875,15 +2579,17 @@ static SLICE* get_slice(size_t dim, int argc, VALUE* arg, size_t* shape) {
slice->coords[r] = 0;
slice->lengths[r] = shape[r];
slice->single = false;
+ t++;
} else if (TYPE(arg[t]) == T_HASH) { // 3:5 notation (inclusive)
VALUE begin_end = rb_funcall(v, rb_intern("shift"), 0); // rb_hash_shift
+ nm_register_value(begin_end);
slice->coords[r] = FIX2UINT(rb_ary_entry(begin_end, 0));
slice->lengths[r] = FIX2UINT(rb_ary_entry(begin_end, 1)) - slice->coords[r];
if (RHASH_EMPTY_P(v)) t++; // go on to the next
-
slice->single = false;
+ nm_unregister_value(begin_end);
} else if (CLASS_OF(v) == rb_cRange) {
rb_range_values(arg[t], &beg, &end, &excl);
@@ -1896,13 +2602,17 @@ static SLICE* get_slice(size_t dim, int argc, VALUE* arg, size_t* shape) {
t++;
} else {
+ NM_CONSERVATIVE(nm_unregister_values(arg, argc));
rb_raise(rb_eArgError, "expected Fixnum, Range, or Hash for slice component instead of %s", rb_obj_classname(v));
}
- if (slice->coords[r] > shape[r] || slice->coords[r] + slice->lengths[r] > shape[r])
- rb_raise(rb_eRangeError, "slice is larger than matrix in dimension %u (slice component %u)", r, t);
+ if (slice->coords[r] > shape[r] || slice->coords[r] + slice->lengths[r] > shape[r]) {
+ NM_CONSERVATIVE(nm_unregister_values(arg, argc));
+ rb_raise(rb_eRangeError, "slice is larger than matrix in dimension %lu (slice component %lu)", r, t);
+ }
}
+ NM_CONSERVATIVE(nm_unregister_values(arg, argc));
return slice;
}
@@ -1960,12 +2670,14 @@ static nm::dtype_t interpret_dtype(int argc, VALUE* argv, nm::stype_t stype) {
* Convert an Ruby value or an array of Ruby values into initial C values.
*/
static void* interpret_initial_value(VALUE arg, nm::dtype_t dtype) {
+ NM_CONSERVATIVE(nm_register_value(arg));
+
unsigned int index;
void* init_val;
if (TYPE(arg) == T_ARRAY) {
// Array
- init_val = ALLOC_N(char, DTYPE_SIZES[dtype] * RARRAY_LEN(arg));
+ init_val = NM_ALLOC_N(char, DTYPE_SIZES[dtype] * RARRAY_LEN(arg));
NM_CHECK_ALLOC(init_val);
for (index = 0; index < RARRAY_LEN(arg); ++index) {
rubyval_to_cval(RARRAY_PTR(arg)[index], dtype, (char*)init_val + (index * DTYPE_SIZES[dtype]));
@@ -1976,6 +2688,7 @@ static void* interpret_initial_value(VALUE arg, nm::dtype_t dtype) {
init_val = rubyobj_to_cval(arg, dtype);
}
+ NM_CONSERVATIVE(nm_unregister_value(arg));
return init_val;
}
@@ -1986,11 +2699,12 @@ static void* interpret_initial_value(VALUE arg, nm::dtype_t dtype) {
* array describing the shape, which must be freed manually.
*/
static size_t* interpret_shape(VALUE arg, size_t* dim) {
+ NM_CONSERVATIVE(nm_register_value(arg));
size_t* shape;
if (TYPE(arg) == T_ARRAY) {
*dim = RARRAY_LEN(arg);
- shape = ALLOC_N(size_t, *dim);
+ shape = NM_ALLOC_N(size_t, *dim);
for (size_t index = 0; index < *dim; ++index) {
shape[index] = FIX2UINT( RARRAY_PTR(arg)[index] );
@@ -1998,15 +2712,17 @@ static size_t* interpret_shape(VALUE arg, size_t* dim) {
} else if (FIXNUM_P(arg)) {
*dim = 2;
- shape = ALLOC_N(size_t, *dim);
+ shape = NM_ALLOC_N(size_t, *dim);
shape[0] = FIX2UINT(arg);
shape[1] = FIX2UINT(arg);
} else {
+ nm_unregister_value(arg);
rb_raise(rb_eArgError, "Expected an array of numbers or a single Fixnum for matrix shape");
}
+ NM_CONSERVATIVE(nm_unregister_value(arg));
return shape;
}
@@ -2038,12 +2754,20 @@ STORAGE* matrix_storage_cast_alloc(NMATRIX* matrix, nm::dtype_t new_dtype) {
}
STORAGE_PAIR binary_storage_cast_alloc(NMATRIX* left_matrix, NMATRIX* right_matrix) {
+ nm_register_nmatrix(left_matrix);
+ nm_register_nmatrix(right_matrix);
+
STORAGE_PAIR casted;
nm::dtype_t new_dtype = Upcast[left_matrix->storage->dtype][right_matrix->storage->dtype];
casted.left = matrix_storage_cast_alloc(left_matrix, new_dtype);
+ nm_register_storage(left_matrix->stype, casted.left);
casted.right = matrix_storage_cast_alloc(right_matrix, new_dtype);
+ nm_unregister_nmatrix(left_matrix);
+ nm_unregister_nmatrix(right_matrix);
+ nm_unregister_storage(left_matrix->stype, casted.left);
+
return casted;
}
@@ -2053,12 +2777,16 @@ static VALUE matrix_multiply_scalar(NMATRIX* left, VALUE scalar) {
}
static VALUE matrix_multiply(NMATRIX* left, NMATRIX* right) {
+ nm_register_nmatrix(left);
+ nm_register_nmatrix(right);
///TODO: multiplication for non-dense and/or non-decimal matrices
// Make sure both of our matrices are of the correct type.
STORAGE_PAIR casted = binary_storage_cast_alloc(left, right);
+ nm_register_storage(left->stype, casted.left);
+ nm_register_storage(right->stype, casted.right);
- size_t* resulting_shape = ALLOC_N(size_t, 2);
+ size_t* resulting_shape = NM_ALLOC_N(size_t, 2);
resulting_shape[0] = left->storage->shape[0];
resulting_shape[1] = right->storage->shape[1];
@@ -2074,6 +2802,7 @@ static VALUE matrix_multiply(NMATRIX* left, NMATRIX* right) {
STORAGE* resulting_storage = storage_matrix_multiply[left->stype](casted, resulting_shape, vector);
NMATRIX* result = nm_create(left->stype, resulting_storage);
+ nm_register_nmatrix(result);
// Free any casted-storage we created for the multiplication.
// TODO: Can we make the Ruby GC take care of this stuff now that we're using it?
@@ -2085,11 +2814,19 @@ static VALUE matrix_multiply(NMATRIX* left, NMATRIX* right) {
nm_yale_storage_delete
};
+ nm_unregister_storage(left->stype, casted.left);
if (left->storage != casted.left) free_storage[result->stype](casted.left);
+
+ nm_unregister_storage(right->stype, casted.right);
if (right->storage != casted.right) free_storage[result->stype](casted.right);
- if (result) return Data_Wrap_Struct(cNMatrix, nm_mark, nm_delete, result);
- return Qnil; // Only if we try to multiply list matrices should we return Qnil.
+ VALUE to_return = result ? Data_Wrap_Struct(cNMatrix, nm_mark, nm_delete, result) : Qnil; // Only if we try to multiply list matrices should we return Qnil.
+
+ nm_unregister_nmatrix(left);
+ nm_unregister_nmatrix(right);
+ nm_unregister_nmatrix(result);
+
+ return to_return;
}
/*
@@ -2100,15 +2837,31 @@ static VALUE matrix_multiply(NMATRIX* left, NMATRIX* right) {
* Note: Currently only implemented for 2x2 and 3x3 matrices.
*/
static VALUE nm_det_exact(VALUE self) {
- if (NM_STYPE(self) != nm::DENSE_STORE) rb_raise(nm_eStorageTypeError, "can only calculate exact determinant for dense matrices");
- if (NM_DIM(self) != 2 || NM_SHAPE0(self) != NM_SHAPE1(self)) return Qnil;
+ if (NM_STYPE(self) != nm::DENSE_STORE) {
+ rb_raise(nm_eStorageTypeError, "can only calculate exact determinant for dense matrices");
+ }
+ if (NM_DIM(self) != 2 || NM_SHAPE0(self) != NM_SHAPE1(self)) {
+ return Qnil;
+ }
+
+ NM_CONSERVATIVE(nm_register_value(self));
// Calculate the determinant and then assign it to the return value
- void* result = ALLOCA_N(char, DTYPE_SIZES[NM_DTYPE(self)]);
+ void* result = NM_ALLOCA_N(char, DTYPE_SIZES[NM_DTYPE(self)]);
+ nm::dtype_t dtype = NM_DTYPE(self);
nm_math_det_exact(NM_SHAPE0(self), NM_STORAGE_DENSE(self)->elements, NM_SHAPE0(self), NM_DTYPE(self), result);
- return rubyobj_from_cval(result, NM_DTYPE(self)).rval;
+ if (dtype == nm::RUBYOBJ) {
+ nm_register_values(reinterpret_cast<VALUE*>(result), 1);
+ }
+ VALUE to_return = rubyobj_from_cval(result, NM_DTYPE(self)).rval;
+ if (dtype == nm::RUBYOBJ) {
+ nm_unregister_values(reinterpret_cast<VALUE*>(result), 1);
+ }
+ NM_CONSERVATIVE(nm_unregister_value(self));
+
+ return to_return;
}
/////////////////
@@ -2127,6 +2880,11 @@ static VALUE nm_det_exact(VALUE self) {
* TODO: Add a column-major option for libraries that use column-major matrices.
*/
VALUE rb_nmatrix_dense_create(nm::dtype_t dtype, size_t* shape, size_t dim, void* elements, size_t length) {
+
+ if (dtype == nm::RUBYOBJ) {
+ nm_register_values(reinterpret_cast<VALUE*>(elements), length);
+ }
+
NMATRIX* nm;
size_t nm_dim;
size_t* shape_copy;
@@ -2134,25 +2892,34 @@ VALUE rb_nmatrix_dense_create(nm::dtype_t dtype, size_t* shape, size_t dim, void
// Do not allow a dim of 1. Treat it as a column or row matrix.
if (dim == 1) {
nm_dim = 2;
- shape_copy = ALLOC_N(size_t, nm_dim);
+ shape_copy = NM_ALLOC_N(size_t, nm_dim);
shape_copy[0] = shape[0];
shape_copy[1] = 1;
} else {
nm_dim = dim;
- shape_copy = ALLOC_N(size_t, nm_dim);
+ shape_copy = NM_ALLOC_N(size_t, nm_dim);
memcpy(shape_copy, shape, sizeof(size_t)*nm_dim);
}
// Copy elements
- void* elements_copy = ALLOC_N(char, DTYPE_SIZES[dtype]*length);
+ void* elements_copy = NM_ALLOC_N(char, DTYPE_SIZES[dtype]*length);
memcpy(elements_copy, elements, DTYPE_SIZES[dtype]*length);
// allocate and create the matrix and its storage
nm = nm_create(nm::DENSE_STORE, nm_dense_storage_create(dtype, shape_copy, dim, elements_copy, length));
+ nm_register_nmatrix(nm);
+
+ VALUE to_return = Data_Wrap_Struct(cNMatrix, nm_mark, nm_delete, nm);
+
+ nm_unregister_nmatrix(nm);
+ if (dtype == nm::RUBYOBJ) {
+ nm_unregister_values(reinterpret_cast<VALUE*>(elements), length);
+ }
+
// tell Ruby about the matrix and its storage, particularly how to garbage collect it.
- return Data_Wrap_Struct(cNMatrix, nm_mark, nm_delete, nm);
+ return to_return;
}
/*
diff --git a/ext/nmatrix/storage/common.cpp b/ext/nmatrix/storage/common.cpp
index 482a48b..e6a1325 100644
--- a/ext/nmatrix/storage/common.cpp
+++ b/ext/nmatrix/storage/common.cpp
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/storage/common.h b/ext/nmatrix/storage/common.h
index 17be0e2..b9b9ac1 100644
--- a/ext/nmatrix/storage/common.h
+++ b/ext/nmatrix/storage/common.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/storage/dense.cpp b/ext/nmatrix/storage/dense/dense.cpp
similarity index 73%
rename from ext/nmatrix/storage/dense.cpp
rename to ext/nmatrix/storage/dense/dense.cpp
index f391582..1253e1a 100644
--- a/ext/nmatrix/storage/dense.cpp
+++ b/ext/nmatrix/storage/dense/dense.cpp
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -34,13 +34,12 @@
/*
* Project Includes
*/
-// #include "types.h"
-#include "data/data.h"
-#include "math/long_dtype.h"
-#include "math/gemm.h"
-#include "math/gemv.h"
-#include "math/math.h"
-#include "common.h"
+#include "../../data/data.h"
+#include "../../math/long_dtype.h"
+#include "../../math/gemm.h"
+#include "../../math/gemv.h"
+#include "../../math/math.h"
+#include "../common.h"
#include "dense.h"
/*
@@ -124,10 +123,13 @@ namespace nm { namespace dense_storage {
*/
template <typename D>
void set(VALUE left, SLICE* slice, VALUE right) {
+ NM_CONSERVATIVE(nm_register_value(left));
+ NM_CONSERVATIVE(nm_register_value(right));
+
DENSE_STORAGE* s = NM_STORAGE_DENSE(left);
std::pair<NMATRIX*,bool> nm_and_free =
- interpret_arg_as_dense_nmatrix(right, NM_DTYPE(left));
+ interpret_arg_as_dense_nmatrix(right, s->dtype);
// Map the data onto D* v.
D* v;
@@ -139,13 +141,20 @@ namespace nm { namespace dense_storage {
v_size = nm_storage_count_max_elements(t);
} else if (TYPE(right) == T_ARRAY) {
+
v_size = RARRAY_LEN(right);
- v = ALLOC_N(D, v_size);
+ v = NM_ALLOC_N(D, v_size);
+ if (s->dtype == nm::RUBYOBJ)
+ nm_register_values(reinterpret_cast<VALUE*>(v), v_size);
+
for (size_t m = 0; m < v_size; ++m) {
rubyval_to_cval(rb_ary_entry(right, m), s->dtype, &(v[m]));
}
+
} else {
v = reinterpret_cast<D*>(rubyobj_to_cval(right, NM_DTYPE(left)));
+ if (s->dtype == nm::RUBYOBJ)
+ nm_register_values(reinterpret_cast<VALUE*>(v), v_size);
}
if (slice->single) {
@@ -156,10 +165,18 @@ namespace nm { namespace dense_storage {
}
// Only free v if it was allocated in this function.
- if (nm_and_free.first && nm_and_free.second)
- nm_delete(nm_and_free.first);
- else
- xfree(v);
+ if (nm_and_free.first) {
+ if (nm_and_free.second) {
+ nm_delete(nm_and_free.first);
+ }
+ } else {
+ if (s->dtype == nm::RUBYOBJ)
+ nm_unregister_values(reinterpret_cast<VALUE*>(v), v_size);
+ NM_FREE(v);
+ }
+ NM_CONSERVATIVE(nm_unregister_value(left));
+ NM_CONSERVATIVE(nm_unregister_value(right));
+
}
}} // end of namespace nm::dense_storage
@@ -186,13 +203,13 @@ static void slice_copy(DENSE_STORAGE *dest, const DENSE_STORAGE *src, size_t* le
* check for that NULL pointer before freeing elements.
*/
static DENSE_STORAGE* nm_dense_storage_create_dummy(nm::dtype_t dtype, size_t* shape, size_t dim) {
- DENSE_STORAGE* s = ALLOC( DENSE_STORAGE );
+ DENSE_STORAGE* s = NM_ALLOC( DENSE_STORAGE );
s->dim = dim;
s->shape = shape;
s->dtype = dtype;
- s->offset = ALLOC_N(size_t, dim);
+ s->offset = NM_ALLOC_N(size_t, dim);
memset(s->offset, 0, sizeof(size_t)*dim);
s->stride = stride(shape, dim);
@@ -212,15 +229,24 @@ static DENSE_STORAGE* nm_dense_storage_create_dummy(nm::dtype_t dtype, size_t* s
* elements is NULL, the new elements array will not be initialized.
*/
DENSE_STORAGE* nm_dense_storage_create(nm::dtype_t dtype, size_t* shape, size_t dim, void* elements, size_t elements_length) {
+ if (dtype == nm::RUBYOBJ)
+ nm_register_values(reinterpret_cast<VALUE*>(elements), elements_length);
DENSE_STORAGE* s = nm_dense_storage_create_dummy(dtype, shape, dim);
size_t count = nm_storage_count_max_elements(s);
if (elements_length == count) {
- s->elements = elements;
+ s->elements = elements;
+
+ if (dtype == nm::RUBYOBJ)
+ nm_unregister_values(reinterpret_cast<VALUE*>(elements), elements_length);
} else {
- s->elements = ALLOC_N(char, DTYPE_SIZES[dtype]*count);
+
+ s->elements = NM_ALLOC_N(char, DTYPE_SIZES[dtype]*count);
+
+ if (dtype == nm::RUBYOBJ)
+ nm_unregister_values(reinterpret_cast<VALUE*>(elements), elements_length);
size_t copy_length = elements_length;
@@ -236,7 +262,7 @@ DENSE_STORAGE* nm_dense_storage_create(nm::dtype_t dtype, size_t* shape, size_t
}
// Get rid of the init_val.
- xfree(elements);
+ NM_FREE(elements);
}
}
@@ -252,12 +278,13 @@ void nm_dense_storage_delete(STORAGE* s) {
if (s) {
DENSE_STORAGE* storage = (DENSE_STORAGE*)s;
if(storage->count-- == 1) {
- xfree(storage->shape);
- xfree(storage->offset);
- xfree(storage->stride);
- if (storage->elements != NULL) // happens with dummy objects
- xfree(storage->elements);
- xfree(storage);
+ NM_FREE(storage->shape);
+ NM_FREE(storage->offset);
+ NM_FREE(storage->stride);
+ if (storage->elements != NULL) {// happens with dummy objects
+ NM_FREE(storage->elements);
+ }
+ NM_FREE(storage);
}
}
}
@@ -270,9 +297,9 @@ void nm_dense_storage_delete_ref(STORAGE* s) {
if (s) {
DENSE_STORAGE* storage = (DENSE_STORAGE*)s;
nm_dense_storage_delete( reinterpret_cast<STORAGE*>(storage->src) );
- xfree(storage->shape);
- xfree(storage->offset);
- xfree(storage);
+ NM_FREE(storage->shape);
+ NM_FREE(storage->offset);
+ NM_FREE(storage);
}
}
@@ -286,14 +313,43 @@ void nm_dense_storage_mark(STORAGE* storage_base) {
if (storage && storage->dtype == nm::RUBYOBJ) {
VALUE* els = reinterpret_cast<VALUE*>(storage->elements);
- rb_gc_mark_locations(els, els + nm_storage_count_max_elements(storage) * sizeof(VALUE));
-
+ if (els) {
+ rb_gc_mark_locations(els, &(els[nm_storage_count_max_elements(storage)-1]));
+ }
//for (size_t index = nm_storage_count_max_elements(storage); index-- > 0;) {
// rb_gc_mark(els[index]);
//}
}
}
+/**
+ * Register a dense storage struct as in-use to avoid garbage collection of the
+ * elements stored.
+ *
+ * This function will check dtype and ignore non-object dtype, so its safe to pass any dense storage in.
+ *
+ */
+void nm_dense_storage_register(const STORAGE* s) {
+ const DENSE_STORAGE* storage = reinterpret_cast<const DENSE_STORAGE*>(s);
+ if (storage->dtype == nm::RUBYOBJ && storage->elements) {
+ nm_register_values(reinterpret_cast<VALUE*>(storage->elements), nm_storage_count_max_elements(storage));
+ }
+}
+
+/**
+ * Unregister a dense storage struct to allow normal garbage collection of the
+ * elements stored.
+ *
+ * This function will check dtype and ignore non-object dtype, so its safe to pass any dense storage in.
+ *
+ */
+void nm_dense_storage_unregister(const STORAGE* s) {
+ const DENSE_STORAGE* storage = reinterpret_cast<const DENSE_STORAGE*>(s);
+ if (storage->dtype == nm::RUBYOBJ && storage->elements) {
+ nm_unregister_values(reinterpret_cast<VALUE*>(storage->elements), nm_storage_count_max_elements(storage));
+ }
+}
+
///////////////
// Accessors //
///////////////
@@ -304,23 +360,30 @@ void nm_dense_storage_mark(STORAGE* storage_base) {
* map_pair iterator for dense matrices (for element-wise operations)
*/
VALUE nm_dense_map_pair(VALUE self, VALUE right) {
- DENSE_STORAGE *s = NM_STORAGE_DENSE(self),
- *t = NM_STORAGE_DENSE(right);
+ NM_CONSERVATIVE(nm_register_value(self));
+ NM_CONSERVATIVE(nm_register_value(right));
+
+ RETURN_SIZED_ENUMERATOR_PRE
+ NM_CONSERVATIVE(nm_unregister_value(right));
+ NM_CONSERVATIVE(nm_unregister_value(self));
RETURN_SIZED_ENUMERATOR(self, 0, 0, nm_enumerator_length);
- size_t* coords = ALLOCA_N(size_t, s->dim);
+ DENSE_STORAGE *s = NM_STORAGE_DENSE(self),
+ *t = NM_STORAGE_DENSE(right);
+
+ size_t* coords = NM_ALLOCA_N(size_t, s->dim);
memset(coords, 0, sizeof(size_t) * s->dim);
- size_t *shape_copy = ALLOC_N(size_t, s->dim);
+ size_t *shape_copy = NM_ALLOC_N(size_t, s->dim);
memcpy(shape_copy, s->shape, sizeof(size_t) * s->dim);
size_t count = nm_storage_count_max_elements(s);
DENSE_STORAGE* result = nm_dense_storage_create(nm::RUBYOBJ, shape_copy, s->dim, NULL, 0);
- VALUE* result_elem = reinterpret_cast<VALUE*>(result->elements);
- nm_register_values(result_elem, count);
+ VALUE* result_elem = reinterpret_cast<VALUE*>(result->elements);
+ nm_dense_storage_register(result);
for (size_t k = 0; k < count; ++k) {
nm_dense_storage_coords(result, k, coords);
@@ -328,17 +391,23 @@ VALUE nm_dense_map_pair(VALUE self, VALUE right) {
t_index = nm_dense_storage_pos(t, coords);
VALUE sval = NM_DTYPE(self) == nm::RUBYOBJ ? reinterpret_cast<VALUE*>(s->elements)[s_index] : rubyobj_from_cval((char*)(s->elements) + s_index*DTYPE_SIZES[NM_DTYPE(self)], NM_DTYPE(self)).rval;
+ nm_register_value(sval);
VALUE tval = NM_DTYPE(right) == nm::RUBYOBJ ? reinterpret_cast<VALUE*>(t->elements)[t_index] : rubyobj_from_cval((char*)(t->elements) + t_index*DTYPE_SIZES[NM_DTYPE(right)], NM_DTYPE(right)).rval;
-
result_elem[k] = rb_yield_values(2, sval, tval);
+ nm_unregister_value(sval);
}
+ VALUE klass = CLASS_OF(self);
NMATRIX* m = nm_create(nm::DENSE_STORE, reinterpret_cast<STORAGE*>(result));
- VALUE rb_nm = Data_Wrap_Struct(CLASS_OF(self), nm_mark, nm_delete, m);
+ nm_register_nmatrix(m);
+ VALUE to_return = Data_Wrap_Struct(klass, nm_mark, nm_delete, m);
- nm_unregister_values(result_elem, count);
+ nm_unregister_nmatrix(m);
+ nm_dense_storage_unregister(result);
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ NM_CONSERVATIVE(nm_unregister_value(right));
- return rb_nm;
+ return to_return;
}
@@ -346,22 +415,28 @@ VALUE nm_dense_map_pair(VALUE self, VALUE right) {
* map enumerator for dense matrices.
*/
VALUE nm_dense_map(VALUE self) {
- DENSE_STORAGE *s = NM_STORAGE_DENSE(self);
+ NM_CONSERVATIVE(nm_register_value(self));
+
+ RETURN_SIZED_ENUMERATOR_PRE
+ NM_CONSERVATIVE(nm_unregister_value(self));
RETURN_SIZED_ENUMERATOR(self, 0, 0, nm_enumerator_length);
- size_t* coords = ALLOCA_N(size_t, s->dim);
+ DENSE_STORAGE *s = NM_STORAGE_DENSE(self);
+
+ size_t* coords = NM_ALLOCA_N(size_t, s->dim);
memset(coords, 0, sizeof(size_t) * s->dim);
- size_t *shape_copy = ALLOC_N(size_t, s->dim);
+ size_t *shape_copy = NM_ALLOC_N(size_t, s->dim);
memcpy(shape_copy, s->shape, sizeof(size_t) * s->dim);
size_t count = nm_storage_count_max_elements(s);
DENSE_STORAGE* result = nm_dense_storage_create(nm::RUBYOBJ, shape_copy, s->dim, NULL, 0);
+
VALUE* result_elem = reinterpret_cast<VALUE*>(result->elements);
- nm_register_values(result_elem, count);
+ nm_dense_storage_register(result);
for (size_t k = 0; k < count; ++k) {
nm_dense_storage_coords(result, k, coords);
@@ -370,13 +445,18 @@ VALUE nm_dense_map(VALUE self) {
result_elem[k] = rb_yield(NM_DTYPE(self) == nm::RUBYOBJ ? reinterpret_cast<VALUE*>(s->elements)[s_index] : rubyobj_from_cval((char*)(s->elements) + s_index*DTYPE_SIZES[NM_DTYPE(self)], NM_DTYPE(self)).rval);
}
+ VALUE klass = CLASS_OF(self);
+
NMATRIX* m = nm_create(nm::DENSE_STORE, reinterpret_cast<STORAGE*>(result));
- VALUE rb_nm = Data_Wrap_Struct(CLASS_OF(self), nm_mark, nm_delete, m);
+ nm_register_nmatrix(m);
- nm_unregister_values(result_elem, count);
+ VALUE to_return = Data_Wrap_Struct(klass, nm_mark, nm_delete, m);
- return rb_nm;
+ nm_unregister_nmatrix(m);
+ nm_dense_storage_unregister(result);
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ return to_return;
}
@@ -384,18 +464,20 @@ VALUE nm_dense_map(VALUE self) {
* each_with_indices iterator for dense matrices.
*/
VALUE nm_dense_each_with_indices(VALUE nmatrix) {
- volatile VALUE nm = nmatrix;
-
- DENSE_STORAGE* s = NM_STORAGE_DENSE(nm);
- RETURN_SIZED_ENUMERATOR(nm, 0, 0, nm_enumerator_length); // fourth argument only used by Ruby2+
+ NM_CONSERVATIVE(nm_register_value(nmatrix));
+
+ RETURN_SIZED_ENUMERATOR_PRE
+ NM_CONSERVATIVE(nm_unregister_value(nmatrix));
+ RETURN_SIZED_ENUMERATOR(nmatrix, 0, 0, nm_enumerator_length); // fourth argument only used by Ruby2+
+ DENSE_STORAGE* s = NM_STORAGE_DENSE(nmatrix);
// Create indices and initialize them to zero
- size_t* coords = ALLOCA_N(size_t, s->dim);
+ size_t* coords = NM_ALLOCA_N(size_t, s->dim);
memset(coords, 0, sizeof(size_t) * s->dim);
size_t slice_index;
- size_t* shape_copy = ALLOC_N(size_t, s->dim);
+ size_t* shape_copy = NM_ALLOC_N(size_t, s->dim);
memcpy(shape_copy, s->shape, sizeof(size_t) * s->dim);
DENSE_STORAGE* sliced_dummy = nm_dense_storage_create_dummy(s->dtype, shape_copy, s->dim);
@@ -404,8 +486,9 @@ VALUE nm_dense_each_with_indices(VALUE nmatrix) {
nm_dense_storage_coords(sliced_dummy, k, coords);
slice_index = nm_dense_storage_pos(s, coords);
VALUE ary = rb_ary_new();
- if (NM_DTYPE(nm) == nm::RUBYOBJ) rb_ary_push(ary, reinterpret_cast<VALUE*>(s->elements)[slice_index]);
- else rb_ary_push(ary, rubyobj_from_cval((char*)(s->elements) + slice_index*DTYPE_SIZES[NM_DTYPE(nm)], NM_DTYPE(nm)).rval);
+ nm_register_value(ary);
+ if (NM_DTYPE(nmatrix) == nm::RUBYOBJ) rb_ary_push(ary, reinterpret_cast<VALUE*>(s->elements)[slice_index]);
+ else rb_ary_push(ary, rubyobj_from_cval((char*)(s->elements) + slice_index*DTYPE_SIZES[NM_DTYPE(nmatrix)], NM_DTYPE(nmatrix)).rval);
for (size_t p = 0; p < s->dim; ++p) {
rb_ary_push(ary, INT2FIX(coords[p]));
@@ -413,11 +496,13 @@ VALUE nm_dense_each_with_indices(VALUE nmatrix) {
// yield the array which now consists of the value and the indices
rb_yield(ary);
-
+ nm_unregister_value(ary);
}
nm_dense_storage_delete(sliced_dummy);
+ NM_CONSERVATIVE(nm_unregister_value(nmatrix));
+
return nmatrix;
}
@@ -431,18 +516,22 @@ VALUE nm_dense_each_with_indices(VALUE nmatrix) {
* containing other types of data.
*/
VALUE nm_dense_each(VALUE nmatrix) {
- volatile VALUE nm = nmatrix; // Not sure this actually does anything.
- DENSE_STORAGE* s = NM_STORAGE_DENSE(nm);
- RETURN_SIZED_ENUMERATOR(nm, 0, 0, nm_enumerator_length);
+ NM_CONSERVATIVE(nm_register_value(nmatrix));
+
+ RETURN_SIZED_ENUMERATOR_PRE
+ NM_CONSERVATIVE(nm_unregister_value(nmatrix));
+ RETURN_SIZED_ENUMERATOR(nmatrix, 0, 0, nm_enumerator_length);
+
+ DENSE_STORAGE* s = NM_STORAGE_DENSE(nmatrix);
- size_t* temp_coords = ALLOCA_N(size_t, s->dim);
+ size_t* temp_coords = NM_ALLOCA_N(size_t, s->dim);
size_t sliced_index;
- size_t* shape_copy = ALLOC_N(size_t, s->dim);
+ size_t* shape_copy = NM_ALLOC_N(size_t, s->dim);
memcpy(shape_copy, s->shape, sizeof(size_t) * s->dim);
DENSE_STORAGE* sliced_dummy = nm_dense_storage_create_dummy(s->dtype, shape_copy, s->dim);
- if (NM_DTYPE(nm) == nm::RUBYOBJ) {
+ if (NM_DTYPE(nmatrix) == nm::RUBYOBJ) {
// matrix of Ruby objects -- yield those objects directly
for (size_t i = 0; i < nm_storage_count_max_elements(s); ++i) {
@@ -458,12 +547,13 @@ VALUE nm_dense_each(VALUE nmatrix) {
for (size_t i = 0; i < nm_storage_count_max_elements(s); ++i) {
nm_dense_storage_coords(sliced_dummy, i, temp_coords);
sliced_index = nm_dense_storage_pos(s, temp_coords);
- VALUE v = rubyobj_from_cval((char*)(s->elements) + sliced_index*DTYPE_SIZES[NM_DTYPE(nm)], NM_DTYPE(nm)).rval;
+ VALUE v = rubyobj_from_cval((char*)(s->elements) + sliced_index*DTYPE_SIZES[NM_DTYPE(nmatrix)], NM_DTYPE(nmatrix)).rval;
rb_yield( v ); // yield to the copy we made
}
}
nm_dense_storage_delete(sliced_dummy);
+ NM_CONSERVATIVE(nm_unregister_value(nmatrix));
return nmatrix;
@@ -487,11 +577,11 @@ static void slice_copy(DENSE_STORAGE *dest, const DENSE_STORAGE *src, size_t* le
*/
void* nm_dense_storage_get(const STORAGE* storage, SLICE* slice) {
DENSE_STORAGE* s = (DENSE_STORAGE*)storage;
-
if (slice->single)
return (char*)(s->elements) + nm_dense_storage_pos(s, slice->coords) * DTYPE_SIZES[s->dtype];
else {
- size_t *shape = ALLOC_N(size_t, s->dim);
+ nm_dense_storage_register(s);
+ size_t *shape = NM_ALLOC_N(size_t, s->dim);
for (size_t i = 0; i < s->dim; ++i) {
shape[i] = slice->lengths[i];
}
@@ -505,6 +595,7 @@ void* nm_dense_storage_get(const STORAGE* storage, SLICE* slice) {
nm_dense_storage_pos(s, slice->coords),
0);
+ nm_dense_storage_unregister(s);
return ns;
}
}
@@ -521,11 +612,12 @@ void* nm_dense_storage_ref(const STORAGE* storage, SLICE* slice) {
return (char*)(s->elements) + nm_dense_storage_pos(s, slice->coords) * DTYPE_SIZES[s->dtype];
else {
- DENSE_STORAGE* ns = ALLOC( DENSE_STORAGE );
+ nm_dense_storage_register(s);
+ DENSE_STORAGE* ns = NM_ALLOC( DENSE_STORAGE );
ns->dim = s->dim;
ns->dtype = s->dtype;
- ns->offset = ALLOC_N(size_t, ns->dim);
- ns->shape = ALLOC_N(size_t, ns->dim);
+ ns->offset = NM_ALLOC_N(size_t, ns->dim);
+ ns->shape = NM_ALLOC_N(size_t, ns->dim);
for (size_t i = 0; i < ns->dim; ++i) {
ns->offset[i] = slice->coords[i] + s->offset[i];
@@ -538,6 +630,7 @@ void* nm_dense_storage_ref(const STORAGE* storage, SLICE* slice) {
s->src->count++;
ns->src = s->src;
+ nm_dense_storage_unregister(s);
return ns;
}
}
@@ -550,8 +643,8 @@ void* nm_dense_storage_ref(const STORAGE* storage, SLICE* slice) {
*/
void nm_dense_storage_set(VALUE left, SLICE* slice, VALUE right) {
NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::dense_storage::set, void, VALUE, SLICE*, VALUE)
-
- ttable[NM_DTYPE(left)](left, slice, right);
+ nm::dtype_t dtype = NM_DTYPE(left);
+ ttable[dtype](left, slice, right);
}
@@ -567,7 +660,7 @@ void nm_dense_storage_set(VALUE left, SLICE* slice, VALUE right) {
* have the same dtype.
*/
bool nm_dense_storage_eqeq(const STORAGE* left, const STORAGE* right) {
- LR_DTYPE_TEMPLATE_TABLE(nm::dense_storage::eqeq, bool, const DENSE_STORAGE*, const DENSE_STORAGE*)
+ LR_DTYPE_TEMPLATE_TABLE(nm::dense_storage::eqeq, bool, const DENSE_STORAGE*, const DENSE_STORAGE*)
if (!ttable[left->dtype][right->dtype]) {
rb_raise(nm_eDataTypeError, "comparison between these dtypes is undefined");
@@ -657,7 +750,7 @@ void nm_dense_storage_coords(const DENSE_STORAGE* s, const size_t slice_pos, siz
*/
static size_t* stride(size_t* shape, size_t dim) {
size_t i, j;
- size_t* stride = ALLOC_N(size_t, dim);
+ size_t* stride = NM_ALLOC_N(size_t, dim);
for (i = 0; i < dim; ++i) {
stride[i] = 1;
@@ -678,22 +771,24 @@ static size_t* stride(size_t* shape, size_t dim) {
* Copy dense storage, changing dtype if necessary.
*/
STORAGE* nm_dense_storage_cast_copy(const STORAGE* rhs, nm::dtype_t new_dtype, void* dummy) {
- NAMED_LR_DTYPE_TEMPLATE_TABLE(ttable, nm::dense_storage::cast_copy, DENSE_STORAGE*, const DENSE_STORAGE* rhs, nm::dtype_t new_dtype);
+ NAMED_LR_DTYPE_TEMPLATE_TABLE(ttable, nm::dense_storage::cast_copy, DENSE_STORAGE*, const DENSE_STORAGE* rhs, nm::dtype_t new_dtype);
if (!ttable[new_dtype][rhs->dtype]) {
rb_raise(nm_eDataTypeError, "cast between these dtypes is undefined");
return NULL;
}
- return (STORAGE*)ttable[new_dtype][rhs->dtype]((DENSE_STORAGE*)rhs, new_dtype);
+ return (STORAGE*)ttable[new_dtype][rhs->dtype]((DENSE_STORAGE*)rhs, new_dtype);
}
/*
* Copy dense storage without a change in dtype.
*/
DENSE_STORAGE* nm_dense_storage_copy(const DENSE_STORAGE* rhs) {
+ nm_dense_storage_register(rhs);
+
size_t count = 0;
- size_t *shape = ALLOC_N(size_t, rhs->dim);
+ size_t *shape = NM_ALLOC_N(size_t, rhs->dim);
// copy shape and offset
for (size_t i = 0; i < rhs->dim; ++i) {
@@ -709,7 +804,8 @@ DENSE_STORAGE* nm_dense_storage_copy(const DENSE_STORAGE* rhs) {
if (rhs == rhs->src) // not a reference
memcpy(lhs->elements, rhs->elements, DTYPE_SIZES[rhs->dtype] * count);
else { // slice whole matrix
- size_t *offset = ALLOC_N(size_t, rhs->dim);
+ nm_dense_storage_register(lhs);
+ size_t *offset = NM_ALLOC_N(size_t, rhs->dim);
memset(offset, 0, sizeof(size_t) * rhs->dim);
slice_copy(lhs,
@@ -718,9 +814,13 @@ DENSE_STORAGE* nm_dense_storage_copy(const DENSE_STORAGE* rhs) {
0,
nm_dense_storage_pos(rhs, offset),
0);
+
+ nm_dense_storage_unregister(lhs);
}
}
+ nm_dense_storage_unregister(rhs);
+
return lhs;
}
@@ -733,7 +833,9 @@ DENSE_STORAGE* nm_dense_storage_copy(const DENSE_STORAGE* rhs) {
STORAGE* nm_dense_storage_copy_transposed(const STORAGE* rhs_base) {
DENSE_STORAGE* rhs = (DENSE_STORAGE*)rhs_base;
- size_t *shape = ALLOC_N(size_t, rhs->dim);
+ nm_dense_storage_register(rhs);
+
+ size_t *shape = NM_ALLOC_N(size_t, rhs->dim);
// swap shape and offset
shape[0] = rhs->shape[1];
@@ -743,17 +845,25 @@ STORAGE* nm_dense_storage_copy_transposed(const STORAGE* rhs_base) {
lhs->offset[0] = rhs->offset[1];
lhs->offset[1] = rhs->offset[0];
+ nm_dense_storage_register(lhs);
+
if (rhs_base->src == rhs_base) {
nm_math_transpose_generic(rhs->shape[0], rhs->shape[1], rhs->elements, rhs->shape[1], lhs->elements, lhs->shape[1], DTYPE_SIZES[rhs->dtype]);
} else {
NAMED_LR_DTYPE_TEMPLATE_TABLE(ttable, nm::dense_storage::ref_slice_copy_transposed, void, const DENSE_STORAGE* rhs, DENSE_STORAGE* lhs);
- if (!ttable[lhs->dtype][rhs->dtype])
+ if (!ttable[lhs->dtype][rhs->dtype]) {
+ nm_dense_storage_unregister(rhs);
+ nm_dense_storage_unregister(lhs);
rb_raise(nm_eDataTypeError, "transposition between these dtypes is undefined");
+ }
ttable[lhs->dtype][rhs->dtype](rhs, lhs);
}
+ nm_dense_storage_unregister(rhs);
+ nm_dense_storage_unregister(lhs);
+
return (STORAGE*)lhs;
}
@@ -768,21 +878,26 @@ namespace nm {
* Otherwise, the NMATRIX* still belongs to Ruby and Ruby will free it.
*/
std::pair<NMATRIX*,bool> interpret_arg_as_dense_nmatrix(VALUE right, nm::dtype_t dtype) {
+ NM_CONSERVATIVE(nm_register_value(right));
if (TYPE(right) == T_DATA && (RDATA(right)->dfree == (RUBY_DATA_FUNC)nm_delete || RDATA(right)->dfree == (RUBY_DATA_FUNC)nm_delete_ref)) {
NMATRIX *r;
if (NM_STYPE(right) != DENSE_STORE || NM_DTYPE(right) != dtype || NM_SRC(right) != NM_STORAGE(right)) {
UnwrapNMatrix( right, r );
NMATRIX* ldtype_r = nm_cast_with_ctype_args(r, nm::DENSE_STORE, dtype, NULL);
+ NM_CONSERVATIVE(nm_unregister_value(right));
return std::make_pair(ldtype_r,true);
} else { // simple case -- right-hand matrix is dense and is not a reference and has same dtype
UnwrapNMatrix( right, r );
+ NM_CONSERVATIVE(nm_unregister_value(right));
return std::make_pair(r, false);
}
// Do not set v_alloc = true for either of these. It is the responsibility of r/ldtype_r
} else if (TYPE(right) == T_DATA) {
+ NM_CONSERVATIVE(nm_unregister_value(right));
rb_raise(rb_eTypeError, "unrecognized type for slice assignment");
}
+ NM_CONSERVATIVE(nm_unregister_value(right));
return std::make_pair<NMATRIX*,bool>(NULL, false);
}
@@ -796,11 +911,14 @@ namespace dense_storage {
template<typename LDType, typename RDType>
void ref_slice_copy_transposed(const DENSE_STORAGE* rhs, DENSE_STORAGE* lhs) {
+ nm_dense_storage_register(rhs);
+ nm_dense_storage_register(lhs);
+
LDType* lhs_els = reinterpret_cast<LDType*>(lhs->elements);
RDType* rhs_els = reinterpret_cast<RDType*>(rhs->elements);
size_t count = nm_storage_count_max_elements(lhs);
- size_t* temp_coords = ALLOCA_N(size_t, lhs->dim);
+ size_t* temp_coords = NM_ALLOCA_N(size_t, lhs->dim);
size_t coord_swap_temp;
while (count-- > 0) {
@@ -810,21 +928,28 @@ void ref_slice_copy_transposed(const DENSE_STORAGE* rhs, DENSE_STORAGE* lhs) {
lhs_els[count] = rhs_els[r_coord];
}
+ nm_dense_storage_unregister(rhs);
+ nm_dense_storage_unregister(lhs);
+
}
template <typename LDType, typename RDType>
DENSE_STORAGE* cast_copy(const DENSE_STORAGE* rhs, dtype_t new_dtype) {
+ nm_dense_storage_register(rhs);
+
size_t count = nm_storage_count_max_elements(rhs);
- size_t *shape = ALLOC_N(size_t, rhs->dim);
+ size_t *shape = NM_ALLOC_N(size_t, rhs->dim);
memcpy(shape, rhs->shape, sizeof(size_t) * rhs->dim);
- DENSE_STORAGE* lhs = nm_dense_storage_create(new_dtype, shape, rhs->dim, NULL, 0);
+ DENSE_STORAGE* lhs = nm_dense_storage_create(new_dtype, shape, rhs->dim, NULL, 0);
+
+ nm_dense_storage_register(lhs);
// Ensure that allocation worked before copying.
if (lhs && count) {
if (rhs->src != rhs) { // Make a copy of a ref to a matrix.
- size_t* offset = ALLOCA_N(size_t, rhs->dim);
+ size_t* offset = NM_ALLOCA_N(size_t, rhs->dim);
memset(offset, 0, sizeof(size_t) * rhs->dim);
slice_copy(lhs, reinterpret_cast<const DENSE_STORAGE*>(rhs->src),
@@ -832,54 +957,72 @@ DENSE_STORAGE* cast_copy(const DENSE_STORAGE* rhs, dtype_t new_dtype) {
nm_dense_storage_pos(rhs, offset), 0);
} else { // Make a regular copy.
- RDType* rhs_els = reinterpret_cast<RDType*>(rhs->elements);
- LDType* lhs_els = reinterpret_cast<LDType*>(lhs->elements);
+ RDType* rhs_els = reinterpret_cast<RDType*>(rhs->elements);
+ LDType* lhs_els = reinterpret_cast<LDType*>(lhs->elements);
- while (count-- > 0) lhs_els[count] = rhs_els[count];
+ for (size_t i = 0; i < count; ++i)
+ lhs_els[i] = rhs_els[i];
}
}
+ nm_dense_storage_unregister(rhs);
+ nm_dense_storage_unregister(lhs);
+
return lhs;
}
template <typename LDType, typename RDType>
bool eqeq(const DENSE_STORAGE* left, const DENSE_STORAGE* right) {
+ nm_dense_storage_register(left);
+ nm_dense_storage_register(right);
+
size_t index;
DENSE_STORAGE *tmp1, *tmp2;
tmp1 = NULL; tmp2 = NULL;
bool result = true;
/* FIXME: Very strange behavior! The GC calls the method directly with non-initialized data. */
- if (left->dim != right->dim) return false;
-
+ if (left->dim != right->dim) {
+ nm_dense_storage_unregister(right);
+ nm_dense_storage_unregister(left);
+ return false;
+ }
- LDType* left_elements = (LDType*)left->elements;
- RDType* right_elements = (RDType*)right->elements;
+ LDType* left_elements = (LDType*)left->elements;
+ RDType* right_elements = (RDType*)right->elements;
// Copy elements in temp matrix if you have reference to the right.
if (left->src != left) {
tmp1 = nm_dense_storage_copy(left);
+ nm_dense_storage_register(tmp1);
left_elements = (LDType*)tmp1->elements;
}
if (right->src != right) {
tmp2 = nm_dense_storage_copy(right);
+ nm_dense_storage_register(tmp2);
right_elements = (RDType*)tmp2->elements;
}
- for (index = nm_storage_count_max_elements(left); index-- > 0;) {
- if (left_elements[index] != right_elements[index]) {
+ for (index = nm_storage_count_max_elements(left); index-- > 0;) {
+ if (left_elements[index] != right_elements[index]) {
result = false;
break;
}
- }
+ }
- if (tmp1)
- free(tmp1);
- if (tmp2)
- free(tmp2);
+ if (tmp1) {
+ nm_dense_storage_unregister(tmp1);
+ NM_FREE(tmp1);
+ }
+ if (tmp2) {
+ nm_dense_storage_unregister(tmp2);
+ NM_FREE(tmp2);
+ }
- return result;
+ nm_dense_storage_unregister(left);
+ nm_dense_storage_unregister(right);
+ return result;
}
template <typename DType>
@@ -929,11 +1072,16 @@ static DENSE_STORAGE* matrix_multiply(const STORAGE_PAIR& casted_storage, size_t
DENSE_STORAGE *left = (DENSE_STORAGE*)(casted_storage.left),
*right = (DENSE_STORAGE*)(casted_storage.right);
+ nm_dense_storage_register(left);
+ nm_dense_storage_register(right);
+
// Create result storage.
DENSE_STORAGE* result = nm_dense_storage_create(left->dtype, resulting_shape, 2, NULL, 0);
- DType *pAlpha = ALLOCA_N(DType, 1),
- *pBeta = ALLOCA_N(DType, 1);
+ nm_dense_storage_register(result);
+
+ DType *pAlpha = NM_ALLOCA_N(DType, 1),
+ *pBeta = NM_ALLOCA_N(DType, 1);
*pAlpha = 1;
*pBeta = 0;
@@ -947,6 +1095,11 @@ static DENSE_STORAGE* matrix_multiply(const STORAGE_PAIR& casted_storage, size_t
reinterpret_cast<DType*>(right->elements), right->shape[1], pBeta,
reinterpret_cast<DType*>(result->elements), result->shape[1]);
+
+ nm_dense_storage_unregister(left);
+ nm_dense_storage_unregister(right);
+ nm_dense_storage_unregister(result);
+
return result;
}
diff --git a/ext/nmatrix/storage/dense.h b/ext/nmatrix/storage/dense/dense.h
similarity index 90%
rename from ext/nmatrix/storage/dense.h
rename to ext/nmatrix/storage/dense/dense.h
index 9f262c7..abe7d2d 100644
--- a/ext/nmatrix/storage/dense.h
+++ b/ext/nmatrix/storage/dense/dense.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -43,7 +43,7 @@
#include "data/data.h"
-#include "common.h"
+#include "../common.h"
#include "nmatrix.h"
@@ -73,8 +73,9 @@ DENSE_STORAGE* nm_dense_storage_create(nm::dtype_t dtype, size_t* shape, size_t
void nm_dense_storage_delete(STORAGE* s);
void nm_dense_storage_delete_ref(STORAGE* s);
void nm_dense_storage_mark(STORAGE*);
-void nm_dense_storage_register_values(VALUE* values, size_t n);
-void nm_dense_storage_unregister_values(VALUE* values, size_t n);
+void nm_dense_storage_register(const STORAGE* s);
+void nm_dense_storage_unregister(const STORAGE* s);
+
///////////////
// Accessors //
diff --git a/ext/nmatrix/storage/list.cpp b/ext/nmatrix/storage/list/list.cpp
similarity index 65%
rename from ext/nmatrix/storage/list.cpp
rename to ext/nmatrix/storage/list/list.cpp
index 9ffed21..d1fa8dc 100644
--- a/ext/nmatrix/storage/list.cpp
+++ b/ext/nmatrix/storage/list/list.cpp
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -25,7 +25,6 @@
//
// List-of-lists n-dimensional matrix storage. Uses singly-linked
// lists.
-
/*
* Standard Includes
*/
@@ -34,21 +33,22 @@
#include <algorithm> // std::min
#include <iostream>
#include <vector>
+#include <list>
/*
* Project Includes
*/
-#include "types.h"
+#include "../../types.h"
-#include "data/data.h"
+#include "../../data/data.h"
-#include "dense.h"
-#include "common.h"
+#include "../dense/dense.h"
+#include "../common.h"
#include "list.h"
-#include "math/math.h"
-#include "util/sl_list.h"
+#include "../../math/math.h"
+#include "../../util/sl_list.h"
/*
* Macros
@@ -61,6 +61,8 @@
extern "C" {
static void slice_set_single(LIST_STORAGE* dest, LIST* l, void* val, size_t* coords, size_t* lengths, size_t n);
+static void __nm_list_storage_unregister_temp_value_list(std::list<VALUE*>& temp_vals);
+static void __nm_list_storage_unregister_temp_list_list(std::list<LIST*>& temp_vals, size_t recursions);
}
namespace nm { namespace list_storage {
@@ -78,11 +80,20 @@ public:
offsets[i] += actual->offset[i];
actual = reinterpret_cast<LIST_STORAGE*>(actual->src);
}
+ nm_list_storage_register(actual);
+ nm_list_storage_register(ref);
actual_shape_ = actual->shape;
if (init_obj_ == Qnil) {
init_obj_ = s->dtype == nm::RUBYOBJ ? *reinterpret_cast<VALUE*>(s->default_val) : rubyobj_from_cval(s->default_val, s->dtype).rval;
}
+ nm_register_value(init_obj_);
+ }
+
+ ~RecurseData() {
+ nm_unregister_value(init_obj_);
+ nm_list_storage_unregister(ref);
+ nm_list_storage_unregister(actual);
}
dtype_t dtype() const { return ref->dtype; }
@@ -95,13 +106,13 @@ public:
}
size_t* copy_alloc_shape() const {
- size_t* new_shape = ALLOC_N(size_t, ref->dim);
+ size_t* new_shape = NM_ALLOC_N(size_t, ref->dim);
memcpy(new_shape, shape_, sizeof(size_t)*ref->dim);
return new_shape;
}
size_t actual_shape(size_t rec) const {
- return actual_shape_[ref->dim - rec - 1];
+ return actual_shape_[actual->dim - rec - 1];
}
size_t offset(size_t rec) const {
@@ -140,53 +151,157 @@ static bool eqeq_r(RecurseData& left, RecurseData& right, const LIST* l, const L
template <typename SDType, typename TDType>
static bool eqeq_empty_r(RecurseData& s, const LIST* l, size_t rec, const TDType* t_init);
-
/*
* Recursive helper for map_merged_stored_r which handles the case where one list is empty and the other is not.
*/
static void map_empty_stored_r(RecurseData& result, RecurseData& s, LIST* x, const LIST* l, size_t rec, bool rev, const VALUE& t_init) {
+ if (s.dtype() == nm::RUBYOBJ) {
+ nm_list_storage_register_list(l, rec);
+ }
+ if (result.dtype() == nm::RUBYOBJ) {
+ nm_list_storage_register_list(x, rec);
+ }
+
NODE *curr = l->first,
*xcurr = NULL;
// For reference matrices, make sure we start in the correct place.
- size_t offset = result.offset(rec);
- size_t x_shape = result.ref_shape(rec);
+ size_t offset = s.offset(rec);
+ size_t x_shape = s.ref_shape(rec);
while (curr && curr->key < offset) { curr = curr->next; }
if (curr && curr->key - offset >= x_shape) curr = NULL;
if (rec) {
+ std::list<LIST*> temp_vals;
while (curr) {
LIST* val = nm::list::create();
map_empty_stored_r(result, s, val, reinterpret_cast<const LIST*>(curr->val), rec-1, rev, t_init);
if (!val->first) nm::list::del(val, 0);
- else nm::list::insert_helper(x, xcurr, curr->key - offset, val);
-
+ else {
+ nm_list_storage_register_list(val, rec-1);
+ temp_vals.push_front(val);
+ nm::list::insert_helper(x, xcurr, curr->key - offset, val);
+ }
curr = curr->next;
if (curr && curr->key - offset >= x_shape) curr = NULL;
}
+ __nm_list_storage_unregister_temp_list_list(temp_vals, rec-1);
} else {
+ std::list<VALUE*> temp_vals;
while (curr) {
VALUE val, s_val = rubyobj_from_cval(curr->val, s.dtype()).rval;
if (rev) val = rb_yield_values(2, t_init, s_val);
else val = rb_yield_values(2, s_val, t_init);
- if (rb_funcall(val, rb_intern("!="), 1, result.init_obj()) == Qtrue)
+ nm_register_value(val);
+
+ if (rb_funcall(val, rb_intern("!="), 1, result.init_obj()) == Qtrue) {
xcurr = nm::list::insert_helper(x, xcurr, curr->key - offset, val);
+ temp_vals.push_front(reinterpret_cast<VALUE*>(xcurr->val));
+ nm_register_value(*reinterpret_cast<VALUE*>(xcurr->val));
+ }
+ nm_unregister_value(val);
curr = curr->next;
if (curr && curr->key - offset >= x_shape) curr = NULL;
}
+ __nm_list_storage_unregister_temp_value_list(temp_vals);
+ }
+
+ if (s.dtype() == nm::RUBYOBJ){
+ nm_list_storage_unregister_list(l, rec);
+ }
+ if (result.dtype() == nm::RUBYOBJ) {
+ nm_list_storage_unregister_list(x, rec);
}
}
/*
+ * Recursive helper function for nm_list_map_stored
+ */
+static void map_stored_r(RecurseData& result, RecurseData& left, LIST* x, const LIST* l, size_t rec) {
+ if (left.dtype() == nm::RUBYOBJ) {
+ nm_list_storage_register_list(l, rec);
+ }
+ if (result.dtype() == nm::RUBYOBJ) {
+ nm_list_storage_register_list(x, rec);
+ }
+ NODE *lcurr = l->first,
+ *xcurr = x->first;
+
+ // For reference matrices, make sure we start in the correct place.
+ while (lcurr && lcurr->key < left.offset(rec)) { lcurr = lcurr->next; }
+
+ if (lcurr && lcurr->key - left.offset(rec) >= result.ref_shape(rec)) lcurr = NULL;
+
+ if (rec) {
+ std::list<LIST*> temp_vals;
+ while (lcurr) {
+ size_t key;
+ LIST* val = nm::list::create();
+ map_stored_r(result, left, val, reinterpret_cast<const LIST*>(lcurr->val), rec-1);
+ key = lcurr->key - left.offset(rec);
+ lcurr = lcurr->next;
+
+ if (!val->first) nm::list::del(val, 0); // empty list -- don't insert
+ else {
+ nm_list_storage_register_list(val, rec-1);
+ temp_vals.push_front(val);
+ xcurr = nm::list::insert_helper(x, xcurr, key, val);
+ }
+ if (lcurr && lcurr->key - left.offset(rec) >= result.ref_shape(rec)) lcurr = NULL;
+ }
+ __nm_list_storage_unregister_temp_list_list(temp_vals, rec-1);
+ } else {
+ std::list<VALUE*> temp_vals;
+ while (lcurr) {
+ size_t key;
+ VALUE val;
+
+ val = rb_yield_values(1, rubyobj_from_cval(lcurr->val, left.dtype()).rval);
+ key = lcurr->key - left.offset(rec);
+ lcurr = lcurr->next;
+
+ if (!rb_equal(val, result.init_obj())) {
+ xcurr = nm::list::insert_helper(x, xcurr, key, val);
+ temp_vals.push_front(reinterpret_cast<VALUE*>(xcurr->val));
+ nm_register_value(*reinterpret_cast<VALUE*>(xcurr->val));
+ }
+
+ if (lcurr && lcurr->key - left.offset(rec) >= result.ref_shape(rec)) lcurr = NULL;
+ }
+ __nm_list_storage_unregister_temp_value_list(temp_vals);
+ }
+
+ if (left.dtype() == nm::RUBYOBJ) {
+ nm_list_storage_unregister_list(l, rec);
+ }
+ if (result.dtype() == nm::RUBYOBJ) {
+ nm_list_storage_unregister_list(x, rec);
+ }
+}
+
+
+
+/*
* Recursive helper function for nm_list_map_merged_stored
*/
static void map_merged_stored_r(RecurseData& result, RecurseData& left, RecurseData& right, LIST* x, const LIST* l, const LIST* r, size_t rec) {
+ if (left.dtype() == nm::RUBYOBJ) {
+ nm_list_storage_register_list(l, rec);
+ }
+ if (right.dtype() == nm::RUBYOBJ) {
+ nm_list_storage_register_list(r, rec);
+ }
+ if (result.dtype() == nm::RUBYOBJ) {
+ nm_list_storage_register_list(x, rec);
+ }
+
+
NODE *lcurr = l->first,
*rcurr = r->first,
*xcurr = x->first;
@@ -199,6 +314,7 @@ static void map_merged_stored_r(RecurseData& result, RecurseData& left, RecurseD
if (lcurr && lcurr->key - left.offset(rec) >= result.ref_shape(rec)) lcurr = NULL;
if (rec) {
+ std::list<LIST*> temp_vals;
while (lcurr || rcurr) {
size_t key;
LIST* val = nm::list::create();
@@ -218,13 +334,19 @@ static void map_merged_stored_r(RecurseData& result, RecurseData& left, RecurseD
rcurr = rcurr->next;
}
- if (!val->first) nm::list::del(val, 0); // empty list -- don't insert
- else xcurr = nm::list::insert_helper(x, xcurr, key, val);
+ if (!val->first) nm::list::del(val, 0); // empty list -- don't insert
+ else {
+ nm_list_storage_register_list(val, rec-1);
+ temp_vals.push_front(val);
+ xcurr = nm::list::insert_helper(x, xcurr, key, val);
+ }
if (rcurr && rcurr->key - right.offset(rec) >= result.ref_shape(rec)) rcurr = NULL;
if (lcurr && lcurr->key - left.offset(rec) >= result.ref_shape(rec)) lcurr = NULL;
}
+ __nm_list_storage_unregister_temp_list_list(temp_vals, rec-1);
} else {
+ std::list<VALUE*> temp_vals;
while (lcurr || rcurr) {
size_t key;
VALUE val;
@@ -234,7 +356,7 @@ static void map_merged_stored_r(RecurseData& result, RecurseData& left, RecurseD
key = lcurr->key - left.offset(rec);
lcurr = lcurr->next;
} else if (!lcurr || (rcurr && (rcurr->key - right.offset(rec) < lcurr->key - left.offset(rec)))) {
- val = rb_yield_values(2, left.init_obj(), rubyobj_from_cval(rcurr->val, right.dtype()).rval);
+ val = rb_yield_values(2, left.init_obj(), rubyobj_from_cval(rcurr->val, right.dtype()).rval);
key = rcurr->key - right.offset(rec);
rcurr = rcurr->next;
} else { // == and both present
@@ -243,15 +365,35 @@ static void map_merged_stored_r(RecurseData& result, RecurseData& left, RecurseD
lcurr = lcurr->next;
rcurr = rcurr->next;
}
- if (rb_funcall(val, rb_intern("!="), 1, result.init_obj()) == Qtrue)
+
+ nm_register_value(val);
+
+ if (rb_funcall(val, rb_intern("!="), 1, result.init_obj()) == Qtrue) {
xcurr = nm::list::insert_helper(x, xcurr, key, val);
+ temp_vals.push_front(reinterpret_cast<VALUE*>(xcurr->val));
+ nm_register_value(*reinterpret_cast<VALUE*>(xcurr->val));
+ }
+
+ nm_unregister_value(val);
if (rcurr && rcurr->key - right.offset(rec) >= result.ref_shape(rec)) rcurr = NULL;
if (lcurr && lcurr->key - left.offset(rec) >= result.ref_shape(rec)) lcurr = NULL;
}
+ __nm_list_storage_unregister_temp_value_list(temp_vals);
+ }
+
+ if (left.dtype() == nm::RUBYOBJ) {
+ nm_list_storage_unregister_list(l, rec);
+ }
+ if (right.dtype() == nm::RUBYOBJ) {
+ nm_list_storage_unregister_list(r, rec);
+ }
+ if (result.dtype() == nm::RUBYOBJ) {
+ nm_list_storage_unregister_list(x, rec);
}
}
+
/*
* Recursive function, sets multiple values in a matrix from multiple source values. Also handles removal; returns true
* if the recursion results in an empty list at that level (which signals that the current parent should be removed).
@@ -266,6 +408,12 @@ static bool slice_set(LIST_STORAGE* dest, LIST* l, size_t* coords, size_t* lengt
using nm::list::insert_after;
size_t* offsets = dest->offset;
+ nm_list_storage_register(dest);
+ if (dest->dtype == nm::RUBYOBJ) {
+ nm_register_values(reinterpret_cast<VALUE*>(v), v_size);
+ nm_list_storage_register_list(l, dest->dim - n - 1);
+ }
+
// drill down into the structure
NODE* prev = find_preceding_from_list(l, coords[n] + offsets[n]);
NODE* node = NULL;
@@ -286,13 +434,16 @@ static bool slice_set(LIST_STORAGE* dest, LIST* l, size_t* coords, size_t* lengt
}
// At this point, it's guaranteed that there is a list here matching key.
-
+ std::list<LIST*> temp_lists;
while (node) {
// Recurse down into the list. If it returns true, it's empty, so we need to delete it.
bool remove_parent = slice_set(dest, reinterpret_cast<LIST*>(node->val), coords, lengths, n+1, v, v_size, v_offset);
-
+ if (dest->dtype == nm::RUBYOBJ) {
+ temp_lists.push_front(reinterpret_cast<LIST*>(node->val));
+ nm_list_storage_register_list(reinterpret_cast<LIST*>(node->val), dest->dim - n - 2);
+ }
if (remove_parent) {
- xfree(remove_by_node(l, prev, node));
+ NM_FREE(remove_by_node(l, prev, node));
if (prev) node = prev->next ? prev->next : NULL;
else node = l->first ? l->first : NULL;
} else { // move forward
@@ -313,12 +464,13 @@ static bool slice_set(LIST_STORAGE* dest, LIST* l, size_t* coords, size_t* lengt
}
}
}
+ __nm_list_storage_unregister_temp_list_list(temp_lists, dest->dim - n - 2);
} else {
size_t i = 0;
size_t key = i + offsets[n] + coords[n];
-
+ std::list<VALUE*> temp_vals;
while (i < lengths[n]) {
// Make sure we have an element to work with
if (v_offset >= v_size) v_offset %= v_size;
@@ -327,7 +479,7 @@ static bool slice_set(LIST_STORAGE* dest, LIST* l, size_t* coords, size_t* lengt
if (node->key == key) {
if (v[v_offset] == *reinterpret_cast<D*>(dest->default_val)) { // remove zero value
- xfree(remove_by_node(l, (prev ? prev : l->first), node));
+ NM_FREE(remove_by_node(l, (prev ? prev : l->first), node));
if (prev) node = prev->next ? prev->next : NULL;
else node = l->first ? l->first : NULL;
@@ -338,7 +490,12 @@ static bool slice_set(LIST_STORAGE* dest, LIST* l, size_t* coords, size_t* lengt
node = node->next ? node->next : NULL;
}
} else if (node->key > key) {
- D* nv = ALLOC(D); *nv = v[v_offset];
+ D* nv = NM_ALLOC(D); *nv = v[v_offset++];
+ if (dest->dtype == nm::RUBYOBJ) {
+ nm_register_value(*reinterpret_cast<VALUE*>(nv));
+ temp_vals.push_front(reinterpret_cast<VALUE*>(nv));
+ }
+
if (prev) node = insert_after(prev, key, nv);
else node = insert_first_node(l, key, nv, sizeof(D));
@@ -346,7 +503,11 @@ static bool slice_set(LIST_STORAGE* dest, LIST* l, size_t* coords, size_t* lengt
node = prev->next ? prev->next : NULL;
}
} else { // no node -- insert a new one
- D* nv = ALLOC(D); *nv = v[v_offset];
+ D* nv = NM_ALLOC(D); *nv = v[v_offset++];
+ if (dest->dtype == nm::RUBYOBJ) {
+ nm_register_value(*reinterpret_cast<VALUE*>(nv));
+ temp_vals.push_front(reinterpret_cast<VALUE*>(nv));
+ }
if (prev) node = insert_after(prev, key, nv);
else node = insert_first_node(l, key, nv, sizeof(D));
@@ -356,7 +517,14 @@ static bool slice_set(LIST_STORAGE* dest, LIST* l, size_t* coords, size_t* lengt
++i; ++key;
}
+ __nm_list_storage_unregister_temp_value_list(temp_vals);
+ }
+
+ if (dest->dtype == nm::RUBYOBJ) {
+ nm_unregister_values(reinterpret_cast<VALUE*>(v), v_size);
+ nm_list_storage_unregister_list(l, dest->dim - n - 1);
}
+ nm_list_storage_unregister(dest);
return (l->first) ? false : true;
}
@@ -364,8 +532,10 @@ static bool slice_set(LIST_STORAGE* dest, LIST* l, size_t* coords, size_t* lengt
template <typename D>
void set(VALUE left, SLICE* slice, VALUE right) {
+ NM_CONSERVATIVE(nm_register_value(left));
+ NM_CONSERVATIVE(nm_register_value(right));
LIST_STORAGE* s = NM_STORAGE_LIST(left);
-
+
std::pair<NMATRIX*,bool> nm_and_free =
interpret_arg_as_dense_nmatrix(right, NM_DTYPE(left));
@@ -379,17 +549,27 @@ void set(VALUE left, SLICE* slice, VALUE right) {
v_size = nm_storage_count_max_elements(t);
} else if (TYPE(right) == T_ARRAY) {
+ nm_register_nmatrix(nm_and_free.first);
v_size = RARRAY_LEN(right);
- v = ALLOC_N(D, v_size);
+ v = NM_ALLOC_N(D, v_size);
+ if (NM_DTYPE(left) == nm::RUBYOBJ)
+ nm_register_values(reinterpret_cast<VALUE*>(v), v_size);
+
for (size_t m = 0; m < v_size; ++m) {
rubyval_to_cval(rb_ary_entry(right, m), s->dtype, &(v[m]));
}
+ if (NM_DTYPE(left) == nm::RUBYOBJ)
+ nm_unregister_values(reinterpret_cast<VALUE*>(v), v_size);
+
} else {
+ nm_register_nmatrix(nm_and_free.first);
v = reinterpret_cast<D*>(rubyobj_to_cval(right, NM_DTYPE(left)));
}
if (v_size == 1 && *v == *reinterpret_cast<D*>(s->default_val)) {
- nm::list::remove_recursive(s->rows, slice->coords, s->offset, slice->lengths, 0, s->dim);
+ if (*reinterpret_cast<D*>(nm_list_storage_get(s, slice)) != *reinterpret_cast<D*>(s->default_val)) {
+ nm::list::remove_recursive(s->rows, slice->coords, s->offset, slice->lengths, 0, s->dim);
+ }
} else if (slice->single) {
slice_set_single(s, s->rows, reinterpret_cast<void*>(v), slice->coords, slice->lengths, 0);
} else {
@@ -403,7 +583,12 @@ void set(VALUE left, SLICE* slice, VALUE right) {
if (nm_and_free.second) {
nm_delete(nm_and_free.first);
}
- } else xfree(v);
+ } else {
+ NM_FREE(v);
+ nm_unregister_nmatrix(nm_and_free.first);
+ }
+ NM_CONSERVATIVE(nm_unregister_value(left));
+ NM_CONSERVATIVE(nm_unregister_value(right));
}
/*
@@ -411,7 +596,7 @@ void set(VALUE left, SLICE* slice, VALUE right) {
*/
template <typename D>
void init_default(LIST_STORAGE* s) {
- s->default_val = ALLOC(D);
+ s->default_val = NM_ALLOC(D);
*reinterpret_cast<D*>(s->default_val) = 0;
}
@@ -438,13 +623,13 @@ extern "C" {
* new storage. You don't need to free them, and you shouldn't re-use them.
*/
LIST_STORAGE* nm_list_storage_create(nm::dtype_t dtype, size_t* shape, size_t dim, void* init_val) {
- LIST_STORAGE* s = ALLOC( LIST_STORAGE );
+ LIST_STORAGE* s = NM_ALLOC( LIST_STORAGE );
s->dim = dim;
s->shape = shape;
s->dtype = dtype;
- s->offset = ALLOC_N(size_t, s->dim);
+ s->offset = NM_ALLOC_N(size_t, s->dim);
memset(s->offset, 0, s->dim * sizeof(size_t));
s->rows = nm::list::create();
@@ -461,7 +646,7 @@ LIST_STORAGE* nm_list_storage_create(nm::dtype_t dtype, size_t* shape, size_t di
}
/*
- * Documentation goes here.
+ * Destructor for list storage.
*/
void nm_list_storage_delete(STORAGE* s) {
if (s) {
@@ -469,30 +654,30 @@ void nm_list_storage_delete(STORAGE* s) {
if (storage->count-- == 1) {
nm::list::del( storage->rows, storage->dim - 1 );
- xfree(storage->shape);
- xfree(storage->offset);
- xfree(storage->default_val);
- xfree(s);
+ NM_FREE(storage->shape);
+ NM_FREE(storage->offset);
+ NM_FREE(storage->default_val);
+ NM_FREE(s);
}
}
}
/*
- * Documentation goes here.
+ * Destructor for a list storage reference slice.
*/
void nm_list_storage_delete_ref(STORAGE* s) {
if (s) {
LIST_STORAGE* storage = (LIST_STORAGE*)s;
nm_list_storage_delete( reinterpret_cast<STORAGE*>(storage->src ) );
- xfree(storage->shape);
- xfree(storage->offset);
- xfree(s);
+ NM_FREE(storage->shape);
+ NM_FREE(storage->offset);
+ NM_FREE(s);
}
}
/*
- * Documentation goes here.
+ * GC mark function for list storage.
*/
void nm_list_storage_mark(STORAGE* storage_base) {
LIST_STORAGE* storage = (LIST_STORAGE*)storage_base;
@@ -503,6 +688,85 @@ void nm_list_storage_mark(STORAGE* storage_base) {
}
}
+static void __nm_list_storage_unregister_temp_value_list(std::list<VALUE*>& temp_vals) {
+ for (std::list<VALUE*>::iterator it = temp_vals.begin(); it != temp_vals.end(); ++it) {
+ nm_unregister_value(**it);
+ }
+}
+
+static void __nm_list_storage_unregister_temp_list_list(std::list<LIST*>& temp_vals, size_t recursions) {
+ for (std::list<LIST*>::iterator it = temp_vals.begin(); it != temp_vals.end(); ++it) {
+ nm_list_storage_unregister_list(*it, recursions);
+ }
+}
+
+void nm_list_storage_register_node(const NODE* curr) {
+ nm_register_value(*reinterpret_cast<VALUE*>(curr->val));
+}
+
+void nm_list_storage_unregister_node(const NODE* curr) {
+ nm_unregister_value(*reinterpret_cast<VALUE*>(curr->val));
+}
+
+/**
+ * Gets rid of all instances of a given node in the registration list.
+ * Sometimes a node will get deleted and replaced deep in a recursion, but
+ * further up it will still get registered. This leads to a potential read
+ * after free during the GC marking. This function completely clears out a
+ * node so that this won't happen.
+ */
+void nm_list_storage_completely_unregister_node(const NODE* curr) {
+ nm_completely_unregister_value(*reinterpret_cast<VALUE*>(curr->val));
+}
+
+void nm_list_storage_register_list(const LIST* list, size_t recursions) {
+ NODE* next;
+ if (!list) return;
+ NODE* curr = list->first;
+
+ while (curr != NULL) {
+ next = curr->next;
+ if (recursions == 0) {
+ nm_list_storage_register_node(curr);
+ } else {
+ nm_list_storage_register_list(reinterpret_cast<LIST*>(curr->val), recursions - 1);
+ }
+ curr = next;
+ }
+}
+
+void nm_list_storage_unregister_list(const LIST* list, size_t recursions) {
+ NODE* next;
+ if (!list) return;
+ NODE* curr = list->first;
+
+ while (curr != NULL) {
+ next = curr->next;
+ if (recursions == 0) {
+ nm_list_storage_unregister_node(curr);
+ } else {
+ nm_list_storage_unregister_list(reinterpret_cast<LIST*>(curr->val), recursions - 1);
+ }
+ curr = next;
+ }
+}
+
+void nm_list_storage_register(const STORAGE* s) {
+ const LIST_STORAGE* storage = reinterpret_cast<const LIST_STORAGE*>(s);
+ if (storage && storage->dtype == nm::RUBYOBJ) {
+ nm_register_value(*reinterpret_cast<VALUE*>(storage->default_val));
+ nm_list_storage_register_list(storage->rows, storage->dim - 1);
+ }
+}
+
+void nm_list_storage_unregister(const STORAGE* s) {
+ const LIST_STORAGE* storage = reinterpret_cast<const LIST_STORAGE*>(s);
+ if (storage && storage->dtype == nm::RUBYOBJ) {
+ nm_unregister_value(*reinterpret_cast<VALUE*>(storage->default_val));
+ nm_list_storage_unregister_list(storage->rows, storage->dim - 1);
+ }
+}
+
///////////////
// Accessors //
///////////////
@@ -510,8 +774,7 @@ void nm_list_storage_mark(STORAGE* storage_base) {
/*
* Documentation goes here.
*/
-static NODE* list_storage_get_single_node(LIST_STORAGE* s, SLICE* slice)
-{
+static NODE* list_storage_get_single_node(LIST_STORAGE* s, SLICE* slice) {
size_t r;
LIST* l = s->rows;
NODE* n;
@@ -532,6 +795,7 @@ static NODE* list_storage_get_single_node(LIST_STORAGE* s, SLICE* slice)
*/
static void each_empty_with_indices_r(nm::list_storage::RecurseData& s, size_t rec, VALUE& stack) {
VALUE empty = s.dtype() == nm::RUBYOBJ ? *reinterpret_cast<VALUE*>(s.init()) : s.init_obj();
+ NM_CONSERVATIVE(nm_register_value(stack));
if (rec) {
for (long index = 0; index < s.ref_shape(rec); ++index) {
@@ -549,12 +813,16 @@ static void each_empty_with_indices_r(nm::list_storage::RecurseData& s, size_t r
}
rb_ary_shift(stack);
}
+ NM_CONSERVATIVE(nm_unregister_value(stack));
}
/*
* Recursive helper function for each_with_indices, based on nm_list_storage_count_elements_r.
*/
static void each_with_indices_r(nm::list_storage::RecurseData& s, const LIST* l, size_t rec, VALUE& stack) {
+ if (s.dtype() == nm::RUBYOBJ)
+ nm_list_storage_register_list(l, rec);
+ NM_CONSERVATIVE(nm_register_value(stack));
NODE* curr = l->first;
size_t offset = s.offset(rec);
@@ -594,7 +862,9 @@ static void each_with_indices_r(nm::list_storage::RecurseData& s, const LIST* l,
rb_ary_pop(stack);
}
}
-
+ NM_CONSERVATIVE(nm_unregister_value(stack));
+ if (s.dtype() == nm::RUBYOBJ)
+ nm_list_storage_unregister_list(l, rec);
}
@@ -602,6 +872,10 @@ static void each_with_indices_r(nm::list_storage::RecurseData& s, const LIST* l,
* Recursive helper function for each_stored_with_indices, based on nm_list_storage_count_elements_r.
*/
static void each_stored_with_indices_r(nm::list_storage::RecurseData& s, const LIST* l, size_t rec, VALUE& stack) {
+ if (s.dtype() == nm::RUBYOBJ)
+ nm_list_storage_register_list(l, rec);
+ NM_CONSERVATIVE(nm_register_value(stack));
+
NODE* curr = l->first;
size_t offset = s.offset(rec);
@@ -639,6 +913,9 @@ static void each_stored_with_indices_r(nm::list_storage::RecurseData& s, const L
if (curr && curr->key - offset >= shape) curr = NULL;
}
}
+ NM_CONSERVATIVE(nm_unregister_value(stack));
+ if (s.dtype() == nm::RUBYOBJ)
+ nm_list_storage_unregister_list(l, rec);
}
@@ -648,7 +925,11 @@ static void each_stored_with_indices_r(nm::list_storage::RecurseData& s, const L
*/
VALUE nm_list_each_with_indices(VALUE nmatrix, bool stored) {
+ NM_CONSERVATIVE(nm_register_value(nmatrix));
+
// If we don't have a block, return an enumerator.
+ RETURN_SIZED_ENUMERATOR_PRE
+ NM_CONSERVATIVE(nm_unregister_value(nmatrix));
RETURN_SIZED_ENUMERATOR(nmatrix, 0, 0, 0);
nm::list_storage::RecurseData sdata(NM_STORAGE_LIST(nmatrix));
@@ -658,6 +939,7 @@ VALUE nm_list_each_with_indices(VALUE nmatrix, bool stored) {
if (stored) each_stored_with_indices_r(sdata, sdata.top_level_list(), sdata.dim() - 1, stack);
else each_with_indices_r(sdata, sdata.top_level_list(), sdata.dim() - 1, stack);
+ NM_CONSERVATIVE(nm_unregister_value(nmatrix));
return nmatrix;
}
@@ -665,7 +947,63 @@ VALUE nm_list_each_with_indices(VALUE nmatrix, bool stored) {
/*
* map merged stored iterator. Always returns a matrix containing RubyObjects which probably needs to be casted.
*/
+VALUE nm_list_map_stored(VALUE left, VALUE init) {
+ NM_CONSERVATIVE(nm_register_value(left));
+ NM_CONSERVATIVE(nm_register_value(init));
+
+ bool scalar = false;
+
+ LIST_STORAGE *s = NM_STORAGE_LIST(left);
+
+ // For each matrix, if it's a reference, we want to deal directly with the original (with appropriate offsetting)
+ nm::list_storage::RecurseData sdata(s);
+
+ void* scalar_init = NULL;
+
+ //if (!rb_block_given_p()) {
+ // rb_raise(rb_eNotImpError, "RETURN_SIZED_ENUMERATOR probably won't work for a map_merged since no merged object is created");
+ //}
+ // If we don't have a block, return an enumerator.
+ RETURN_SIZED_ENUMERATOR_PRE
+ NM_CONSERVATIVE(nm_unregister_value(left));
+ NM_CONSERVATIVE(nm_unregister_value(init));
+ RETURN_SIZED_ENUMERATOR(left, 0, 0, 0); // FIXME: Test this. Probably won't work. Enable above code instead.
+
+ // Figure out default value if none provided by the user
+ if (init == Qnil) {
+ nm_unregister_value(init);
+ init = rb_yield_values(1, sdata.init_obj());
+ nm_register_value(init);
+ }
+ // Allocate a new shape array for the resulting matrix.
+ void* init_val = NM_ALLOC(VALUE);
+ memcpy(init_val, &init, sizeof(VALUE));
+ nm_register_value(*reinterpret_cast<VALUE*>(init_val));
+
+ NMATRIX* result = nm_create(nm::LIST_STORE, nm_list_storage_create(nm::RUBYOBJ, sdata.copy_alloc_shape(), s->dim, init_val));
+ LIST_STORAGE* r = reinterpret_cast<LIST_STORAGE*>(result->storage);
+ nm::list_storage::RecurseData rdata(r, init);
+ nm_register_nmatrix(result);
+ map_stored_r(rdata, sdata, rdata.top_level_list(), sdata.top_level_list(), sdata.dim() - 1);
+
+ VALUE to_return = Data_Wrap_Struct(CLASS_OF(left), nm_mark, nm_delete, result);
+
+ nm_unregister_nmatrix(result);
+ nm_unregister_value(*reinterpret_cast<VALUE*>(init_val));
+ NM_CONSERVATIVE(nm_unregister_value(init));
+ NM_CONSERVATIVE(nm_unregister_value(left));
+
+ return to_return;
+}
+
+
+/*
+ * map merged stored iterator. Always returns a matrix containing RubyObjects which probably needs to be casted.
+ */
VALUE nm_list_map_merged_stored(VALUE left, VALUE right, VALUE init) {
+ NM_CONSERVATIVE(nm_register_value(left));
+ NM_CONSERVATIVE(nm_register_value(right));
+ NM_CONSERVATIVE(nm_register_value(init));
bool scalar = false;
@@ -679,8 +1017,7 @@ VALUE nm_list_map_merged_stored(VALUE left, VALUE right, VALUE init) {
// right might be a scalar, in which case this is a scalar operation.
if (TYPE(right) != T_DATA || (RDATA(right)->dfree != (RUBY_DATA_FUNC)nm_delete && RDATA(right)->dfree != (RUBY_DATA_FUNC)nm_delete_ref)) {
- nm::dtype_t r_dtype = nm_dtype_min(right);
-
+ nm::dtype_t r_dtype = Upcast[NM_DTYPE(left)][nm_dtype_min(right)];
scalar_init = rubyobj_to_cval(right, r_dtype); // make a copy of right
t = reinterpret_cast<LIST_STORAGE*>(nm_list_storage_create(r_dtype, sdata.copy_alloc_shape(), s->dim, scalar_init));
@@ -693,26 +1030,43 @@ VALUE nm_list_map_merged_stored(VALUE left, VALUE right, VALUE init) {
// rb_raise(rb_eNotImpError, "RETURN_SIZED_ENUMERATOR probably won't work for a map_merged since no merged object is created");
//}
// If we don't have a block, return an enumerator.
+ RETURN_SIZED_ENUMERATOR_PRE
+ NM_CONSERVATIVE(nm_unregister_value(left));
+ NM_CONSERVATIVE(nm_unregister_value(right));
+ NM_CONSERVATIVE(nm_unregister_value(init));
RETURN_SIZED_ENUMERATOR(left, 0, 0, 0); // FIXME: Test this. Probably won't work. Enable above code instead.
// Figure out default value if none provided by the user
- nm::list_storage::RecurseData tdata(t);
- if (init == Qnil) init = rb_yield_values(2, sdata.init_obj(), tdata.init_obj());
+ nm::list_storage::RecurseData& tdata = *(new nm::list_storage::RecurseData(t)); //FIXME: this is a hack to make sure that we can run the destructor before nm_list_storage_delete(t) below.
+ if (init == Qnil) {
+ nm_unregister_value(init);
+ init = rb_yield_values(2, sdata.init_obj(), tdata.init_obj());
+ nm_register_value(init);
+ }
- // Allocate a new shape array for the resulting matrix.
- void* init_val = ALLOC(VALUE);
+ // Allocate a new shape array for the resulting matrix.
+ void* init_val = NM_ALLOC(VALUE);
memcpy(init_val, &init, sizeof(VALUE));
+ nm_register_value(*reinterpret_cast<VALUE*>(init_val));
NMATRIX* result = nm_create(nm::LIST_STORE, nm_list_storage_create(nm::RUBYOBJ, sdata.copy_alloc_shape(), s->dim, init_val));
LIST_STORAGE* r = reinterpret_cast<LIST_STORAGE*>(result->storage);
nm::list_storage::RecurseData rdata(r, init);
-
map_merged_stored_r(rdata, sdata, tdata, rdata.top_level_list(), sdata.top_level_list(), tdata.top_level_list(), sdata.dim() - 1);
+ delete &tdata;
// If we are working with a scalar operation
if (scalar) nm_list_storage_delete(t);
- return Data_Wrap_Struct(CLASS_OF(left), nm_mark, nm_delete, result);
+ VALUE to_return = Data_Wrap_Struct(CLASS_OF(left), nm_mark, nm_delete, result);
+
+ nm_unregister_value(*reinterpret_cast<VALUE*>(init_val));
+
+ NM_CONSERVATIVE(nm_unregister_value(init));
+ NM_CONSERVATIVE(nm_unregister_value(right));
+ NM_CONSERVATIVE(nm_unregister_value(left));
+
+ return to_return;
}
@@ -720,13 +1074,14 @@ VALUE nm_list_map_merged_stored(VALUE left, VALUE right, VALUE init) {
* Copy a slice of a list matrix into a regular list matrix.
*/
static LIST* slice_copy(const LIST_STORAGE* src, LIST* src_rows, size_t* coords, size_t* lengths, size_t n) {
-
+ nm_list_storage_register(src);
void *val = NULL;
int key;
LIST* dst_rows = nm::list::create();
NODE* src_node = src_rows->first;
-
+ std::list<VALUE*> temp_vals;
+ std::list<LIST*> temp_lists;
while (src_node) {
key = src_node->key - (src->offset[n] + coords[n]);
@@ -737,16 +1092,28 @@ static LIST* slice_copy(const LIST_STORAGE* src, LIST* src_rows, size_t* coords,
coords,
lengths,
n + 1 );
-
- if (val) { nm::list::insert_copy(dst_rows, false, key, val, sizeof(LIST)); }
+ if (val) {
+ if (src->dtype == nm::RUBYOBJ) {
+ nm_list_storage_register_list(reinterpret_cast<LIST*>(val), src->dim - n - 2);
+ temp_lists.push_front(reinterpret_cast<LIST*>(val));
+ }
+ nm::list::insert_copy(dst_rows, false, key, val, sizeof(LIST));
+ }
+ } else { // matches src->dim - n > 1
+ if (src->dtype == nm::RUBYOBJ) {
+ nm_register_value(*reinterpret_cast<VALUE*>(src_node->val));
+ temp_vals.push_front(reinterpret_cast<VALUE*>(src_node->val));
+ }
+ nm::list::insert_copy(dst_rows, false, key, src_node->val, DTYPE_SIZES[src->dtype]);
}
-
- else nm::list::insert_copy(dst_rows, false, key, src_node->val, DTYPE_SIZES[src->dtype]);
}
-
src_node = src_node->next;
+ }
+ if (src->dtype == nm::RUBYOBJ) {
+ __nm_list_storage_unregister_temp_list_list(temp_lists, src->dim - n - 2);
+ __nm_list_storage_unregister_temp_value_list(temp_vals);
}
-
+ nm_list_storage_unregister(src);
return dst_rows;
}
@@ -756,21 +1123,31 @@ static LIST* slice_copy(const LIST_STORAGE* src, LIST* src_rows, size_t* coords,
void* nm_list_storage_get(const STORAGE* storage, SLICE* slice) {
LIST_STORAGE* s = (LIST_STORAGE*)storage;
LIST_STORAGE* ns = NULL;
- NODE* n;
+
+ nm_list_storage_register(s);
if (slice->single) {
- n = list_storage_get_single_node(s, slice);
+ NODE* n = list_storage_get_single_node(s, slice);
+ nm_list_storage_unregister(s);
return (n ? n->val : s->default_val);
+
} else {
- void *init_val = ALLOC_N(char, DTYPE_SIZES[s->dtype]);
+ void *init_val = NM_ALLOC_N(char, DTYPE_SIZES[s->dtype]);
memcpy(init_val, s->default_val, DTYPE_SIZES[s->dtype]);
+ if (s->dtype == nm::RUBYOBJ)
+ nm_register_value(*reinterpret_cast<VALUE*>(init_val));
- size_t *shape = ALLOC_N(size_t, s->dim);
+ size_t *shape = NM_ALLOC_N(size_t, s->dim);
memcpy(shape, slice->lengths, sizeof(size_t) * s->dim);
ns = nm_list_storage_create(s->dtype, shape, s->dim, init_val);
-
+
ns->rows = slice_copy(s, s->rows, slice->coords, slice->lengths, 0);
+
+ if (s->dtype == nm::RUBYOBJ)
+ nm_unregister_value(*reinterpret_cast<VALUE*>(init_val));
+ nm_list_storage_unregister(s);
+
return ns;
}
}
@@ -782,20 +1159,21 @@ void* nm_list_storage_get(const STORAGE* storage, SLICE* slice) {
void* nm_list_storage_ref(const STORAGE* storage, SLICE* slice) {
LIST_STORAGE* s = (LIST_STORAGE*)storage;
LIST_STORAGE* ns = NULL;
- NODE* n;
+ nm_list_storage_register(s);
//TODO: It needs a refactoring.
if (slice->single) {
- n = list_storage_get_single_node(s, slice);
+ NODE* n = list_storage_get_single_node(s, slice);
+ nm_list_storage_unregister(s);
return (n ? n->val : s->default_val);
}
else {
- ns = ALLOC( LIST_STORAGE );
+ ns = NM_ALLOC( LIST_STORAGE );
ns->dim = s->dim;
ns->dtype = s->dtype;
- ns->offset = ALLOC_N(size_t, ns->dim);
- ns->shape = ALLOC_N(size_t, ns->dim);
+ ns->offset = NM_ALLOC_N(size_t, ns->dim);
+ ns->shape = NM_ALLOC_N(size_t, ns->dim);
for (size_t i = 0; i < ns->dim; ++i) {
ns->offset[i] = slice->coords[i] + s->offset[i];
@@ -807,7 +1185,7 @@ void* nm_list_storage_ref(const STORAGE* storage, SLICE* slice) {
s->src->count++;
ns->src = s->src;
-
+ nm_list_storage_unregister(s);
return ns;
}
}
@@ -817,10 +1195,16 @@ void* nm_list_storage_ref(const STORAGE* storage, SLICE* slice) {
* Recursive function, sets multiple values in a matrix from a single source value.
*/
static void slice_set_single(LIST_STORAGE* dest, LIST* l, void* val, size_t* coords, size_t* lengths, size_t n) {
+ nm_list_storage_register(dest);
+ if (dest->dtype == nm::RUBYOBJ) {
+ nm_register_value(*reinterpret_cast<VALUE*>(val));
+ nm_list_storage_register_list(l, dest->dim - n - 1);
+ }
// drill down into the structure
NODE* node = NULL;
if (dest->dim - n > 1) {
+ std::list<LIST*> temp_nodes;
for (size_t i = 0; i < lengths[n]; ++i) {
size_t key = i + dest->offset[n] + coords[n];
@@ -833,10 +1217,17 @@ static void slice_set_single(LIST_STORAGE* dest, LIST* l, void* val, size_t* coo
node = node->next; // correct rank already exists.
}
+ if (dest->dtype == nm::RUBYOBJ) {
+ temp_nodes.push_front(reinterpret_cast<LIST*>(node->val));
+ nm_list_storage_register_list(reinterpret_cast<LIST*>(node->val), dest->dim - n - 2);
+ }
+
// cast it to a list and recurse
slice_set_single(dest, reinterpret_cast<LIST*>(node->val), val, coords, lengths, n + 1);
}
+ __nm_list_storage_unregister_temp_list_list(temp_nodes, dest->dim - n - 2);
} else {
+ std::list<VALUE*> temp_vals;
for (size_t i = 0; i < lengths[n]; ++i) {
size_t key = i + dest->offset[n] + coords[n];
@@ -846,7 +1237,18 @@ static void slice_set_single(LIST_STORAGE* dest, LIST* l, void* val, size_t* coo
} else {
node = nm::list::replace_insert_after(node, key, val, true, DTYPE_SIZES[dest->dtype]);
}
+ if (dest->dtype == nm::RUBYOBJ) {
+ temp_vals.push_front(reinterpret_cast<VALUE*>(node->val));
+ nm_register_value(*reinterpret_cast<VALUE*>(node->val));
+ }
}
+ __nm_list_storage_unregister_temp_value_list(temp_vals);
+ }
+
+ nm_list_storage_unregister(dest);
+ if (dest->dtype == nm::RUBYOBJ) {
+ nm_unregister_value(*reinterpret_cast<VALUE*>(val));
+ nm_list_storage_unregister_list(l, dest->dim - n - 1);
}
}
@@ -870,6 +1272,9 @@ void nm_list_storage_set(VALUE left, SLICE* slice, VALUE right) {
*/
NODE* nm_list_storage_insert(STORAGE* storage, SLICE* slice, void* val) {
LIST_STORAGE* s = (LIST_STORAGE*)storage;
+ nm_list_storage_register(s);
+ if (s->dtype == nm::RUBYOBJ)
+ nm_register_value(*reinterpret_cast<VALUE*>(val));
// Pretend dims = 2
// Then coords is going to be size 2
// So we need to find out if some key already exists
@@ -878,12 +1283,16 @@ NODE* nm_list_storage_insert(STORAGE* storage, SLICE* slice, void* val) {
LIST* l = s->rows;
// drill down into the structure
- for (r = s->dim; r > 1; --r) {
- n = nm::list::insert(l, false, s->offset[s->dim - r] + slice->coords[s->dim - r], nm::list::create());
+ for (r = 0; r < s->dim -1; ++r) {
+ n = nm::list::insert(l, false, s->offset[r] + slice->coords[s->dim - r], nm::list::create());
l = reinterpret_cast<LIST*>(n->val);
}
- return nm::list::insert(l, true, s->offset[s->dim - r] + slice->coords[s->dim - r], val);
+ nm_list_storage_unregister(s);
+ if (s->dtype == nm::RUBYOBJ)
+ nm_unregister_value(*reinterpret_cast<VALUE*>(val));
+
+ return nm::list::insert(l, true, s->offset[r] + slice->coords[r], val);
}
/*
@@ -937,10 +1346,10 @@ STORAGE* nm_list_storage_matrix_multiply(const STORAGE_PAIR& casted_storage, siz
* it's a sparse matrix.
*/
VALUE nm_list_storage_to_hash(const LIST_STORAGE* s, const nm::dtype_t dtype) {
-
+ nm_list_storage_register(s);
// Get the default value for the list storage.
VALUE default_value = rubyobj_from_cval(s->default_val, dtype).rval;
-
+ nm_list_storage_unregister(s);
// Recursively copy each dimension of the matrix into a nested hash.
return nm_list_copy_to_hash(s->rows, dtype, s->dim - 1, default_value);
}
@@ -1006,18 +1415,21 @@ size_t nm_list_storage_count_nd_elements(const LIST_STORAGE* s) {
* List storage copy constructor C access.
*/
-LIST_STORAGE* nm_list_storage_copy(const LIST_STORAGE* rhs)
-{
- size_t *shape = ALLOC_N(size_t, rhs->dim);
+LIST_STORAGE* nm_list_storage_copy(const LIST_STORAGE* rhs) {
+ nm_list_storage_register(rhs);
+ size_t *shape = NM_ALLOC_N(size_t, rhs->dim);
memcpy(shape, rhs->shape, sizeof(size_t) * rhs->dim);
- void *init_val = ALLOC_N(char, DTYPE_SIZES[rhs->dtype]);
+ void *init_val = NM_ALLOC_N(char, DTYPE_SIZES[rhs->dtype]);
memcpy(init_val, rhs->default_val, DTYPE_SIZES[rhs->dtype]);
LIST_STORAGE* lhs = nm_list_storage_create(rhs->dtype, shape, rhs->dim, init_val);
-
+ nm_list_storage_register(lhs);
+
lhs->rows = slice_copy(rhs, rhs->rows, lhs->offset, lhs->shape, 0);
+ nm_list_storage_unregister(rhs);
+ nm_list_storage_unregister(lhs);
return lhs;
}
@@ -1057,27 +1469,31 @@ namespace list_storage {
*/
template <typename LDType, typename RDType>
static LIST_STORAGE* cast_copy(const LIST_STORAGE* rhs, dtype_t new_dtype) {
-
+ nm_list_storage_register(rhs);
// allocate and copy shape
- size_t* shape = ALLOC_N(size_t, rhs->dim);
+ size_t* shape = NM_ALLOC_N(size_t, rhs->dim);
memcpy(shape, rhs->shape, rhs->dim * sizeof(size_t));
// copy default value
- LDType* default_val = ALLOC_N(LDType, 1);
+ LDType* default_val = NM_ALLOC_N(LDType, 1);
*default_val = *reinterpret_cast<RDType*>(rhs->default_val);
LIST_STORAGE* lhs = nm_list_storage_create(new_dtype, shape, rhs->dim, default_val);
//lhs->rows = nm::list::create();
+ nm_list_storage_register(lhs);
// TODO: Needs optimization. When matrix is reference it is copped twice.
if (rhs->src == rhs)
nm::list::cast_copy_contents<LDType, RDType>(lhs->rows, rhs->rows, rhs->dim - 1);
else {
LIST_STORAGE *tmp = nm_list_storage_copy(rhs);
+ nm_list_storage_register(tmp);
nm::list::cast_copy_contents<LDType, RDType>(lhs->rows, tmp->rows, rhs->dim - 1);
+ nm_list_storage_unregister(tmp);
nm_list_storage_delete(tmp);
}
-
+ nm_list_storage_unregister(lhs);
+ nm_list_storage_unregister(rhs);
return lhs;
}
@@ -1196,13 +1612,16 @@ extern "C" {
return nm_list_storage_to_hash(NM_STORAGE_LIST(self), NM_DTYPE(self));
}
- /*
- * call-seq:
- * __list_default_value__ -> ...
- *
- * Get the default_value property from a list matrix.
- */
- VALUE nm_list_default_value(VALUE self) {
- return (NM_DTYPE(self) == nm::RUBYOBJ) ? *reinterpret_cast<VALUE*>(NM_DEFAULT_VAL(self)) : rubyobj_from_cval(NM_DEFAULT_VAL(self), NM_DTYPE(self)).rval;
- }
+ /*
+ * call-seq:
+ * __list_default_value__ -> ...
+ *
+ * Get the default_value property from a list matrix.
+ */
+ VALUE nm_list_default_value(VALUE self) {
+ NM_CONSERVATIVE(nm_register_value(self));
+ VALUE to_return = (NM_DTYPE(self) == nm::RUBYOBJ) ? *reinterpret_cast<VALUE*>(NM_DEFAULT_VAL(self)) : rubyobj_from_cval(NM_DEFAULT_VAL(self), NM_DTYPE(self)).rval;
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ return to_return;
+ }
} // end of extern "C" block
diff --git a/ext/nmatrix/storage/list.h b/ext/nmatrix/storage/list/list.h
similarity index 78%
rename from ext/nmatrix/storage/list.h
rename to ext/nmatrix/storage/list/list.h
index f10824b..9e15a5f 100644
--- a/ext/nmatrix/storage/list.h
+++ b/ext/nmatrix/storage/list/list.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -34,14 +34,14 @@
*/
#include <stdlib.h>
-
+#include <list>
/*
* Project Includes
*/
#include "types.h"
#include "data/data.h"
-#include "common.h"
+#include "../common.h"
#include "util/sl_list.h"
#include "nmatrix.h"
@@ -73,7 +73,13 @@ extern "C" {
void nm_list_storage_delete(STORAGE* s);
void nm_list_storage_delete_ref(STORAGE* s);
void nm_list_storage_mark(STORAGE*);
-
+ void nm_list_storage_register(const STORAGE* s);
+ void nm_list_storage_unregister(const STORAGE* s);
+ void nm_list_storage_register_list(const LIST* l, size_t recursions);
+ void nm_list_storage_unregister_list(const LIST* l, size_t recursions);
+ void nm_list_storage_register_node(const NODE* n);
+ void nm_list_storage_unregister_node(const NODE* n);
+ void nm_list_storage_completely_unregister_node(const NODE* curr);
///////////////
// Accessors //
///////////////
@@ -82,7 +88,7 @@ extern "C" {
void* nm_list_storage_ref(const STORAGE* s, SLICE* slice);
void* nm_list_storage_get(const STORAGE* s, SLICE* slice);
NODE* nm_list_storage_insert(STORAGE* s, SLICE* slice, void* val);
- void nm_list_storage_set(VALUE left, SLICE* slice, VALUE right);
+ void nm_list_storage_set(VALUE left, SLICE* slice, VALUE right);
void nm_list_storage_remove(STORAGE* s, SLICE* slice);
///////////
@@ -124,6 +130,7 @@ extern "C" {
// Exposed functions
VALUE nm_to_hash(VALUE self);
VALUE nm_list_map_merged_stored(VALUE left, VALUE right, VALUE init);
+ VALUE nm_list_map_stored(VALUE left, VALUE init);
VALUE nm_list_default_value(VALUE self);
} // end of extern "C" block
diff --git a/ext/nmatrix/storage/storage.cpp b/ext/nmatrix/storage/storage.cpp
index 97af774..5ceed36 100644
--- a/ext/nmatrix/storage/storage.cpp
+++ b/ext/nmatrix/storage/storage.cpp
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -87,9 +87,9 @@ static void cast_copy_list_default(LDType* lhs, RDType* default_val, size_t& pos
*/
template <typename LDType, typename RDType>
DENSE_STORAGE* create_from_list_storage(const LIST_STORAGE* rhs, dtype_t l_dtype) {
-
+ nm_list_storage_register(rhs);
// allocate and copy shape
- size_t* shape = ALLOC_N(size_t, rhs->dim);
+ size_t* shape = NM_ALLOC_N(size_t, rhs->dim);
memcpy(shape, rhs->shape, rhs->dim * sizeof(size_t));
DENSE_STORAGE* lhs = nm_dense_storage_create(l_dtype, shape, rhs->dim, NULL, 0);
@@ -114,6 +114,7 @@ DENSE_STORAGE* create_from_list_storage(const LIST_STORAGE* rhs, dtype_t l_dtype
nm_list_storage_delete(tmp);
}
+ nm_list_storage_unregister(rhs);
return lhs;
}
@@ -127,12 +128,13 @@ DENSE_STORAGE* create_from_list_storage(const LIST_STORAGE* rhs, dtype_t l_dtype
template <typename LDType, typename RDType>
DENSE_STORAGE* create_from_yale_storage(const YALE_STORAGE* rhs, dtype_t l_dtype) {
+ nm_yale_storage_register(rhs);
// Position in rhs->elements.
IType* rhs_ija = reinterpret_cast<YALE_STORAGE*>(rhs->src)->ija;
RDType* rhs_a = reinterpret_cast<RDType*>(reinterpret_cast<YALE_STORAGE*>(rhs->src)->a);
// Allocate and set shape.
- size_t* shape = ALLOC_N(size_t, rhs->dim);
+ size_t* shape = NM_ALLOC_N(size_t, rhs->dim);
shape[0] = rhs->shape[0];
shape[1] = rhs->shape[1];
@@ -195,6 +197,7 @@ DENSE_STORAGE* create_from_yale_storage(const YALE_STORAGE* rhs, dtype_t l_dtype
}
}
}
+ nm_yale_storage_unregister(rhs);
return lhs;
}
@@ -209,7 +212,9 @@ static void cast_copy_list_contents(LDType* lhs, const LIST* rhs, RDType* defaul
NODE *curr = rhs->first;
int last_key = -1;
- for (size_t i = 0; i < shape[dim - 1 - recursions]; ++i, ++pos) {
+ nm_list_storage_register_list(rhs, recursions);
+
+ for (size_t i = 0; i < shape[dim - 1 - recursions]; ++i, ++pos) {
if (!curr || (curr->key > (size_t)(last_key+1))) {
@@ -229,6 +234,8 @@ static void cast_copy_list_contents(LDType* lhs, const LIST* rhs, RDType* defaul
}
}
+ nm_list_storage_unregister_list(rhs, recursions);
+
--pos;
}
@@ -237,7 +244,7 @@ static void cast_copy_list_contents(LDType* lhs, const LIST* rhs, RDType* defaul
*/
template <typename LDType,typename RDType>
static void cast_copy_list_default(LDType* lhs, RDType* default_val, size_t& pos, const size_t* shape, size_t dim, size_t max_elements, size_t recursions) {
- for (size_t i = 0; i < shape[dim - 1 - recursions]; ++i, ++pos) {
+ for (size_t i = 0; i < shape[dim - 1 - recursions]; ++i, ++pos) {
if (recursions == 0) lhs[pos] = static_cast<LDType>(*default_val);
else cast_copy_list_default<LDType,RDType>(lhs, default_val, pos, shape, dim, max_elements, recursions-1);
@@ -261,13 +268,14 @@ static bool cast_copy_contents_dense(LIST* lhs, const RDType* rhs, RDType* zero,
*/
template <typename LDType, typename RDType>
LIST_STORAGE* create_from_dense_storage(const DENSE_STORAGE* rhs, dtype_t l_dtype, void* init) {
+ nm_dense_storage_register(rhs);
- LDType* l_default_val = ALLOC_N(LDType, 1);
- RDType* r_default_val = ALLOCA_N(RDType, 1); // clean up when finished with this function
+ LDType* l_default_val = NM_ALLOC_N(LDType, 1);
+ RDType* r_default_val = NM_ALLOCA_N(RDType, 1); // clean up when finished with this function
// allocate and copy shape and coords
- size_t *shape = ALLOC_N(size_t, rhs->dim),
- *coords = ALLOC_N(size_t, rhs->dim);
+ size_t *shape = NM_ALLOC_N(size_t, rhs->dim),
+ *coords = NM_ALLOC_N(size_t, rhs->dim);
memcpy(shape, rhs->shape, rhs->dim * sizeof(size_t));
memset(coords, 0, rhs->dim * sizeof(size_t));
@@ -286,6 +294,8 @@ LIST_STORAGE* create_from_dense_storage(const DENSE_STORAGE* rhs, dtype_t l_dtyp
LIST_STORAGE* lhs = nm_list_storage_create(l_dtype, shape, rhs->dim, l_default_val);
+ nm_list_storage_register(lhs);
+
size_t pos = 0;
if (rhs->src == rhs)
@@ -303,6 +313,9 @@ LIST_STORAGE* create_from_dense_storage(const DENSE_STORAGE* rhs, dtype_t l_dtyp
nm_dense_storage_delete(tmp);
}
+ nm_list_storage_unregister(lhs);
+ nm_dense_storage_unregister(rhs);
+
return lhs;
}
@@ -314,14 +327,16 @@ LIST_STORAGE* create_from_dense_storage(const DENSE_STORAGE* rhs, dtype_t l_dtyp
template <typename LDType, typename RDType>
LIST_STORAGE* create_from_yale_storage(const YALE_STORAGE* rhs, dtype_t l_dtype) {
// allocate and copy shape
- size_t *shape = ALLOC_N(size_t, rhs->dim);
+ nm_yale_storage_register(rhs);
+
+ size_t *shape = NM_ALLOC_N(size_t, rhs->dim);
shape[0] = rhs->shape[0]; shape[1] = rhs->shape[1];
RDType* rhs_a = reinterpret_cast<RDType*>(reinterpret_cast<YALE_STORAGE*>(rhs->src)->a);
RDType R_ZERO = rhs_a[ rhs->src->shape[0] ];
// copy default value from the zero location in the Yale matrix
- LDType* default_val = ALLOC_N(LDType, 1);
+ LDType* default_val = NM_ALLOC_N(LDType, 1);
*default_val = static_cast<LDType>(R_ZERO);
LIST_STORAGE* lhs = nm_list_storage_create(l_dtype, shape, rhs->dim, default_val);
@@ -360,7 +375,7 @@ LIST_STORAGE* create_from_yale_storage(const YALE_STORAGE* rhs, dtype_t l_dtype)
// Is there a nonzero diagonal item between the previously added item and the current one?
if (rj > ri && add_diag) {
// Allocate and copy insertion value
- insert_val = ALLOC_N(LDType, 1);
+ insert_val = NM_ALLOC_N(LDType, 1);
*insert_val = static_cast<LDType>(rhs_a[ri]);
// Insert the item in the list at the appropriate location.
@@ -375,7 +390,7 @@ LIST_STORAGE* create_from_yale_storage(const YALE_STORAGE* rhs, dtype_t l_dtype)
}
// now allocate and add the current item
- insert_val = ALLOC_N(LDType, 1);
+ insert_val = NM_ALLOC_N(LDType, 1);
*insert_val = static_cast<LDType>(rhs_a[ija]);
if (last_added) last_added = list::insert_after(last_added, j, insert_val);
@@ -387,7 +402,7 @@ LIST_STORAGE* create_from_yale_storage(const YALE_STORAGE* rhs, dtype_t l_dtype)
if (add_diag) {
// still haven't added the diagonal.
- insert_val = ALLOC_N(LDType, 1);
+ insert_val = NM_ALLOC_N(LDType, 1);
*insert_val = static_cast<LDType>(rhs_a[ri]);
// insert the item in the list at the appropriate location
@@ -405,6 +420,8 @@ LIST_STORAGE* create_from_yale_storage(const YALE_STORAGE* rhs, dtype_t l_dtype)
// end of walk through rows
}
+ nm_yale_storage_unregister(rhs);
+
return lhs;
}
@@ -415,6 +432,9 @@ LIST_STORAGE* create_from_yale_storage(const YALE_STORAGE* rhs, dtype_t l_dtype)
*/
template <typename LDType, typename RDType>
static bool cast_copy_contents_dense(LIST* lhs, const RDType* rhs, RDType* zero, size_t& pos, size_t* coords, const size_t* shape, size_t dim, size_t recursions) {
+
+ nm_list_storage_register_list(lhs, recursions);
+
NODE *prev = NULL;
LIST *sub_list;
bool added = false, added_list = false;
@@ -429,7 +449,7 @@ static bool cast_copy_contents_dense(LIST* lhs, const RDType* rhs, RDType* zero,
// is not zero
// Create a copy of our value that we will insert in the list
- LDType* insert_value = ALLOC_N(LDType, 1);
+ LDType* insert_value = NM_ALLOC_N(LDType, 1);
*insert_value = static_cast<LDType>(rhs[pos]);
if (!lhs->first) prev = list::insert(lhs, false, coords[dim-1-recursions], insert_value);
@@ -453,6 +473,8 @@ static bool cast_copy_contents_dense(LIST* lhs, const RDType* rhs, RDType* zero,
}
}
+ nm_list_storage_unregister_list(lhs, recursions);
+
coords[dim-1-recursions] = 0;
--pos;
@@ -471,6 +493,8 @@ namespace yale_storage { // FIXME: Move to yale.cpp
if (rhs->dim != 2) rb_raise(nm_eStorageTypeError, "can only convert matrices of dim 2 to yale");
+ nm_dense_storage_register(rhs);
+
IType pos = 0;
IType ndnz = 0;
@@ -495,7 +519,7 @@ namespace yale_storage { // FIXME: Move to yale.cpp
}
// Copy shape for yale construction
- size_t* shape = ALLOC_N(size_t, 2);
+ size_t* shape = NM_ALLOC_N(size_t, 2);
shape[0] = rhs->shape[0];
shape[1] = rhs->shape[1];
@@ -539,6 +563,8 @@ namespace yale_storage { // FIXME: Move to yale.cpp
lhs_ija[shape[0]] = ija; // indicate the end of the last row
lhs->ndnz = ndnz;
+ nm_dense_storage_unregister(rhs);
+
return lhs;
}
@@ -556,10 +582,11 @@ namespace yale_storage { // FIXME: Move to yale.cpp
} else if (strncmp(reinterpret_cast<const char*>(rhs->default_val), "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", DTYPE_SIZES[rhs->dtype]))
rb_raise(nm_eStorageTypeError, "list matrix of non-Ruby objects must have default value of 0 to convert to yale");
+ nm_list_storage_register(rhs);
size_t ndnz = nm_list_storage_count_nd_elements(rhs);
// Copy shape for yale construction
- size_t* shape = ALLOC_N(size_t, 2);
+ size_t* shape = NM_ALLOC_N(size_t, 2);
shape[0] = rhs->shape[0];
shape[1] = rhs->shape[1];
@@ -612,6 +639,8 @@ namespace yale_storage { // FIXME: Move to yale.cpp
lhs_ija[rhs->shape[0]] = ija; // indicate the end of the last row
lhs->ndnz = ndnz;
+ nm_list_storage_unregister(rhs);
+
return lhs;
}
diff --git a/ext/nmatrix/storage/storage.h b/ext/nmatrix/storage/storage.h
index 0a42d39..2b8fd30 100644
--- a/ext/nmatrix/storage/storage.h
+++ b/ext/nmatrix/storage/storage.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -45,8 +45,8 @@
#include "data/data.h"
#include "common.h"
-#include "dense.h"
-#include "list.h"
+#include "dense/dense.h"
+#include "list/list.h"
#include "yale/yale.h"
/*
diff --git a/ext/nmatrix/storage/yale/class.h b/ext/nmatrix/storage/yale/class.h
index c517796..c83822a 100644
--- a/ext/nmatrix/storage/yale/class.h
+++ b/ext/nmatrix/storage/yale/class.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -29,8 +29,9 @@
#ifndef YALE_CLASS_H
# define YALE_CLASS_H
-#include "../dense.h"
+#include "../dense/dense.h"
#include "math/transpose.h"
+#include "yale.h"
namespace nm {
@@ -50,14 +51,22 @@ public:
slice(storage != storage->src),
slice_shape(storage->shape),
slice_offset(storage->offset)
- { }
+ {
+ nm_yale_storage_register(storage->src);
+ }
YaleStorage(const STORAGE* storage)
: s(reinterpret_cast<YALE_STORAGE*>(storage->src)),
slice(storage != storage->src),
slice_shape(storage->shape),
slice_offset(storage->offset)
- { }
+ {
+ nm_yale_storage_register(reinterpret_cast<STORAGE*>(storage->src));
+ }
+
+ ~YaleStorage() {
+ nm_yale_storage_unregister(s);
+ }
/* Allows us to do YaleStorage<uint8>::dtype() to get an nm::dtype_t */
static nm::dtype_t dtype() {
@@ -72,6 +81,7 @@ public:
inline const D& default_obj() const { return a(s->shape[0]); }
inline const D& const_default_obj() const { return a(s->shape[0]); }
+
/*
* Return a Ruby VALUE representation of default_obj()
*/
@@ -99,6 +109,14 @@ public:
/*
+ * Returns true if the value at apos is the default value.
+ * Mainly used for determining if the diagonal contains zeros.
+ */
+ bool is_pos_default_value(size_t apos) const {
+ return (a(apos) == const_default_obj());
+ }
+
+ /*
* Given a size-2 array of size_t, representing the shape, determine
* the maximum size of YaleStorage arrays.
*/
@@ -328,7 +346,7 @@ public:
// Make the necessary modifications, which hopefully can be done in-place.
size_t v_offset = 0;
- int accum = 0;
+ //int accum = 0;
for (size_t ii = 0; ii < lengths[0]; ++ii, ++i) {
i.insert(row_stored_nd_iterator(i, p.pos[ii]), j, lengths[1], v, v_size, v_offset);
}
@@ -344,6 +362,8 @@ public:
*/
void insert(SLICE* slice, VALUE right) {
+ NM_CONSERVATIVE(nm_register_value(right));
+
std::pair<NMATRIX*,bool> nm_and_free =
interpret_arg_as_dense_nmatrix(right, dtype());
// Map the data onto D* v
@@ -358,10 +378,17 @@ public:
} else if (TYPE(right) == T_ARRAY) {
v_size = RARRAY_LEN(right);
- v = ALLOC_N(D, v_size);
+ v = NM_ALLOC_N(D, v_size);
+ if (dtype() == nm::RUBYOBJ) {
+ nm_register_values(reinterpret_cast<VALUE*>(v), v_size);
+ }
for (size_t m = 0; m < v_size; ++m) {
rubyval_to_cval(rb_ary_entry(right, m), s->dtype, &(v[m]));
}
+ if (dtype() == nm::RUBYOBJ) {
+ nm_unregister_values(reinterpret_cast<VALUE*>(v), v_size);
+ }
+
} else {
v = reinterpret_cast<D*>(rubyobj_to_cval(right, dtype()));
}
@@ -381,7 +408,9 @@ public:
if (nm_and_free.second) {
nm_delete(nm_and_free.first);
}
- } else xfree(v);
+ } else NM_FREE(v);
+
+ NM_CONSERVATIVE(nm_unregister_value(right));
}
@@ -489,15 +518,15 @@ public:
* Allocate a reference pointing to s. Note that even if +this+ is a reference,
* we can create a reference within it.
*
- * Note: Make sure you xfree() the result of this call. You can't just cast it
+ * Note: Make sure you NM_FREE() the result of this call. You can't just cast it
* directly into a YaleStorage<D> class.
*/
YALE_STORAGE* alloc_ref(SLICE* slice) {
- YALE_STORAGE* ns = ALLOC( YALE_STORAGE );
+ YALE_STORAGE* ns = NM_ALLOC( YALE_STORAGE );
ns->dim = s->dim;
- ns->offset = ALLOC_N(size_t, ns->dim);
- ns->shape = ALLOC_N(size_t, ns->dim);
+ ns->offset = NM_ALLOC_N(size_t, ns->dim);
+ ns->shape = NM_ALLOC_N(size_t, ns->dim);
for (size_t d = 0; d < ns->dim; ++d) {
ns->offset[d] = slice->coords[d] + offset(d);
@@ -522,12 +551,12 @@ public:
* Allocates and initializes the basic struct (but not IJA or A vectors).
*/
static YALE_STORAGE* alloc(size_t* shape, size_t dim = 2) {
- YALE_STORAGE* s = ALLOC( YALE_STORAGE );
+ YALE_STORAGE* s = NM_ALLOC( YALE_STORAGE );
s->ndnz = 0;
s->dtype = dtype();
s->shape = shape;
- s->offset = ALLOC_N(size_t, dim);
+ s->offset = NM_ALLOC_N(size_t, dim);
for (size_t d = 0; d < dim; ++d)
s->offset[d] = 0;
s->dim = dim;
@@ -556,8 +585,8 @@ public:
s->capacity = reserve;
}
- s->ija = ALLOC_N( size_t, s->capacity );
- s->a = ALLOC_N( D, s->capacity );
+ s->ija = NM_ALLOC_N( size_t, s->capacity );
+ s->a = NM_ALLOC_N( D, s->capacity );
return s;
}
@@ -608,14 +637,14 @@ public:
template <typename E>
YALE_STORAGE* alloc_basic_copy(size_t new_capacity, size_t new_ndnz) const {
nm::dtype_t new_dtype = nm::ctype_to_dtype_enum<E>::value_type;
- YALE_STORAGE* lhs = ALLOC( YALE_STORAGE );
+ YALE_STORAGE* lhs = NM_ALLOC( YALE_STORAGE );
lhs->dim = s->dim;
- lhs->shape = ALLOC_N( size_t, lhs->dim );
+ lhs->shape = NM_ALLOC_N( size_t, lhs->dim );
lhs->shape[0] = shape(0);
lhs->shape[1] = shape(1);
- lhs->offset = ALLOC_N( size_t, lhs->dim );
+ lhs->offset = NM_ALLOC_N( size_t, lhs->dim );
lhs->offset[0] = 0;
lhs->offset[1] = 0;
@@ -623,8 +652,8 @@ public:
lhs->capacity = new_capacity;
lhs->dtype = new_dtype;
lhs->ndnz = new_ndnz;
- lhs->ija = ALLOC_N( size_t, new_capacity );
- lhs->a = ALLOC_N( E, new_capacity );
+ lhs->ija = NM_ALLOC_N( size_t, new_capacity );
+ lhs->a = NM_ALLOC_N( E, new_capacity );
lhs->src = lhs;
lhs->count = 1;
@@ -633,7 +662,7 @@ public:
/*
- * Make a full matrix structure copy (entries remain uninitialized). Remember to xfree()!
+ * Make a full matrix structure copy (entries remain uninitialized). Remember to NM_FREE()!
*/
template <typename E>
YALE_STORAGE* alloc_struct_copy(size_t new_capacity) const {
@@ -655,7 +684,7 @@ public:
*/
template <typename E, bool Yield=false>
void copy(YALE_STORAGE& ns) const {
- nm::dtype_t new_dtype = nm::ctype_to_dtype_enum<E>::value_type;
+ //nm::dtype_t new_dtype = nm::ctype_to_dtype_enum<E>::value_type;
// get the default value for initialization (we'll re-use val for other copies after this)
E val = static_cast<E>(const_default_obj());
@@ -665,6 +694,7 @@ public:
E* ns_a = reinterpret_cast<E*>(ns.a);
size_t sz = shape(0) + 1; // current used size of ns
+ nm_yale_storage_register(&ns);
// FIXME: If diagonals line up, it's probably faster to do this with stored diagonal and stored non-diagonal iterators
for (const_row_iterator it = cribegin(); it != criend(); ++it) {
@@ -681,6 +711,7 @@ public:
}
ns.ija[it.i()+1] = sz;
}
+ nm_yale_storage_unregister(&ns);
//ns.ija[shape(0)] = sz; // indicate end of last row
ns.ndnz = sz - shape(0) - 1; // update ndnz count
@@ -688,17 +719,17 @@ public:
/*
- * Allocate a casted copy of this matrix/reference. Remember to xfree() the result!
+ * Allocate a casted copy of this matrix/reference. Remember to NM_FREE() the result!
*
* If Yield is true, E must be nm::RubyObject, and it will call an rb_yield upon the stored value.
*/
template <typename E, bool Yield = false>
YALE_STORAGE* alloc_copy() const {
- nm::dtype_t new_dtype = nm::ctype_to_dtype_enum<E>::value_type;
+ //nm::dtype_t new_dtype = nm::ctype_to_dtype_enum<E>::value_type;
YALE_STORAGE* lhs;
if (slice) {
- size_t* xshape = ALLOC_N(size_t, 2);
+ size_t* xshape = NM_ALLOC_N(size_t, 2);
xshape[0] = shape(0);
xshape[1] = shape(1);
size_t ndnz = count_copy_ndnz();
@@ -708,6 +739,7 @@ public:
lhs = YaleStorage<E>::create(xshape, reserve);
+ // FIXME: This should probably be a throw which gets caught outside of the object.
if (lhs->capacity < reserve)
rb_raise(nm_eStorageTypeError, "conversion failed; capacity of %lu requested, max allowable is %lu", reserve, lhs->capacity);
@@ -718,10 +750,15 @@ public:
lhs = alloc_struct_copy<E>(s->capacity);
E* la = reinterpret_cast<E*>(lhs->a);
+
+ nm_yale_storage_register(lhs);
for (size_t m = 0; m < size(); ++m) {
- if (Yield) la[m] = rb_yield(nm::yale_storage::nm_rb_dereference(a(m)));
+ if (Yield) {
+ la[m] = rb_yield(nm::yale_storage::nm_rb_dereference(a(m)));
+ }
else la[m] = static_cast<E>(a(m));
}
+ nm_yale_storage_unregister(lhs);
}
@@ -732,7 +769,7 @@ public:
* Allocate a transposed copy of the matrix
*/
/*
- * Allocate a casted copy of this matrix/reference. Remember to xfree() the result!
+ * Allocate a casted copy of this matrix/reference. Remember to NM_FREE() the result!
*
* If Yield is true, E must be nm::RubyObject, and it will call an rb_yield upon the stored value.
*/
@@ -743,7 +780,7 @@ public:
rb_raise(rb_eNotImpError, "please make a copy before transposing");
} else {
// Copy the structure and setup the IJA structure.
- size_t* xshape = ALLOC_N(size_t, 2);
+ size_t* xshape = NM_ALLOC_N(size_t, 2);
xshape[0] = shape(1);
xshape[1] = shape(0);
@@ -806,30 +843,44 @@ public:
*/
template <typename E>
VALUE map_merged_stored(VALUE klass, nm::YaleStorage<E>& t, VALUE r_init) const {
+ nm_register_value(r_init);
VALUE s_init = const_default_value(),
t_init = t.const_default_value();
-
+ nm_register_value(s_init);
+ nm_register_value(t_init);
+
// Make a reasonable approximation of the resulting capacity
size_t s_ndnz = count_copy_ndnz(),
t_ndnz = t.count_copy_ndnz();
size_t reserve = shape(0) + std::max(s_ndnz, t_ndnz) + 1;
- size_t* xshape = ALLOC_N(size_t, 2);
+ size_t* xshape = NM_ALLOC_N(size_t, 2);
xshape[0] = shape(0);
xshape[1] = shape(1);
YALE_STORAGE* rs= YaleStorage<nm::RubyObject>::create(xshape, reserve);
- if (r_init == Qnil)
+ if (r_init == Qnil) {
+ nm_unregister_value(r_init);
r_init = rb_yield_values(2, s_init, t_init);
+ nm_register_value(r_init);
+ }
nm::RubyObject r_init_obj(r_init);
// Prepare the matrix structure
YaleStorage<nm::RubyObject>::init(*rs, &r_init_obj);
NMATRIX* m = nm_create(nm::YALE_STORE, reinterpret_cast<STORAGE*>(rs));
+ nm_register_nmatrix(m);
VALUE result = Data_Wrap_Struct(klass, nm_mark, nm_delete, m);
-
+ nm_unregister_nmatrix(m);
+ nm_register_value(result);
+ nm_unregister_value(r_init);
+
+ RETURN_SIZED_ENUMERATOR_PRE
+ nm_unregister_value(result);
+ nm_unregister_value(t_init);
+ nm_unregister_value(s_init);
// No obvious, efficient way to pass a length function as the fourth argument here:
RETURN_SIZED_ENUMERATOR(result, 0, 0, 0);
@@ -873,6 +924,9 @@ public:
//RB_P(rb_funcall(result, rb_intern("yale_ija"), 0));
}
}
+ nm_unregister_value(result);
+ nm_unregister_value(t_init);
+ nm_unregister_value(s_init);
return result;
}
@@ -900,12 +954,16 @@ protected:
size_t new_cap = sz + p.total_change;
if (new_cap > real_max_size()) {
- xfree(v);
+ NM_FREE(v);
rb_raise(rb_eStandardError, "resize caused by insertion of size %d (on top of current size %lu) would have caused yale matrix size to exceed its maximum (%lu)", p.total_change, sz, real_max_size());
}
- size_t* new_ija = ALLOC_N( size_t,new_cap );
- D* new_a = ALLOC_N( D, new_cap );
+ if (s->dtype == nm::RUBYOBJ) {
+ nm_register_values(reinterpret_cast<VALUE*>(v), v_size);
+ }
+
+ size_t* new_ija = NM_ALLOC_N( size_t,new_cap );
+ D* new_a = NM_ALLOC_N( D, new_cap );
// Copy unchanged row pointers first.
size_t m = 0;
@@ -967,8 +1025,12 @@ protected:
s->capacity = new_cap;
- xfree(s->ija);
- xfree(s->a);
+ NM_FREE(s->ija);
+ NM_FREE(s->a);
+
+ if (s->dtype == nm::RUBYOBJ) {
+ nm_unregister_values(reinterpret_cast<VALUE*>(v), v_size);
+ }
s->ija = new_ija;
s->a = reinterpret_cast<void*>(new_a);
@@ -994,8 +1056,8 @@ protected:
if (new_cap < sz + n) new_cap = sz + n;
- size_t* new_ija = ALLOC_N( size_t,new_cap );
- D* new_a = ALLOC_N( D, new_cap );
+ size_t* new_ija = NM_ALLOC_N( size_t,new_cap );
+ D* new_a = NM_ALLOC_N( D, new_cap );
// Copy unchanged row pointers first.
for (size_t m = 0; m <= real_i; ++m) {
@@ -1024,11 +1086,18 @@ protected:
new_a[m+n] = a(m);
}
+ if (s->dtype == nm::RUBYOBJ) {
+ nm_yale_storage_register_a(new_a, new_cap);
+ }
s->capacity = new_cap;
- xfree(s->ija);
- xfree(s->a);
+ NM_FREE(s->ija);
+ NM_FREE(s->a);
+
+ if (s->dtype == nm::RUBYOBJ) {
+ nm_yale_storage_unregister_a(new_a, new_cap);
+ }
s->ija = new_ija;
s->a = reinterpret_cast<void*>(new_a);
@@ -1067,4 +1136,4 @@ protected:
} // end of nm namespace
-#endif // YALE_CLASS_H
\ No newline at end of file
+#endif // YALE_CLASS_H
diff --git a/ext/nmatrix/storage/yale/iterators/base.h b/ext/nmatrix/storage/yale/iterators/base.h
index 5c96f6e..04e6065 100644
--- a/ext/nmatrix/storage/yale/iterators/base.h
+++ b/ext/nmatrix/storage/yale/iterators/base.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/storage/yale/iterators/iterator.h b/ext/nmatrix/storage/yale/iterators/iterator.h
index b92875a..83bd869 100644
--- a/ext/nmatrix/storage/yale/iterators/iterator.h
+++ b/ext/nmatrix/storage/yale/iterators/iterator.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/storage/yale/iterators/row.h b/ext/nmatrix/storage/yale/iterators/row.h
index dd096ea..00c036d 100644
--- a/ext/nmatrix/storage/yale/iterators/row.h
+++ b/ext/nmatrix/storage/yale/iterators/row.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/storage/yale/iterators/row_stored.h b/ext/nmatrix/storage/yale/iterators/row_stored.h
index 5d725c6..58b206a 100644
--- a/ext/nmatrix/storage/yale/iterators/row_stored.h
+++ b/ext/nmatrix/storage/yale/iterators/row_stored.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/storage/yale/iterators/row_stored_nd.h b/ext/nmatrix/storage/yale/iterators/row_stored_nd.h
index 965f225..8956877 100644
--- a/ext/nmatrix/storage/yale/iterators/row_stored_nd.h
+++ b/ext/nmatrix/storage/yale/iterators/row_stored_nd.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -87,6 +87,7 @@ public:
if (&r != &(rhs.r))
throw std::logic_error("can't assign iterator from another row iterator");
p_ = rhs.p_;
+ return *this;
}
virtual size_t p() const { return p_; }
@@ -164,4 +165,4 @@ public:
} } // end of namespace nm::yale_storage
-#endif // YALE_ITERATORS_ROW_STORED_ND_H
\ No newline at end of file
+#endif // YALE_ITERATORS_ROW_STORED_ND_H
diff --git a/ext/nmatrix/storage/yale/iterators/stored_diagonal.h b/ext/nmatrix/storage/yale/iterators/stored_diagonal.h
index fd8eb61..7ab1e19 100644
--- a/ext/nmatrix/storage/yale/iterators/stored_diagonal.h
+++ b/ext/nmatrix/storage/yale/iterators/stored_diagonal.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/storage/yale/math/transpose.h b/ext/nmatrix/storage/yale/math/transpose.h
index d1af377..56918f3 100644
--- a/ext/nmatrix/storage/yale/math/transpose.h
+++ b/ext/nmatrix/storage/yale/math/transpose.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/storage/yale/yale.cpp b/ext/nmatrix/storage/yale/yale.cpp
index 0bbf821..6652cfa 100644
--- a/ext/nmatrix/storage/yale/yale.cpp
+++ b/ext/nmatrix/storage/yale/yale.cpp
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -101,6 +101,7 @@ extern "C" {
static VALUE nm_ia(VALUE self);
static VALUE nm_ja(VALUE self);
static VALUE nm_ija(int argc, VALUE* argv, VALUE self);
+ static VALUE nm_row_keys_intersection(VALUE m1, VALUE ii1, VALUE m2, VALUE ii2);
static VALUE nm_nd_row(int argc, VALUE* argv, VALUE self);
@@ -208,8 +209,8 @@ YALE_STORAGE* create_from_old_yale(dtype_t dtype, size_t* shape, char* r_ia, cha
s->ndnz = ndnz;
// Setup IJA and A arrays
- s->ija = ALLOC_N( IType, s->capacity );
- s->a = ALLOC_N( LDType, s->capacity );
+ s->ija = NM_ALLOC_N( IType, s->capacity );
+ s->a = NM_ALLOC_N( LDType, s->capacity );
IType* ijl = reinterpret_cast<IType*>(s->ija);
LDType* al = reinterpret_cast<LDType*>(s->a);
@@ -452,14 +453,14 @@ static void vector_grow(YALE_STORAGE* s) {
if (s != s->src) {
throw; // need to correct this quickly.
}
-
+ nm_yale_storage_register(s);
size_t new_capacity = s->capacity * GROWTH_CONSTANT;
size_t max_capacity = YaleStorage<uint8_t>::max_size(s->shape);
if (new_capacity > max_capacity) new_capacity = max_capacity;
- IType* new_ija = ALLOC_N(IType, new_capacity);
- void* new_a = ALLOC_N(char, DTYPE_SIZES[s->dtype] * new_capacity);
+ IType* new_ija = NM_ALLOC_N(IType, new_capacity);
+ void* new_a = NM_ALLOC_N(char, DTYPE_SIZES[s->dtype] * new_capacity);
IType* old_ija = s->ija;
void* old_a = s->a;
@@ -469,11 +470,18 @@ static void vector_grow(YALE_STORAGE* s) {
s->capacity = new_capacity;
- xfree(old_ija);
- xfree(old_a);
+ if (s->dtype == nm::RUBYOBJ)
+ nm_yale_storage_register_a(new_a, s->capacity * DTYPE_SIZES[s->dtype]);
+
+ NM_FREE(old_ija);
+ nm_yale_storage_unregister(s);
+ NM_FREE(old_a);
+ if (s->dtype == nm::RUBYOBJ)
+ nm_yale_storage_unregister_a(new_a, s->capacity * DTYPE_SIZES[s->dtype]);
s->ija = new_ija;
s->a = new_a;
+
}
@@ -497,11 +505,13 @@ static char vector_insert_resize(YALE_STORAGE* s, size_t current_size, size_t po
if (new_capacity < current_size + n)
new_capacity = current_size + n;
+ nm_yale_storage_register(s);
+
// Allocate the new vectors.
- IType* new_ija = ALLOC_N( IType, new_capacity );
+ IType* new_ija = NM_ALLOC_N( IType, new_capacity );
NM_CHECK_ALLOC(new_ija);
- DType* new_a = ALLOC_N( DType, new_capacity );
+ DType* new_a = NM_ALLOC_N( DType, new_capacity );
NM_CHECK_ALLOC(new_a);
IType* old_ija = reinterpret_cast<IType*>(s->ija);
@@ -533,9 +543,15 @@ static char vector_insert_resize(YALE_STORAGE* s, size_t current_size, size_t po
}
s->capacity = new_capacity;
+ if (s->dtype == nm::RUBYOBJ)
+ nm_yale_storage_register_a(new_a, new_capacity);
- xfree(s->ija);
- xfree(s->a);
+ NM_FREE(s->ija);
+ nm_yale_storage_unregister(s);
+ NM_FREE(s->a);
+
+ if (s->dtype == nm::RUBYOBJ)
+ nm_yale_storage_unregister_a(new_a, new_capacity);
s->ija = new_ija;
s->a = reinterpret_cast<void*>(new_a);
@@ -566,12 +582,11 @@ static char vector_insert(YALE_STORAGE* s, size_t pos, size_t* j, void* val_, si
DType* a = reinterpret_cast<DType*>(s->a);
if (size + n > s->capacity) {
- vector_insert_resize<DType>(s, size, pos, j, n, struct_only);
+ vector_insert_resize<DType>(s, size, pos, j, n, struct_only);
// Need to get the new locations for ija and a.
ija = s->ija;
a = reinterpret_cast<DType*>(s->a);
-
} else {
/*
* No resize required:
@@ -673,6 +688,8 @@ static STORAGE* matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resu
YALE_STORAGE *left = (YALE_STORAGE*)(casted_storage.left),
*right = (YALE_STORAGE*)(casted_storage.right);
+ nm_yale_storage_register(left);
+ nm_yale_storage_register(right);
// We can safely get dtype from the casted matrices; post-condition of binary_storage_cast_alloc is that dtype is the
// same for left and right.
// int8_t dtype = left->dtype;
@@ -704,6 +721,8 @@ static STORAGE* matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resu
// Sort the columns
nm::math::smmp_sort_columns<DType>(result->shape[0], ija, ija, reinterpret_cast<DType*>(result->a));
+ nm_yale_storage_unregister(right);
+ nm_yale_storage_unregister(left);
return reinterpret_cast<STORAGE*>(result);
}
@@ -872,11 +891,13 @@ public:
// Helper function used only for the RETURN_SIZED_ENUMERATOR macro. Returns the length of
// the matrix's storage.
static VALUE nm_yale_stored_enumerator_length(VALUE nmatrix) {
+ NM_CONSERVATIVE(nm_register_value(nmatrix));
YALE_STORAGE* s = NM_STORAGE_YALE(nmatrix);
YALE_STORAGE* src = s->src == s ? s : reinterpret_cast<YALE_STORAGE*>(s->src);
size_t ia_size = src->shape[0];
// FIXME: This needs to be corrected for slicing.
size_t len = std::min( s->shape[0] + s->offset[0], s->shape[1] + s->offset[1] ) + nm_yale_storage_get_size(src) - ia_size;
+ NM_CONSERVATIVE(nm_unregister_value(nmatrix));
return INT2FIX(len);
}
@@ -884,27 +905,32 @@ static VALUE nm_yale_stored_enumerator_length(VALUE nmatrix) {
// Helper function used only for the RETURN_SIZED_ENUMERATOR macro. Returns the length of
// the matrix's storage.
static VALUE nm_yale_stored_nondiagonal_enumerator_length(VALUE nmatrix) {
+ NM_CONSERVATIVE(nm_register_value(nmatrix));
YALE_STORAGE* s = NM_STORAGE_YALE(nmatrix);
if (s->src != s) s = reinterpret_cast<YALE_STORAGE*>(s->src); // need to get the original storage shape
size_t ia_size = s->shape[0];
size_t len = nm_yale_storage_get_size(NM_STORAGE_YALE(nmatrix)) - ia_size;
-
+ NM_CONSERVATIVE(nm_unregister_value(nmatrix));
return INT2FIX(len);
}
// Helper function for diagonal length.
static VALUE nm_yale_stored_diagonal_enumerator_length(VALUE nmatrix) {
+ NM_CONSERVATIVE(nm_register_value(nmatrix));
YALE_STORAGE* s = NM_STORAGE_YALE(nmatrix);
size_t len = std::min( s->shape[0] + s->offset[0], s->shape[1] + s->offset[1] );
+ NM_CONSERVATIVE(nm_unregister_value(nmatrix));
return INT2FIX(len);
}
// Helper function for full enumerator length.
static VALUE nm_yale_enumerator_length(VALUE nmatrix) {
+ NM_CONSERVATIVE(nm_register_value(nmatrix));
YALE_STORAGE* s = NM_STORAGE_YALE(nmatrix);
size_t len = s->shape[0] * s->shape[1];
+ NM_CONSERVATIVE(nm_unregister_value(nmatrix));
return INT2FIX(len);
}
@@ -914,12 +940,21 @@ static VALUE nm_yale_enumerator_length(VALUE nmatrix) {
*/
template <typename D>
static VALUE map_stored(VALUE self) {
+ NM_CONSERVATIVE(nm_register_value(self));
YALE_STORAGE* s = NM_STORAGE_YALE(self);
YaleStorage<D> y(s);
+
+ RETURN_SIZED_ENUMERATOR_PRE
+ NM_CONSERVATIVE(nm_unregister_value(self));
RETURN_SIZED_ENUMERATOR(self, 0, 0, nm_yale_stored_enumerator_length);
+
YALE_STORAGE* r = y.template alloc_copy<nm::RubyObject, true>();
+ nm_yale_storage_register(r);
NMATRIX* m = nm_create(nm::YALE_STORE, reinterpret_cast<STORAGE*>(r));
- return Data_Wrap_Struct(CLASS_OF(self), nm_mark, nm_delete, m);
+ VALUE to_return = Data_Wrap_Struct(CLASS_OF(self), nm_mark, nm_delete, m);
+ nm_yale_storage_unregister(r);
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ return to_return;
}
@@ -930,7 +965,8 @@ template <typename LD, typename RD>
static VALUE map_merged_stored(VALUE left, VALUE right, VALUE init) {
nm::YaleStorage<LD> l(NM_STORAGE_YALE(left));
nm::YaleStorage<RD> r(NM_STORAGE_YALE(right));
- return l.map_merged_stored(CLASS_OF(left), r, init);
+ VALUE to_return = l.map_merged_stored(CLASS_OF(left), r, init);
+ return to_return;
}
@@ -939,10 +975,13 @@ static VALUE map_merged_stored(VALUE left, VALUE right, VALUE init) {
*/
template <typename DType>
static VALUE each_stored_with_indices(VALUE nm) {
+ NM_CONSERVATIVE(nm_register_value(nm));
YALE_STORAGE* s = NM_STORAGE_YALE(nm);
YaleStorage<DType> y(s);
// If we don't have a block, return an enumerator.
+ RETURN_SIZED_ENUMERATOR_PRE
+ NM_CONSERVATIVE(nm_unregister_value(nm));
RETURN_SIZED_ENUMERATOR(nm, 0, 0, nm_yale_stored_enumerator_length);
for (typename YaleStorage<DType>::const_stored_diagonal_iterator d = y.csdbegin(); d != y.csdend(); ++d) {
@@ -955,6 +994,8 @@ static VALUE each_stored_with_indices(VALUE nm) {
}
}
+ NM_CONSERVATIVE(nm_unregister_value(nm));
+
return nm;
}
@@ -964,16 +1005,22 @@ static VALUE each_stored_with_indices(VALUE nm) {
*/
template <typename DType>
static VALUE stored_diagonal_each_with_indices(VALUE nm) {
+ NM_CONSERVATIVE(nm_register_value(nm));
+
YALE_STORAGE* s = NM_STORAGE_YALE(nm);
YaleStorage<DType> y(s);
// If we don't have a block, return an enumerator.
+ RETURN_SIZED_ENUMERATOR_PRE
+ NM_CONSERVATIVE(nm_unregister_value(nm));
RETURN_SIZED_ENUMERATOR(nm, 0, 0, nm_yale_stored_diagonal_length); // FIXME: need diagonal length
-
+
for (typename YaleStorage<DType>::const_stored_diagonal_iterator d = y.csdbegin(); d != y.csdend(); ++d) {
rb_yield_values(3, ~d, d.rb_i(), d.rb_j());
}
+ NM_CONSERVATIVE(nm_unregister_value(nm));
+
return nm;
}
@@ -983,10 +1030,14 @@ static VALUE stored_diagonal_each_with_indices(VALUE nm) {
*/
template <typename DType>
static VALUE stored_nondiagonal_each_with_indices(VALUE nm) {
+ NM_CONSERVATIVE(nm_register_value(nm));
+
YALE_STORAGE* s = NM_STORAGE_YALE(nm);
YaleStorage<DType> y(s);
// If we don't have a block, return an enumerator.
+ RETURN_SIZED_ENUMERATOR_PRE
+ NM_CONSERVATIVE(nm_unregister_value(nm));
RETURN_SIZED_ENUMERATOR(nm, 0, 0, 0); // FIXME: need diagonal length
for (typename YaleStorage<DType>::const_row_iterator it = y.cribegin(); it != y.criend(); ++it) {
@@ -995,6 +1046,8 @@ static VALUE stored_nondiagonal_each_with_indices(VALUE nm) {
}
}
+ NM_CONSERVATIVE(nm_unregister_value(nm));
+
return nm;
}
@@ -1004,10 +1057,14 @@ static VALUE stored_nondiagonal_each_with_indices(VALUE nm) {
*/
template <typename DType>
static VALUE each_ordered_stored_with_indices(VALUE nm) {
+ NM_CONSERVATIVE(nm_register_value(nm));
+
YALE_STORAGE* s = NM_STORAGE_YALE(nm);
YaleStorage<DType> y(s);
// If we don't have a block, return an enumerator.
+ RETURN_SIZED_ENUMERATOR_PRE
+ NM_CONSERVATIVE(nm_unregister_value(nm));
RETURN_SIZED_ENUMERATOR(nm, 0, 0, nm_yale_stored_enumerator_length);
for (typename YaleStorage<DType>::const_row_iterator it = y.cribegin(); it != y.criend(); ++it) {
@@ -1016,25 +1073,39 @@ static VALUE each_ordered_stored_with_indices(VALUE nm) {
}
}
+ NM_CONSERVATIVE(nm_unregister_value(nm));
+
return nm;
}
template <typename DType>
static VALUE each_with_indices(VALUE nm) {
+ NM_CONSERVATIVE(nm_register_value(nm));
+
YALE_STORAGE* s = NM_STORAGE_YALE(nm);
YaleStorage<DType> y(s);
// If we don't have a block, return an enumerator.
+ RETURN_SIZED_ENUMERATOR_PRE
+ NM_CONSERVATIVE(nm_unregister_value(nm));
RETURN_SIZED_ENUMERATOR(nm, 0, 0, nm_yale_enumerator_length);
for (typename YaleStorage<DType>::const_iterator iter = y.cbegin(); iter != y.cend(); ++iter) {
rb_yield_values(3, ~iter, iter.rb_i(), iter.rb_j());
}
+ NM_CONSERVATIVE(nm_unregister_value(nm));
+
return nm;
}
+template <typename D>
+static bool is_pos_default_value(YALE_STORAGE* s, size_t apos) {
+ YaleStorage<D> y(s);
+ return y.is_pos_default_value(apos);
+}
+
} // end of namespace nm::yale_storage
@@ -1056,6 +1127,10 @@ void nm_init_yale_functions() {
*/
cNMatrix_YaleFunctions = rb_define_module_under(cNMatrix, "YaleFunctions");
+ // Expert recommendation. Eventually this should go in a separate gem, or at least a separate module.
+ rb_define_method(cNMatrix_YaleFunctions, "yale_row_keys_intersection", (METHOD)nm_row_keys_intersection, 3);
+
+ // Debugging functions.
rb_define_method(cNMatrix_YaleFunctions, "yale_ija", (METHOD)nm_ija, -1);
rb_define_method(cNMatrix_YaleFunctions, "yale_a", (METHOD)nm_a, -1);
rb_define_method(cNMatrix_YaleFunctions, "yale_size", (METHOD)nm_size, 0);
@@ -1162,7 +1237,7 @@ void* nm_yale_storage_get(const STORAGE* storage, SLICE* slice) {
return elem_copy_table[casted_storage->dtype](casted_storage, slice);
} else {
-
+ nm_yale_storage_register(casted_storage);
//return reinterpret_cast<void*>(nm::YaleStorage<nm::dtype_enum_T<storage->dtype>::type>(casted_storage).alloc_ref(slice));
NAMED_DTYPE_TEMPLATE_TABLE(ref_table, nm::yale_storage::ref, YALE_STORAGE*, YALE_STORAGE* storage, SLICE* slice)
@@ -1172,7 +1247,9 @@ void* nm_yale_storage_get(const STORAGE* storage, SLICE* slice) {
YALE_STORAGE* ns = slice_copy_table[casted_storage->dtype][casted_storage->dtype](ref);
- xfree(ref);
+ NM_FREE(ref);
+
+ nm_yale_storage_unregister(casted_storage);
return ns;
}
@@ -1339,11 +1416,11 @@ void nm_yale_storage_delete(STORAGE* s) {
if (s) {
YALE_STORAGE* storage = (YALE_STORAGE*)s;
if (storage->count-- == 1) {
- xfree(storage->shape);
- xfree(storage->offset);
- xfree(storage->ija);
- xfree(storage->a);
- xfree(storage);
+ NM_FREE(storage->shape);
+ NM_FREE(storage->offset);
+ NM_FREE(storage->ija);
+ NM_FREE(storage->a);
+ NM_FREE(storage);
}
}
}
@@ -1355,9 +1432,9 @@ void nm_yale_storage_delete_ref(STORAGE* s) {
if (s) {
YALE_STORAGE* storage = (YALE_STORAGE*)s;
nm_yale_storage_delete( reinterpret_cast<STORAGE*>(storage->src) );
- xfree(storage->shape);
- xfree(storage->offset);
- xfree(s);
+ NM_FREE(storage->shape);
+ NM_FREE(storage->offset);
+ NM_FREE(s);
}
}
@@ -1378,15 +1455,35 @@ void nm_yale_storage_init(YALE_STORAGE* s, void* init_val) {
*/
void nm_yale_storage_mark(STORAGE* storage_base) {
YALE_STORAGE* storage = (YALE_STORAGE*)storage_base;
- size_t i;
if (storage && storage->dtype == nm::RUBYOBJ) {
VALUE* a = (VALUE*)(storage->a);
- rb_gc_mark_locations(a, a + storage->capacity * sizeof(VALUE));
+ rb_gc_mark_locations(a, &(a[storage->capacity-1]));
}
}
+void nm_yale_storage_register_a(void* a, size_t size) {
+ nm_register_values(reinterpret_cast<VALUE*>(a), size);
+}
+
+void nm_yale_storage_unregister_a(void* a, size_t size) {
+ nm_unregister_values(reinterpret_cast<VALUE*>(a), size);
+}
+
+void nm_yale_storage_register(const STORAGE* s) {
+ const YALE_STORAGE* y = reinterpret_cast<const YALE_STORAGE*>(s);
+ if (y->dtype == nm::RUBYOBJ) {
+ nm_register_values(reinterpret_cast<VALUE*>(y->a), nm::yale_storage::get_size(y));
+ }
+}
+
+void nm_yale_storage_unregister(const STORAGE* s) {
+ const YALE_STORAGE* y = reinterpret_cast<const YALE_STORAGE*>(s);
+ if (y->dtype == nm::RUBYOBJ) {
+ nm_unregister_values(reinterpret_cast<VALUE*>(y->a), nm::yale_storage::get_size(y));
+ }
+}
/*
* Allocates and initializes the basic struct (but not the IJA or A vectors).
@@ -1396,12 +1493,12 @@ void nm_yale_storage_mark(STORAGE* storage_base) {
static YALE_STORAGE* alloc(nm::dtype_t dtype, size_t* shape, size_t dim) {
YALE_STORAGE* s;
- s = ALLOC( YALE_STORAGE );
+ s = NM_ALLOC( YALE_STORAGE );
s->ndnz = 0;
s->dtype = dtype;
s->shape = shape;
- s->offset = ALLOC_N(size_t, dim);
+ s->offset = NM_ALLOC_N(size_t, dim);
for (size_t i = 0; i < dim; ++i)
s->offset[i] = 0;
s->dim = dim;
@@ -1432,7 +1529,110 @@ YALE_STORAGE* nm_yale_storage_create_from_old_yale(nm::dtype_t dtype, size_t* sh
*/
static VALUE nm_size(VALUE self) {
YALE_STORAGE* s = (YALE_STORAGE*)(NM_SRC(self));
- return INT2FIX(nm::yale_storage::IJA(s)[s->shape[0]]);
+ VALUE to_return = INT2FIX(nm::yale_storage::IJA(s)[s->shape[0]]);
+ return to_return;
+}
+
+
+/*
+ * Determine if some pos in the diagonal is the default. No bounds checking!
+ */
+static bool is_pos_default_value(YALE_STORAGE* s, size_t apos) {
+ DTYPE_TEMPLATE_TABLE(nm::yale_storage::is_pos_default_value, bool, YALE_STORAGE*, size_t)
+ return ttable[s->dtype](s, apos);
+}
+
+
+/*
+ * call-seq:
+ * yale_row_keys_intersection(i, m2, i2) -> Array
+ *
+ * This function is experimental.
+ *
+ * It finds the intersection of row i of the current matrix with row i2 of matrix m2.
+ * Both matrices must be Yale. They may not be slices.
+ *
+ * Only checks the stored indices; does not care about matrix default value.
+ */
+static VALUE nm_row_keys_intersection(VALUE m1, VALUE ii1, VALUE m2, VALUE ii2) {
+
+ NM_CONSERVATIVE(nm_register_value(m1));
+ NM_CONSERVATIVE(nm_register_value(m2));
+
+ if (NM_SRC(m1) != NM_STORAGE(m1) || NM_SRC(m2) != NM_STORAGE(m2)) {
+ NM_CONSERVATIVE(nm_unregister_value(m2));
+ NM_CONSERVATIVE(nm_unregister_value(m1));
+ rb_raise(rb_eNotImpError, "must be called on a real matrix and not a slice");
+ }
+
+ size_t i1 = FIX2INT(ii1),
+ i2 = FIX2INT(ii2);
+
+ YALE_STORAGE *s = NM_STORAGE_YALE(m1),
+ *t = NM_STORAGE_YALE(m2);
+
+ size_t pos1 = s->ija[i1],
+ pos2 = t->ija[i2];
+
+ size_t nextpos1 = s->ija[i1+1],
+ nextpos2 = t->ija[i2+1];
+
+ size_t diff1 = nextpos1 - pos1,
+ diff2 = nextpos2 - pos2;
+
+ // Does the diagonal have a nonzero in it?
+ bool diag1 = i1 < s->shape[0] && !is_pos_default_value(s, i1),
+ diag2 = i2 < t->shape[0] && !is_pos_default_value(t, i2);
+
+ // Reserve max(diff1,diff2) space -- that's the max intersection possible.
+ VALUE ret = rb_ary_new2(std::max(diff1,diff2)+1);
+ nm_register_value(ret);
+
+ // Handle once the special case where both have the diagonal in exactly
+ // the same place.
+ if (diag1 && diag2 && i1 == i2) {
+ rb_ary_push(ret, INT2FIX(i1));
+ diag1 = false; diag2 = false; // no need to deal with diagonals anymore.
+ }
+
+ // Now find the intersection.
+ size_t idx1 = pos1, idx2 = pos2;
+ while (idx1 < nextpos1 && idx2 < nextpos2) {
+ if (s->ija[idx1] == t->ija[idx2]) {
+ rb_ary_push(ret, INT2FIX(s->ija[idx1]));
+ ++idx1; ++idx2;
+ } else if (diag1 && i1 == t->ija[idx2]) {
+ rb_ary_push(ret, INT2FIX(i1));
+ diag1 = false;
+ ++idx2;
+ } else if (diag2 && i2 == s->ija[idx1]) {
+ rb_ary_push(ret, INT2FIX(i2));
+ diag2 = false;
+ ++idx1;
+ } else if (s->ija[idx1] < t->ija[idx2]) {
+ ++idx1;
+ } else { // s->ija[idx1] > t->ija[idx2]
+ ++idx2;
+ }
+ }
+
+ // Past the end of row i2's stored entries; need to try to find diagonal
+ if (diag2 && idx1 < nextpos1) {
+ idx1 = nm::yale_storage::binary_search_left_boundary(s, idx1, nextpos1, i2);
+ if (s->ija[idx1] == i2) rb_ary_push(ret, INT2FIX(i2));
+ }
+
+ // Find the diagonal, if possible, in the other one.
+ if (diag1 && idx2 < nextpos2) {
+ idx2 = nm::yale_storage::binary_search_left_boundary(t, idx2, nextpos2, i1);
+ if (t->ija[idx2] == i1) rb_ary_push(ret, INT2FIX(i1));
+ }
+
+ nm_unregister_value(ret);
+ NM_CONSERVATIVE(nm_unregister_value(m1));
+ NM_CONSERVATIVE(nm_unregister_value(m2));
+
+ return ret;
}
@@ -1444,15 +1644,21 @@ static VALUE nm_size(VALUE self) {
* Get the A array of a Yale matrix (which stores the diagonal and the LU portions of the matrix).
*/
static VALUE nm_a(int argc, VALUE* argv, VALUE self) {
+ NM_CONSERVATIVE(nm_register_value(self));
+
VALUE idx;
rb_scan_args(argc, argv, "01", &idx);
+ NM_CONSERVATIVE(nm_register_value(idx));
YALE_STORAGE* s = reinterpret_cast<YALE_STORAGE*>(NM_SRC(self));
size_t size = nm_yale_storage_get_size(s);
if (idx == Qnil) {
- VALUE* vals = ALLOCA_N(VALUE, size);
+ VALUE* vals = NM_ALLOCA_N(VALUE, size);
+
+ nm_register_values(vals, size);
+
if (NM_DTYPE(self) == nm::RUBYOBJ) {
for (size_t i = 0; i < size; ++i) {
vals[i] = reinterpret_cast<VALUE*>(s->a)[i];
@@ -1467,11 +1673,15 @@ static VALUE nm_a(int argc, VALUE* argv, VALUE self) {
for (size_t i = size; i < s->capacity; ++i)
rb_ary_push(ary, Qnil);
+ nm_unregister_values(vals, size);
+ NM_CONSERVATIVE(nm_unregister_value(idx));
+ NM_CONSERVATIVE(nm_unregister_value(self));
return ary;
} else {
size_t index = FIX2INT(idx);
+ NM_CONSERVATIVE(nm_unregister_value(idx));
+ NM_CONSERVATIVE(nm_unregister_value(self));
if (index >= size) rb_raise(rb_eRangeError, "out of range");
-
return rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype] * index, s->dtype).rval;
}
}
@@ -1485,13 +1695,17 @@ static VALUE nm_a(int argc, VALUE* argv, VALUE self) {
* Get the diagonal ("D") portion of the A array of a Yale matrix.
*/
static VALUE nm_d(int argc, VALUE* argv, VALUE self) {
+ NM_CONSERVATIVE(nm_register_value(self));
VALUE idx;
rb_scan_args(argc, argv, "01", &idx);
+ NM_CONSERVATIVE(nm_register_value(idx));
YALE_STORAGE* s = reinterpret_cast<YALE_STORAGE*>(NM_SRC(self));
if (idx == Qnil) {
- VALUE* vals = ALLOCA_N(VALUE, s->shape[0]);
+ VALUE* vals = NM_ALLOCA_N(VALUE, s->shape[0]);
+
+ nm_register_values(vals, s->shape[0]);
if (NM_DTYPE(self) == nm::RUBYOBJ) {
for (size_t i = 0; i < s->shape[0]; ++i) {
@@ -1502,12 +1716,16 @@ static VALUE nm_d(int argc, VALUE* argv, VALUE self) {
vals[i] = rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*i, s->dtype).rval;
}
}
+ nm_unregister_values(vals, s->shape[0]);
+ NM_CONSERVATIVE(nm_unregister_value(idx));
+ NM_CONSERVATIVE(nm_unregister_value(self));
return rb_ary_new4(s->shape[0], vals);
} else {
size_t index = FIX2INT(idx);
+ NM_CONSERVATIVE(nm_unregister_value(idx));
+ NM_CONSERVATIVE(nm_unregister_value(self));
if (index >= s->shape[0]) rb_raise(rb_eRangeError, "out of range");
-
return rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype] * index, s->dtype).rval;
}
}
@@ -1519,11 +1737,15 @@ static VALUE nm_d(int argc, VALUE* argv, VALUE self) {
* Get the non-diagonal ("LU") portion of the A array of a Yale matrix.
*/
static VALUE nm_lu(VALUE self) {
+ NM_CONSERVATIVE(nm_register_value(self));
+
YALE_STORAGE* s = reinterpret_cast<YALE_STORAGE*>(NM_SRC(self));
size_t size = nm_yale_storage_get_size(s);
- VALUE* vals = ALLOCA_N(VALUE, size - s->shape[0] - 1);
+ VALUE* vals = NM_ALLOCA_N(VALUE, size - s->shape[0] - 1);
+
+ nm_register_values(vals, size - s->shape[0] - 1);
if (NM_DTYPE(self) == nm::RUBYOBJ) {
for (size_t i = 0; i < size - s->shape[0] - 1; ++i) {
@@ -1540,6 +1762,9 @@ static VALUE nm_lu(VALUE self) {
for (size_t i = size; i < s->capacity; ++i)
rb_ary_push(ary, Qnil);
+ nm_unregister_values(vals, size - s->shape[0] - 1);
+ NM_CONSERVATIVE(nm_unregister_value(self));
+
return ary;
}
@@ -1551,14 +1776,18 @@ static VALUE nm_lu(VALUE self) {
* JA and LU portions of the IJA and A arrays, respectively.
*/
static VALUE nm_ia(VALUE self) {
+ NM_CONSERVATIVE(nm_register_value(self));
+
YALE_STORAGE* s = reinterpret_cast<YALE_STORAGE*>(NM_SRC(self));
- VALUE* vals = ALLOCA_N(VALUE, s->shape[0] + 1);
+ VALUE* vals = NM_ALLOCA_N(VALUE, s->shape[0] + 1);
for (size_t i = 0; i < s->shape[0] + 1; ++i) {
vals[i] = INT2FIX(s->ija[i]);
}
+ NM_CONSERVATIVE(nm_unregister_value(self));
+
return rb_ary_new4(s->shape[0]+1, vals);
}
@@ -1570,11 +1799,16 @@ static VALUE nm_ia(VALUE self) {
* positions in the LU portion of the A array.
*/
static VALUE nm_ja(VALUE self) {
+
+ NM_CONSERVATIVE(nm_register_value(self));
+
YALE_STORAGE* s = reinterpret_cast<YALE_STORAGE*>(NM_SRC(self));
size_t size = nm_yale_storage_get_size(s);
- VALUE* vals = ALLOCA_N(VALUE, size - s->shape[0] - 1);
+ VALUE* vals = NM_ALLOCA_N(VALUE, size - s->shape[0] - 1);
+
+ nm_register_values(vals, size - s->shape[0] - 1);
for (size_t i = 0; i < size - s->shape[0] - 1; ++i) {
vals[i] = INT2FIX(s->ija[s->shape[0] + 1 + i]);
@@ -1585,6 +1819,9 @@ static VALUE nm_ja(VALUE self) {
for (size_t i = size; i < s->capacity; ++i)
rb_ary_push(ary, Qnil);
+ nm_unregister_values(vals, size - s->shape[0] - 1);
+ NM_CONSERVATIVE(nm_unregister_value(self));
+
return ary;
}
@@ -1596,15 +1833,20 @@ static VALUE nm_ja(VALUE self) {
* Get the IJA array of a Yale matrix (or a component of the IJA array).
*/
static VALUE nm_ija(int argc, VALUE* argv, VALUE self) {
+ NM_CONSERVATIVE(nm_register_value(self));
+
VALUE idx;
rb_scan_args(argc, argv, "01", &idx);
+ NM_CONSERVATIVE(nm_register_value(idx));
YALE_STORAGE* s = reinterpret_cast<YALE_STORAGE*>(NM_SRC(self));
size_t size = nm_yale_storage_get_size(s);
if (idx == Qnil) {
- VALUE* vals = ALLOCA_N(VALUE, size);
+ VALUE* vals = NM_ALLOCA_N(VALUE, size);
+
+ nm_register_values(vals, size);
for (size_t i = 0; i < size; ++i) {
vals[i] = INT2FIX(s->ija[i]);
@@ -1615,12 +1857,17 @@ static VALUE nm_ija(int argc, VALUE* argv, VALUE self) {
for (size_t i = size; i < s->capacity; ++i)
rb_ary_push(ary, Qnil);
+ nm_unregister_values(vals, size);
+ NM_CONSERVATIVE(nm_unregister_value(idx));
+ NM_CONSERVATIVE(nm_unregister_value(self));
+
return ary;
} else {
size_t index = FIX2INT(idx);
if (index >= size) rb_raise(rb_eRangeError, "out of range");
-
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ NM_CONSERVATIVE(nm_unregister_value(idx));
return INT2FIX(s->ija[index]);
}
}
@@ -1638,11 +1885,18 @@ static VALUE nm_ija(int argc, VALUE* argv, VALUE self) {
* range.
*/
static VALUE nm_nd_row(int argc, VALUE* argv, VALUE self) {
- if (NM_SRC(self) != NM_STORAGE(self))
+
+ NM_CONSERVATIVE(nm_register_value(self));
+
+ if (NM_SRC(self) != NM_STORAGE(self)) {
+ NM_CONSERVATIVE(nm_unregister_value(self));
rb_raise(rb_eNotImpError, "must be called on a real matrix and not a slice");
+ }
VALUE i_, as;
rb_scan_args(argc, argv, "11", &i_, &as);
+ NM_CONSERVATIVE(nm_register_value(as));
+ NM_CONSERVATIVE(nm_register_value(i_));
bool keys = false;
if (as != Qnil && rb_to_id(as) != nm_rb_hash) keys = true;
@@ -1650,7 +1904,14 @@ static VALUE nm_nd_row(int argc, VALUE* argv, VALUE self) {
size_t i = FIX2INT(i_);
YALE_STORAGE* s = NM_STORAGE_YALE(self);
- nm::dtype_t dtype = NM_DTYPE(self);
+ //nm::dtype_t dtype = NM_DTYPE(self);
+
+ if (i >= s->shape[0]) {
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ NM_CONSERVATIVE(nm_unregister_value(as));
+ NM_CONSERVATIVE(nm_unregister_value(i_));
+ rb_raise(rb_eRangeError, "out of range (%lu >= %lu)", i, s->shape[0]);
+ }
size_t pos = s->ija[i];
size_t nextpos = s->ija[i+1];
@@ -1671,7 +1932,9 @@ static VALUE nm_nd_row(int argc, VALUE* argv, VALUE self) {
rb_hash_aset(ret, INT2FIX(s->ija[idx]), rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*idx, s->dtype).rval);
}
}
-
+ NM_CONSERVATIVE(nm_unregister_value(as));
+ NM_CONSERVATIVE(nm_unregister_value(i_));
+ NM_CONSERVATIVE(nm_unregister_value(self));
return ret;
}
@@ -1706,18 +1969,32 @@ static VALUE nm_nd_row(int argc, VALUE* argv, VALUE self) {
*/
VALUE nm_vector_set(int argc, VALUE* argv, VALUE self) { //, VALUE i_, VALUE jv, VALUE vv, VALUE pos_) {
- if (NM_SRC(self) != NM_STORAGE(self))
+ NM_CONSERVATIVE(nm_register_value(self));
+
+ if (NM_SRC(self) != NM_STORAGE(self)) {
+ NM_CONSERVATIVE(nm_unregister_value(self));
rb_raise(rb_eNotImpError, "must be called on a real matrix and not a slice");
+ }
// i, jv, vv are mandatory; pos is optional; thus "31"
VALUE i_, jv, vv, pos_;
rb_scan_args(argc, argv, "31", &i_, &jv, &vv, &pos_);
+ NM_CONSERVATIVE(nm_register_value(i_));
+ NM_CONSERVATIVE(nm_register_value(jv));
+ NM_CONSERVATIVE(nm_register_value(vv));
+ NM_CONSERVATIVE(nm_register_value(pos_));
size_t len = RARRAY_LEN(jv); // need length in order to read the arrays in
size_t vvlen = RARRAY_LEN(vv);
- if (len != vvlen)
- rb_raise(rb_eArgError, "lengths must match between j array (%d) and value array (%d)", len, vvlen);
+ if (len != vvlen) {
+ NM_CONSERVATIVE(nm_unregister_value(pos_));
+ NM_CONSERVATIVE(nm_unregister_value(vv));
+ NM_CONSERVATIVE(nm_unregister_value(jv));
+ NM_CONSERVATIVE(nm_unregister_value(i_));
+ NM_CONSERVATIVE(nm_unregister_value(self));
+ rb_raise(rb_eArgError, "lengths must match between j array (%lu) and value array (%lu)", len, vvlen);
+ }
YALE_STORAGE* s = NM_STORAGE_YALE(self);
nm::dtype_t dtype = NM_DTYPE(self);
@@ -1726,8 +2003,11 @@ VALUE nm_vector_set(int argc, VALUE* argv, VALUE self) { //, VALUE i_, VALUE jv,
size_t pos = s->ija[i];
// Allocate the j array and the values array
- size_t* j = ALLOCA_N(size_t, len);
- void* vals = ALLOCA_N(char, DTYPE_SIZES[dtype] * len);
+ size_t* j = NM_ALLOCA_N(size_t, len);
+ void* vals = NM_ALLOCA_N(char, DTYPE_SIZES[dtype] * len);
+ if (dtype == nm::RUBYOBJ){
+ nm_register_values(reinterpret_cast<VALUE*>(vals), len);
+ }
// Copy array contents
for (size_t idx = 0; idx < len; ++idx) {
@@ -1739,6 +2019,16 @@ VALUE nm_vector_set(int argc, VALUE* argv, VALUE self) { //, VALUE i_, VALUE jv,
nm_yale_storage_increment_ia_after(s, s->shape[0], i, len);
s->ndnz += len;
+ if (dtype == nm::RUBYOBJ){
+ nm_unregister_values(reinterpret_cast<VALUE*>(vals), len);
+ }
+
+ NM_CONSERVATIVE(nm_unregister_value(pos_));
+ NM_CONSERVATIVE(nm_unregister_value(vv));
+ NM_CONSERVATIVE(nm_unregister_value(jv));
+ NM_CONSERVATIVE(nm_unregister_value(i_));
+ NM_CONSERVATIVE(nm_unregister_value(self));
+
// Return the updated position
pos += len;
return INT2FIX(pos);
@@ -1754,7 +2044,8 @@ VALUE nm_vector_set(int argc, VALUE* argv, VALUE self) { //, VALUE i_, VALUE jv,
* Get the default_value property from a yale matrix.
*/
VALUE nm_yale_default_value(VALUE self) {
- return default_value(NM_STORAGE_YALE(self));
+ VALUE to_return = default_value(NM_STORAGE_YALE(self));
+ return to_return;
}
diff --git a/ext/nmatrix/storage/yale/yale.h b/ext/nmatrix/storage/yale/yale.h
index a9a7dad..9782964 100644
--- a/ext/nmatrix/storage/yale/yale.h
+++ b/ext/nmatrix/storage/yale/yale.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -89,7 +89,11 @@ extern "C" {
void nm_yale_storage_delete_ref(STORAGE* s);
void nm_yale_storage_init(YALE_STORAGE* s, void* default_val);
void nm_yale_storage_mark(STORAGE*);
-
+ void nm_yale_storage_register(const STORAGE* s);
+ void nm_yale_storage_unregister(const STORAGE* s);
+ void nm_yale_storage_register_a(void* a, size_t size);
+ void nm_yale_storage_unregister_a(void* a, size_t size);
+
///////////////
// Accessors //
///////////////
diff --git a/ext/nmatrix/types.h b/ext/nmatrix/types.h
index cf77ceb..1a972f2 100644
--- a/ext/nmatrix/types.h
+++ b/ext/nmatrix/types.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/util/io.cpp b/ext/nmatrix/util/io.cpp
index ada64f2..0a1d05e 100644
--- a/ext/nmatrix/util/io.cpp
+++ b/ext/nmatrix/util/io.cpp
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -75,7 +75,7 @@ template <typename DType, typename MDType>
char* matlab_cstring_to_dtype_string(size_t& result_len, const char* str, size_t bytes) {
result_len = sizeof(DType) * bytes / sizeof(MDType);
- char* result = ALLOC_N(char, result_len);
+ char* result = NM_ALLOC_N(char, result_len);
if (bytes % sizeof(MDType) != 0) {
rb_raise(rb_eArgError, "the given string does not divide evenly for the given MATLAB dtype");
@@ -223,7 +223,7 @@ static VALUE nm_rbstring_matlab_repack(VALUE self, VALUE str, VALUE from, VALUE
// Encode as 8-bit ASCII with a length -- don't want to hiccup on \0
VALUE result = rb_str_new(repacked_data, repacked_data_length);
- xfree(repacked_data); // Don't forget to free what we allocated!
+ NM_FREE(repacked_data); // Don't forget to free what we allocated!
return result;
}
@@ -246,7 +246,7 @@ static VALUE nm_rbstring_merge(VALUE self, VALUE rb_real, VALUE rb_imaginary, VA
char *real = RSTRING_PTR(rb_real),
*imag = RSTRING_PTR(rb_imaginary);
- char* merge = ALLOCA_N(char, RSTRING_LEN(rb_real)*2);
+ char* merge = NM_ALLOCA_N(char, RSTRING_LEN(rb_real)*2);
size_t merge_pos = 0;
diff --git a/ext/nmatrix/util/io.h b/ext/nmatrix/util/io.h
index d038a5c..a2b9759 100644
--- a/ext/nmatrix/util/io.h
+++ b/ext/nmatrix/util/io.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/ext/nmatrix/util/sl_list.cpp b/ext/nmatrix/util/sl_list.cpp
index fe35e27..a9c6e5f 100644
--- a/ext/nmatrix/util/sl_list.cpp
+++ b/ext/nmatrix/util/sl_list.cpp
@@ -1,6 +1,16 @@
+/////////////////////////////////////////////////////////////////////
+// = NMatrix
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// A linear algebra library for scientific computation in Ruby.
+// NMatrix is part of SciRuby.
+//
+// NMatrix was originally inspired by and derived from NArray, by
+// Masahiro Tanaka: http://narray.rubyforge.org
+//
+// == Copyright Information
+//
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -31,6 +41,8 @@
#include "sl_list.h"
+#include "storage/list/list.h"
+
namespace nm { namespace list {
/*
@@ -58,7 +70,7 @@ namespace nm { namespace list {
* Creates an empty linked list.
*/
LIST* create(void) {
- LIST* list = ALLOC( LIST );
+ LIST* list = NM_ALLOC( LIST );
list->first = NULL;
return list;
}
@@ -77,18 +89,19 @@ void del(LIST* list, size_t recursions) {
if (recursions == 0) {
//fprintf(stderr, " free_val: %p\n", curr->val);
- xfree(curr->val);
+ nm_list_storage_completely_unregister_node(curr);
+ NM_FREE(curr->val);
} else {
//fprintf(stderr, " free_list: %p\n", list);
del((LIST*)curr->val, recursions - 1);
}
- xfree(curr);
+ NM_FREE(curr);
curr = next;
}
//fprintf(stderr, " free_list: %p\n", list);
- xfree(list);
+ NM_FREE(list);
}
/*
@@ -122,10 +135,10 @@ void mark(LIST* list, size_t recursions) {
* checks, just inserts.
*/
NODE* insert_first_node(LIST* list, size_t key, void* val, size_t val_size) {
- NODE* ins = ALLOC(NODE);
+ NODE* ins = NM_ALLOC(NODE);
ins->next = list->first;
- void* val_copy = ALLOC_N(char, val_size);
+ void* val_copy = NM_ALLOC_N(char, val_size);
memcpy(val_copy, val, val_size);
ins->val = reinterpret_cast<void*>(val_copy);
@@ -136,7 +149,7 @@ NODE* insert_first_node(LIST* list, size_t key, void* val, size_t val_size) {
}
NODE* insert_first_list(LIST* list, size_t key, LIST* l) {
- NODE* ins = ALLOC(NODE);
+ NODE* ins = NM_ALLOC(NODE);
ins->next = list->first;
ins->val = reinterpret_cast<void*>(l);
@@ -160,7 +173,7 @@ NODE* insert(LIST* list, bool replace, size_t key, void* val) {
// List is empty
//if (!(ins = malloc(sizeof(NODE)))) return NULL;
- ins = ALLOC(NODE);
+ ins = NM_ALLOC(NODE);
ins->next = NULL;
ins->val = val;
ins->key = key;
@@ -172,7 +185,7 @@ NODE* insert(LIST* list, bool replace, size_t key, void* val) {
// Goes at the beginning of the list
//if (!(ins = malloc(sizeof(NODE)))) return NULL;
- ins = ALLOC(NODE);
+ ins = NM_ALLOC(NODE);
ins->next = list->first;
ins->val = val;
ins->key = key;
@@ -187,11 +200,11 @@ NODE* insert(LIST* list, bool replace, size_t key, void* val) {
if (ins->key == key) {
// key already exists
if (replace) {
- xfree(ins->val);
+ nm_list_storage_completely_unregister_node(ins);
+ NM_FREE(ins->val);
ins->val = val;
-
} else {
- xfree(val);
+ NM_FREE(val);
}
return ins;
@@ -208,7 +221,7 @@ NODE* insert(LIST* list, bool replace, size_t key, void* val) {
*/
NODE* insert_after(NODE* node, size_t key, void* val) {
//if (!(ins = malloc(sizeof(NODE)))) return NULL;
- NODE* ins = ALLOC(NODE);
+ NODE* ins = NM_ALLOC(NODE);
// insert 'ins' between 'node' and 'node->next'
ins->next = node->next;
@@ -231,7 +244,7 @@ NODE* replace_insert_after(NODE* node, size_t key, void* val, bool copy, size_t
// Should we copy into the current one or free and insert?
if (copy) memcpy(node->next->val, val, copy_size);
else {
- xfree(node->next->val);
+ NM_FREE(node->next->val);
node->next->val = val;
}
@@ -240,7 +253,7 @@ NODE* replace_insert_after(NODE* node, size_t key, void* val, bool copy, size_t
} else { // no next node, or if there is one, it's greater than the current key
if (copy) {
- void* val_copy = ALLOC_N(char, copy_size);
+ void* val_copy = NM_ALLOC_N(char, copy_size);
memcpy(val_copy, val, copy_size);
return insert_after(node, key, val_copy);
} else {
@@ -256,7 +269,7 @@ NODE* replace_insert_after(NODE* node, size_t key, void* val, bool copy, size_t
* Functions analogously to list::insert but this inserts a copy of the value instead of the original.
*/
NODE* insert_copy(LIST *list, bool replace, size_t key, void *val, size_t size) {
- void *copy_val = ALLOC_N(char, size);
+ void *copy_val = NM_ALLOC_N(char, size);
memcpy(copy_val, val, size);
return insert(list, replace, key, copy_val);
@@ -272,7 +285,7 @@ void* remove_by_node(LIST* list, NODE* prev, NODE* rm) {
else prev->next = rm->next;
void* val = rm->val;
- xfree(rm);
+ NM_FREE(rm);
return val;
}
@@ -296,7 +309,7 @@ void* remove_by_key(LIST* list, size_t key) {
rm = list->first;
list->first = rm->next;
- xfree(rm);
+ NM_FREE(rm);
return val;
}
@@ -313,7 +326,7 @@ void* remove_by_key(LIST* list, size_t key) {
// get the value and free the memory for the node
val = rm->val;
- xfree(rm);
+ NM_FREE(rm);
return val;
}
@@ -348,7 +361,7 @@ bool remove_recursive(LIST* list, const size_t* coords, const size_t* offsets, c
if (remove_parent) { // now empty -- so remove the sub-list
// std::cerr << r << ": removing parent list at " << n->key << std::endl;
- xfree(remove_by_node(list, prev, n));
+ NM_FREE(remove_by_node(list, prev, n));
if (prev) n = prev->next && node_is_within_slice(prev->next, coords[r] + offsets[r], lengths[r]) ? prev->next : NULL;
else n = node_is_within_slice(list->first, coords[r] + offsets[r], lengths[r]) ? list->first : NULL;
@@ -367,7 +380,7 @@ bool remove_recursive(LIST* list, const size_t* coords, const size_t* offsets, c
while (n) {
// std::cerr << r << ": removing node at " << n->key << std::endl;
- xfree(remove_by_node(list, prev, n));
+ NM_FREE(remove_by_node(list, prev, n));
if (prev) n = prev->next && node_is_within_slice(prev->next, coords[r] + offsets[r], lengths[r]) ? prev->next : NULL;
else n = node_is_within_slice(list->first, coords[r] + offsets[r], lengths[r]) ? list->first : NULL;
@@ -505,7 +518,7 @@ void cast_copy_contents(LIST* lhs, const LIST* rhs, size_t recursions) {
if (rhs->first) {
// copy head node
rcurr = rhs->first;
- lcurr = lhs->first = ALLOC( NODE );
+ lcurr = lhs->first = NM_ALLOC( NODE );
while (rcurr) {
lcurr->key = rcurr->key;
@@ -513,14 +526,14 @@ void cast_copy_contents(LIST* lhs, const LIST* rhs, size_t recursions) {
if (recursions == 0) {
// contents is some kind of value
- lcurr->val = ALLOC( LDType );
+ lcurr->val = NM_ALLOC( LDType );
*reinterpret_cast<LDType*>(lcurr->val) = *reinterpret_cast<RDType*>( rcurr->val );
} else {
// contents is a list
- lcurr->val = ALLOC( LIST );
+ lcurr->val = NM_ALLOC( LIST );
cast_copy_contents<LDType, RDType>(
reinterpret_cast<LIST*>(lcurr->val),
@@ -530,7 +543,7 @@ void cast_copy_contents(LIST* lhs, const LIST* rhs, size_t recursions) {
}
if (rcurr->next) {
- lcurr->next = ALLOC( NODE );
+ lcurr->next = NM_ALLOC( NODE );
} else {
lcurr->next = NULL;
diff --git a/ext/nmatrix/util/sl_list.h b/ext/nmatrix/util/sl_list.h
index 803356c..d44070f 100644
--- a/ext/nmatrix/util/sl_list.h
+++ b/ext/nmatrix/util/sl_list.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
@@ -90,7 +90,7 @@ bool node_is_within_slice(NODE* n, size_t coord, size_t len);
template <typename Type>
inline NODE* insert_helper(LIST* list, NODE* node, size_t key, Type val) {
- Type* val_mem = ALLOC(Type);
+ Type* val_mem = NM_ALLOC(Type);
*val_mem = val;
if (node == NULL) {
diff --git a/ext/nmatrix/util/util.h b/ext/nmatrix/util/util.h
index 6ad4205..d4e6e5d 100644
--- a/ext/nmatrix/util/util.h
+++ b/ext/nmatrix/util/util.h
@@ -9,8 +9,8 @@
//
// == Copyright Information
//
-// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-// NMatrix is Copyright (c) 2013, Ruby Science Foundation
+// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
diff --git a/lib/nmatrix.rb b/lib/nmatrix.rb
index d53dcf5..6a1aec6 100644
--- a/lib/nmatrix.rb
+++ b/lib/nmatrix.rb
@@ -8,8 +8,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-# NMatrix is Copyright (c) 2013, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
diff --git a/lib/nmatrix/blas.rb b/lib/nmatrix/blas.rb
index 79172c8..e70135a 100644
--- a/lib/nmatrix/blas.rb
+++ b/lib/nmatrix/blas.rb
@@ -9,8 +9,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-# NMatrix is Copyright (c) 2013, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
diff --git a/lib/nmatrix/enumerate.rb b/lib/nmatrix/enumerate.rb
index d41a991..26e5884 100644
--- a/lib/nmatrix/enumerate.rb
+++ b/lib/nmatrix/enumerate.rb
@@ -9,8 +9,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-# NMatrix is Copyright (c) 2013, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -66,9 +66,12 @@ class NMatrix
#
# Returns an NMatrix if a block is given. For an Array, use #flat_map
#
+ # Note that #map will always return an :object matrix, because it has no way of knowing
+ # how to handle operations on the different dtypes.
+ #
def map(&bl)
return enum_for(:map) unless block_given?
- cp = self.dup
+ cp = self.cast(dtype: :object)
cp.map! &bl
cp
end
@@ -83,10 +86,18 @@ class NMatrix
#
def map!
return enum_for(:map!) unless block_given?
+ iterated = false
self.each_stored_with_indices do |e, *i|
+ iterated = true
self[*i] = (yield e)
end
- self
+ #HACK: if there's a single element in a non-dense matrix, it won't iterate and
+ #won't change the default value; this ensures that it does get changed.
+ unless iterated then
+ self.each_with_indices do |e, *i|
+ self[*i] = (yield e)
+ end
+ end
end
@@ -215,7 +226,7 @@ class NMatrix
first_as_acc = false
if initial then
- acc = NMatrix.new(new_shape, initial, :dtype => dtype || self.dtype)
+ acc = NMatrix.new(new_shape, initial, :dtype => dtype || self.dtype, stype: self.stype)
else
each_rank(dimen) do |sub_mat|
acc = (sub_mat.is_a?(NMatrix) and !dtype.nil? and dtype != self.dtype) ? sub_mat.cast(self.stype, dtype) : sub_mat
@@ -238,4 +249,4 @@ class NMatrix
alias :reduce_along_dim :inject_rank
alias :inject_along_dim :inject_rank
-end
\ No newline at end of file
+end
diff --git a/lib/nmatrix/io/market.rb b/lib/nmatrix/io/market.rb
index 974d0f0..fb09d61 100644
--- a/lib/nmatrix/io/market.rb
+++ b/lib/nmatrix/io/market.rb
@@ -1,4 +1,3 @@
-#--
# = NMatrix
#
# A linear algebra library for scientific computation in Ruby.
@@ -9,8 +8,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-# NMatrix is Copyright (c) 2012, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
diff --git a/lib/nmatrix/io/mat5_reader.rb b/lib/nmatrix/io/mat5_reader.rb
index c190775..e4dc44f 100644
--- a/lib/nmatrix/io/mat5_reader.rb
+++ b/lib/nmatrix/io/mat5_reader.rb
@@ -9,8 +9,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-# NMatrix is Copyright (c) 2012, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
diff --git a/lib/nmatrix/io/mat_reader.rb b/lib/nmatrix/io/mat_reader.rb
index 9d01edc..39d1fc9 100644
--- a/lib/nmatrix/io/mat_reader.rb
+++ b/lib/nmatrix/io/mat_reader.rb
@@ -9,8 +9,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-# NMatrix is Copyright (c) 2012, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
diff --git a/lib/nmatrix/lapack.rb b/lib/nmatrix/lapack.rb
index 160a59d..829da4d 100644
--- a/lib/nmatrix/lapack.rb
+++ b/lib/nmatrix/lapack.rb
@@ -9,8 +9,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-# NMatrix is Copyright (c) 2013, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -64,16 +64,16 @@ class NMatrix
# - +nrhs+ ->
# - +a+ ->
# - +lda+ ->
- # - +ipiv+ ->
# - +b+ ->
# - +ldb+ ->
+ # - +ipiv+ -> A pivot array (if nil, one will be generated with +clapack_getrf+)
# * *Returns* :
# -
# * *Raises* :
# - ++ ->
#
- def clapack_gesv(order, n, nrhs, a, lda, ipiv, b, ldb)
- clapack_getrf(order, n, n, a, lda, ipiv)
+ def clapack_gesv(order, n, nrhs, a, lda, b, ldb, ipiv=nil)
+ ipiv ||= clapack_getrf(order, n, n, a, lda)
clapack_getrs(order, :no_transpose, n, nrhs, a, lda, ipiv, b, ldb)
end
@@ -120,47 +120,6 @@ class NMatrix
clapack_potrs(order, uplo, n, nrhs, a, lda, b, ldb)
end
- #
- # call-seq:
- # gesvd(matrix, type)
- #
- #
- # * *Arguments* :
- # - +matrix+ -> matrix for which to compute the singular values ##TODO make this a self
- # - +type+ -> :all_values, :both, :left, :right, :left_matrix, :right_matrix, :overwrite_right, :overwrite_left, :none , or signifying what combination of singular values and matrices are desired in your output.
- # * *Returns* :
- # - Array with the result values in an array
- # * *Raises* :
- # - +ArgumentError+ -> Expected dense NMatrix as first argument.
- #
- def gesvd(matrix, type = :both)
- raise ArgumentError, 'Expected dense NMatrix as first argument.' unless matrix.is_a?(NMatrix) and matrix.stype == :dense
- #define jobu, jobvt
- jobu, jobvt = :none, :none
- case type
- when :both
- jobu, jobvt = :all, :all
- when :arrays
- jobu, jobvt = :return, :return
- when :left
- jobu = :return
- when :right
- jobvt = :return
- end
-
- # Build up the u and vt matrices
- m, n = matrix.shape
- dtype = matrix.dtype
- s_matrix = NMatrix.new([1,matrix.shape.min], dtype: dtype)
- u_matrix = NMatrix.new([m,m], dtype: dtype)
- v_matrix = NMatrix.new([n,n], dtype: dtype)
- # test this
- s = gesvd(type, matrix, s_matrix, u_matrix, v_matrix)
-
- # what should this return?
- [s_matrix, u_matrix, v_matrix]
- end # #svd
-
# laswp(matrix, ipiv) -> NMatrix
#
# Permute the columns of a matrix (in-place) according to the Array +ipiv+.
@@ -173,6 +132,47 @@ class NMatrix
clapack_laswp(matrix.shape[0], matrix, matrix.shape[1], 0, ipiv.size-1, ipiv, 1)
end
+ def alloc_svd_result(matrix)
+ [
+ NMatrix.new(matrix.shape[0], dtype: matrix.dtype),
+ NMatrix.new([matrix.shape[0],1], dtype: matrix.dtype),
+ NMatrix.new(matrix.shape[1], dtype: matrix.dtype)
+ ]
+ end
+
+ #
+ # call-seq:
+ # gesvd -> [u, sigma, v_transpose]
+ # gesvd -> [u, sigma, v_conjugate_transpose] # complex
+ #
+ # Compute the singular value decomposition of a matrix using LAPACK's GESVD function.
+ #
+ # Optionally accepts a +workspace_size+ parameter, which will be honored only if it is larger than what LAPACK
+ # requires.
+ #
+ def gesvd(matrix, workspace_size=1)
+ result = alloc_svd_result(matrix)
+ NMatrix::LAPACK::lapack_gesvd(:a, :a, matrix.shape[0], matrix.shape[1], matrix, matrix.shape[0], result[1], result[0], matrix.shape[0], result[2], matrix.shape[1], workspace_size)
+ result
+ end
+
+ #
+ # call-seq:
+ # gesdd -> [u, sigma, v_transpose]
+ # gesdd -> [u, sigma, v_conjugate_transpose] # complex
+ #
+ # Compute the singular value decomposition of a matrix using LAPACK's GESDD function. This uses a divide-and-conquer
+ # strategy. See also #gesvd.
+ #
+ # Optionally accepts a +workspace_size+ parameter, which will be honored only if it is larger than what LAPACK
+ # requires.
+ #
+ def gesdd(matrix, workspace_size=100000)
+ result = alloc_svd_result(matrix)
+ NMatrix::LAPACK::lapack_gesdd(:a, matrix.shape[0], matrix.shape[1], matrix, matrix.shape[0], result[1], result[0], matrix.shape[0], result[2], matrix.shape[1], workspace_size)
+ result
+ end
+
end
end
end
diff --git a/lib/nmatrix/math.rb b/lib/nmatrix/math.rb
index 551a45f..833d44c 100644
--- a/lib/nmatrix/math.rb
+++ b/lib/nmatrix/math.rb
@@ -9,8 +9,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-# NMatrix is Copyright (c) 2013, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -29,6 +29,13 @@
#++
class NMatrix
+
+ module NMMath
+ METHODS_ARITY_2 = [:atan2, :ldexp, :hypot]
+ METHODS_ARITY_1 = [:cos, :sin, :tan, :acos, :asin, :atan, :cosh, :sinh, :tanh, :acosh,
+ :asinh, :atanh, :exp, :log2, :log10, :sqrt, :cbrt, :erf, :erfc, :gamma]
+ end
+
#
# call-seq:
# invert! -> NMatrix
@@ -75,10 +82,66 @@ class NMatrix
# - +StorageTypeError+ -> ATLAS functions only work on dense matrices.
#
def getrf!
- raise(StorageTypeError, "ATLAS functions only work on dense matrices") unless self.stype == :dense
+ raise(StorageTypeError, "ATLAS functions only work on dense matrices") unless self.dense?
NMatrix::LAPACK::clapack_getrf(:row, self.shape[0], self.shape[1], self, self.shape[1])
end
+
+ #
+ # call-seq:
+ # getrf -> NMatrix
+ #
+ # In-place version of #getrf!. Returns the new matrix, which contains L and U matrices.
+ #
+ # * *Raises* :
+ # - +StorageTypeError+ -> ATLAS functions only work on dense matrices.
+ #
+ def getrf
+ a = self.clone
+ a.getrf!
+ return a
+ end
+
+
+ #
+ # call-seq:
+ # potrf!(upper_or_lower) -> NMatrix
+ #
+ # Cholesky factorization of a symmetric positive-definite matrix -- or, if complex,
+ # a Hermitian positive-definite matrix +A+. This uses the ATLAS function clapack_potrf,
+ # so the result will be written in either the upper or lower triangular portion of the
+ # matrix upon which it is called.
+ #
+ # * *Returns* :
+ # the triangular portion specified by the parameter
+ # * *Raises* :
+ # - +StorageTypeError+ -> ATLAS functions only work on dense matrices.
+ #
+ def potrf!(which)
+ raise(StorageTypeError, "ATLAS functions only work on dense matrices") unless self.dense?
+ # FIXME: Surely there's an easy way to calculate one of these from the other. Do we really need to run twice?
+ NMatrix::LAPACK::clapack_potrf(:row, which, self.shape[0], self, self.shape[1])
+ end
+
+ def potrf_upper!
+ potrf! :upper
+ end
+
+ def potrf_lower!
+ potrf! :lower
+ end
+
+
+ #
+ # call-seq:
+ # factorize_cholesky -> ...
+ #
+ # Cholesky factorization of a matrix.
+ def factorize_cholesky
+ [self.clone.potrf_upper!.triu!,
+ self.clone.potrf_lower!.tril!]
+ end
+
#
# call-seq:
# factorize_lu -> ...
@@ -97,12 +160,19 @@ class NMatrix
t.transpose
end
- def alloc_svd_result
- [
- NMatrix.new(self.shape[0], dtype: self.dtype),
- NMatrix.new([self.shape[0],1], dtype: self.dtype),
- NMatrix.new(self.shape[1], dtype: self.dtype)
- ]
+ #
+ # call-seq:
+ # gesvd! -> [u, sigma, v_transpose]
+ # gesvd! -> [u, sigma, v_conjugate_transpose] # complex
+ #
+ # Compute the singular value decomposition of a matrix using LAPACK's GESVD function.
+ # This is destructive, modifying the source NMatrix. See also #gesdd.
+ #
+ # Optionally accepts a +workspace_size+ parameter, which will be honored only if it is larger than what LAPACK
+ # requires.
+ #
+ def gesvd!(workspace_size=1)
+ NMatrix::LAPACK::gesvd(self, workspace_size)
end
#
@@ -116,12 +186,26 @@ class NMatrix
# requires.
#
def gesvd(workspace_size=1)
- result = alloc_svd_result
- NMatrix::LAPACK::lapack_gesvd(:a, :a, self.shape[0], self.shape[1], self, self.shape[0], result[1], result[0], self.shape[0], result[2], self.shape[1], workspace_size)
- result
+ self.clone.gesvd!(workspace_size)
end
+
+ #
+ # call-seq:
+ # gesdd! -> [u, sigma, v_transpose]
+ # gesdd! -> [u, sigma, v_conjugate_transpose] # complex
+ #
+ # Compute the singular value decomposition of a matrix using LAPACK's GESDD function. This uses a divide-and-conquer
+ # strategy. This is destructive, modifying the source NMatrix. See also #gesvd.
+ #
+ # Optionally accepts a +workspace_size+ parameter, which will be honored only if it is larger than what LAPACK
+ # requires.
+ #
+ def gesdd!(workspace_size=1)
+ NMatrix::LAPACK::gesdd(self, workspace_size)
+ end
+
#
# call-seq:
# gesdd -> [u, sigma, v_transpose]
@@ -134,11 +218,8 @@ class NMatrix
# requires.
#
def gesdd(workspace_size=1)
- result = alloc_svd_result
- NMatrix::LAPACK::lapack_gesvd(:a, :a, self.shape[0], self.shape[1], self, self.shape[0], result[1], result[0], self.shape[0], result[2], self.shape[1], workspace_size)
- result
+ self.clone.gesdd!(workspace_size)
end
-
#
# call-seq:
# laswp!(ary) -> NMatrix
@@ -279,8 +360,8 @@ class NMatrix
reduce_dtype = :float64
end
inject_rank(dimen, 0.0, reduce_dtype) do |mean, sub_mat|
- mean + sub_mat/shape[dimen]
- end
+ mean + sub_mat
+ end / shape[dimen]
end
##
@@ -372,7 +453,7 @@ class NMatrix
# @see #inject_rank
#
def std(dimen=0)
- variance(dimen).map! { |e| Math.sqrt(e) }
+ variance(dimen).sqrt
end
@@ -409,6 +490,38 @@ class NMatrix
end.cast(self.stype, abs_dtype)
end
+
+ #
+ # call-seq:
+ # absolute_sum -> Numeric
+ #
+ # == Arguments
+ # - +incx+ -> the skip size (defaults to 1, no skip)
+ # - +n+ -> the number of elements to include
+ #
+ # Return the sum of the contents of the vector. This is the BLAS asum routine.
+ def asum incx=1, n=nil
+ return method_missing(:asum, incx, n) unless vector?
+ NMatrix::BLAS::asum(self, incx, self.size / incx)
+ end
+ alias :absolute_sum :asum
+
+ #
+ # call-seq:
+ # norm2 -> Numeric
+ #
+ # == Arguments
+ # - +incx+ -> the skip size (defaults to 1, no skip)
+ # - +n+ -> the number of elements to include
+ #
+ # Return the 2-norm of the vector. This is the BLAS nrm2 routine.
+ def nrm2 incx=1, n=nil
+ return method_missing(:nrm2, incx, n) unless vector?
+ NMatrix::BLAS::nrm2(self, incx, self.size / incx)
+ end
+ alias :norm2 :nrm2
+
+
alias :permute_columns :laswp
alias :permute_columns! :laswp!
@@ -437,6 +550,86 @@ protected
end
end
+ # These don't actually take an argument -- they're called reverse-polish style on the matrix.
+ # This group always gets casted to float64.
+ [:log2, :log10, :sqrt, :sin, :cos, :tan, :acos, :asin, :atan, :cosh, :sinh, :tanh, :acosh, :asinh, :atanh, :exp, :erf, :erfc, :gamma, :cbrt].each do |ewop|
+ define_method("__list_unary_#{ewop}__") do
+ self.__list_map_stored__(nil) { |l| Math.send(ewop, l) }.cast(stype, NMatrix.upcast(dtype, :float64))
+ end
+ define_method("__yale_unary_#{ewop}__") do
+ self.__yale_map_stored__ { |l| Math.send(ewop, l) }.cast(stype, NMatrix.upcast(dtype, :float64))
+ end
+ define_method("__dense_unary_#{ewop}__") do
+ self.__dense_map__ { |l| Math.send(ewop, l) }.cast(stype, NMatrix.upcast(dtype, :float64))
+ end
+ end
+
+ # log takes an optional single argument, the base. Default to natural log.
+ def __list_unary_log__(base)
+ self.__list_map_stored__(nil) { |l| Math.log(l, base) }.cast(stype, NMatrix.upcast(dtype, :float64))
+ end
+
+ def __yale_unary_log__(base)
+ self.__yale_map_stored__ { |l| Math.log(l, base) }.cast(stype, NMatrix.upcast(dtype, :float64))
+ end
+
+ def __dense_unary_log__(base)
+ self.__dense_map__ { |l| Math.log(l, base) }.cast(stype, NMatrix.upcast(dtype, :float64))
+ end
+
+ # These take two arguments. One might be a matrix, and one might be a scalar.
+ # See also monkeys.rb, which contains Math module patches to let the first
+ # arg be a scalar
+ [:atan2, :ldexp, :hypot].each do |ewop|
+ define_method("__list_elementwise_#{ewop}__") do |rhs,order|
+ if order then
+ self.__list_map_merged_stored__(rhs, nil) { |r,l| Math.send(ewop,l,r) }
+ else
+ self.__list_map_merged_stored__(rhs, nil) { |l,r| Math.send(ewop,l,r) }
+ end.cast(stype, NMatrix.upcast(dtype, :float64))
+ end
+
+ define_method("__dense_elementwise_#{ewop}__") do |rhs, order|
+ if order then
+ self.__dense_map_pair__(rhs) { |r,l| Math.send(ewop,l,r) }
+ else
+ self.__dense_map_pair__(rhs) { |l,r| Math.send(ewop,l,r) }
+ end.cast(stype, NMatrix.upcast(dtype, :float64))
+ end
+
+ define_method("__yale_elementwise_#{ewop}__") do |rhs, order|
+ if order then
+ self.__yale_map_merged_stored__(rhs, nil) { |r,l| Math.send(ewop,l,r) }
+ else
+ self.__yale_map_merged_stored__(rhs, nil) { |l,r| Math.send(ewop,l,r) }
+ end.cast(stype, NMatrix.upcast(dtype, :float64))
+ end
+
+ define_method("__list_scalar_#{ewop}__") do |rhs,order|
+ if order then
+ self.__list_map_stored__(nil) { |l| Math.send(ewop, rhs, l) }
+ else
+ self.__list_map_stored__(nil) { |l| Math.send(ewop, l, rhs) }
+ end.cast(stype, NMatrix.upcast(dtype, :float64))
+ end
+
+ define_method("__yale_scalar_#{ewop}__") do |rhs,order|
+ if order then
+ self.__yale_map_stored__ { |l| Math.send(ewop, rhs, l) }
+ else
+ self.__yale_map_stored__ { |l| Math.send(ewop, l, rhs) }
+ end.cast(stype, NMatrix.upcast(dtype, :float64))
+ end
+
+ define_method("__dense_scalar_#{ewop}__") do |rhs,order|
+ if order
+ self.__dense_map__ { |l| Math.send(ewop, rhs, l) }
+ else
+ self.__dense_map__ { |l| Math.send(ewop, l, rhs) }
+ end.cast(stype, NMatrix.upcast(dtype, :float64))
+ end
+ end
+
# Equality operators do not involve a cast. We want to get back matrices of TrueClass and FalseClass.
{eqeq: :==, neq: :!=, lt: :<, gt: :>, leq: :<=, geq: :>=}.each_pair do |ewop, op|
define_method("__list_elementwise_#{ewop}__") do |rhs|
@@ -459,4 +652,4 @@ protected
self.__dense_map__ { |l| l.send(op,rhs) }
end
end
-end
\ No newline at end of file
+end
diff --git a/lib/nmatrix/monkeys.rb b/lib/nmatrix/monkeys.rb
index 2a12fc9..508efac 100644
--- a/lib/nmatrix/monkeys.rb
+++ b/lib/nmatrix/monkeys.rb
@@ -9,8 +9,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-# NMatrix is Copyright (c) 2013, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -26,6 +26,8 @@
# Ruby core extensions for NMatrix.
#++
+require 'nmatrix/math'
+
#######################
# Classes and Modules #
#######################
@@ -60,3 +62,23 @@ class Object #:nodoc:
value
end
end
+
+
+module Math
+ class << self
+ NMatrix::NMMath::METHODS_ARITY_2.each do |meth|
+ define_method "nm_#{meth}" do |arg0, arg1|
+ if arg0.is_a? NMatrix then
+ arg0.send(meth, arg1)
+ elsif arg1.is_a? NMatrix then
+ arg1.send(meth, arg0, true)
+ else
+ self.send("old_#{meth}".to_sym, arg0, arg1)
+ end
+ end
+ alias_method "old_#{meth}".to_sym, meth
+ alias_method meth, "nm_#{meth}".to_sym
+ end
+ end
+end
+
diff --git a/lib/nmatrix/nmatrix.rb b/lib/nmatrix/nmatrix.rb
index 08a3dd7..5e1032f 100644
--- a/lib/nmatrix/nmatrix.rb
+++ b/lib/nmatrix/nmatrix.rb
@@ -9,8 +9,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-# NMatrix is Copyright (c) 2013, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -41,6 +41,7 @@ class NMatrix
def load_mat file_path
NMatrix::IO::Matlab::Mat5Reader.new(File.open(file_path, "rb+")).to_ruby
end
+ alias :load :load_mat
end
# FIXME: Remove autoloads
@@ -76,7 +77,9 @@ class NMatrix
# TODO: Make this actually pretty.
def pretty_print(q) #:nodoc:
- if self.dim > 3 || self.dim == 1
+ if self.shape.size > 1 and self.shape[1] > 100
+ self.inspect.pretty_print(q)
+ elsif self.dim > 3 || self.dim == 1
self.to_a.pretty_print(q)
else
# iterate through the whole matrix and find the longest number
@@ -115,7 +118,6 @@ class NMatrix
#alias :pp :pretty_print
-
#
# call-seq:
# cast(stype, dtype, default) -> NMatrix
@@ -150,13 +152,17 @@ class NMatrix
else
params << self.stype if params.size == 0
params << self.dtype if params.size == 1
- params << (self.stype == :dense ? 0 : self.default_value) if params.size == 2
-
+ #HACK: the default value can cause an exception if dtype is not complex
+ #and default_value is. (The ruby C code apparently won't convert these.)
+ #Perhaps this should be fixed in the C code (in rubyval_to_cval).
+ default_value = maybe_get_noncomplex_default_value(params[1])
+ params << (self.stype == :dense ? 0 : default_value) if params.size == 2
self.cast_full(*params)
end
end
+
#
# call-seq:
# rows -> Integer
@@ -378,12 +384,283 @@ class NMatrix
# - +row_number+ -> Integer.
# - +get_by+ -> Type of slicing to use, +:copy+ or +:reference+.
# * *Returns* :
- # - A NMatrix representing the requested row as a row vector.
+ # - An NMatrix representing the requested row as a row vector.
#
def row(row_number, get_by = :copy)
rank(0, row_number, get_by)
end
+
+ #
+ # call-seq:
+ # reshape(new_shape) -> NMatrix
+ #
+ # Clone a matrix, changing the shape in the process. Note that this function does not do a resize; the product of
+ # the new and old shapes' components must be equal.
+ #
+ # * *Arguments* :
+ # - +new_shape+ -> Array of positive Fixnums.
+ # * *Returns* :
+ # - A copy with a different shape.
+ #
+ def reshape new_shape
+ t = reshape_clone_structure(new_shape)
+ left_params = [:*]*new_shape.size
+ right_params = [:*]*self.shape.size
+ t[*left_params] = self[*right_params]
+ t
+ end
+
+
+ #
+ # call-seq:
+ # transpose -> NMatrix
+ # transpose(permutation) -> NMatrix
+ #
+ # Clone a matrix, transposing it in the process. If the matrix is two-dimensional, the permutation is taken to be [1,0]
+ # automatically (switch dimension 0 with dimension 1). If the matrix is n-dimensional, you must provide a permutation
+ # of +0...n+.
+ #
+ # * *Arguments* :
+ # - +permutation+ -> Optional Array giving a permutation.
+ # * *Returns* :
+ # - A copy of the matrix, but transposed.
+ #
+ def transpose(permute = nil)
+ if self.dim <= 2 # This will give an error if dim is 1.
+ new_shape = [self.shape[1], self.shape[0]]
+ elsif permute.nil?
+ raise(ArgumentError, "need permutation array of size #{self.dim}")
+ elsif permute.sort.uniq != (0...self.dim).to_a
+ raise(ArgumentError, "invalid permutation array")
+ else
+ # Figure out the new shape based on the permutation given as an argument.
+ new_shape = permute.map { |p| self.shape[p] }
+ end
+
+ if self.dim > 2 # FIXME: For dense, several of these are basically equivalent to reshape.
+
+ # Make the new data structure.
+ t = self.reshape_clone_structure(new_shape)
+
+ self.each_stored_with_indices do |v,*indices|
+ p_indices = permute.map { |p| indices[p] }
+ t[*p_indices] = v
+ end
+ t
+ elsif self.list? # TODO: Need a C list transposition algorithm.
+ # Make the new data structure.
+ t = self.reshape_clone_structure(new_shape)
+
+ self.each_column.with_index do |col,j|
+ t[j,:*] = col.to_flat_array
+ end
+ t
+ else
+ # Call C versions of Yale and List transpose, which do their own copies
+ self.clone_transpose
+ end
+ end
+
+
+ #
+ # call-seq:
+ # matrix1.concat(*m2) -> NMatrix
+ # matrix1.concat(*m2, rank) -> NMatrix
+ # matrix1.hconcat(*m2) -> NMatrix
+ # matrix1.vconcat(*m2) -> NMatrix
+ # matrix1.dconcat(*m3) -> NMatrix
+ #
+ # Joins two matrices together into a new larger matrix. Attempts to determine which direction to concatenate
+ # on by looking for the first common element of the matrix +shape+ in reverse. In other words, concatenating two
+ # columns together without supplying +rank+ will glue them into an n x 2 matrix.
+ #
+ # You can also use hconcat, vconcat, and dconcat for the first three ranks. concat performs an hconcat when no
+ # rank argument is provided.
+ #
+ # The two matrices must have the same +dim+.
+ #
+ # * *Arguments* :
+ # - +matrices+ -> one or more matrices
+ # - +rank+ -> Fixnum (for rank); alternatively, may use :row, :column, or :layer for 0, 1, 2, respectively
+ #
+ def concat *matrices
+ rank = nil
+ rank = matrices.pop unless matrices.last.is_a?(NMatrix)
+
+ # Find the first matching dimension and concatenate along that (unless rank is specified)
+ if rank.nil?
+ rank = self.dim-1
+ self.shape.reverse_each.with_index do |s,i|
+ matrices.each do |m|
+ if m.shape[i] != s
+ rank -= 1
+ break
+ end
+ end
+ end
+ elsif rank.is_a?(Symbol) # Convert to numeric
+ rank = {:row => 0, :column => 1, :col => 1, :lay => 2, :layer => 2}[rank]
+ end
+
+ # Need to figure out the new shape.
+ new_shape = self.shape.dup
+ new_shape[rank] = matrices.inject(self.shape[rank]) { |total,m| total + m.shape[rank] }
+
+ # Now figure out the options for constructing the concatenated matrix.
+ opts = {stype: self.stype, default: self.default_value, dtype: self.dtype}
+ if self.yale?
+ # We can generally predict the new capacity for Yale. Subtract out the number of rows
+ # for each matrix being concatenated, and then add in the number of rows for the new
+ # shape. That takes care of the diagonal. The rest of the capacity is represented by
+ # the non-diagonal non-default values.
+ new_cap = matrices.inject(self.capacity - self.shape[0]) do |total,m|
+ total + m.capacity - m.shape[0]
+ end - self.shape[0] + new_shape[0]
+ opts = {capacity: self.new_cap}.merge(opts)
+ end
+
+ # Do the actual construction.
+ n = NMatrix.new(new_shape, opts)
+
+ # Figure out where to start and stop the concatenation. We'll use NMatrices instead of
+ # Arrays because then we can do elementwise addition.
+ ranges = self.shape.map.with_index { |s,i| 0...self.shape[i] }
+
+ matrices.unshift(self)
+ matrices.each do |m|
+ n[*ranges] = m
+
+ # move over by the requisite amount
+ ranges[rank] = (ranges[rank].first + m.shape[rank])...(ranges[rank].last + m.shape[rank])
+ end
+
+ n
+ end
+
+ def hconcat *matrices
+ concat(*matrices, :column)
+ end
+
+ def vconcat *matrices
+ concat(*matrices, :row)
+ end
+
+ def dconcat *matrices
+ concat(*matrices, :layer)
+ end
+
+
+ #
+ # call-seq:
+ # upper_triangle -> NMatrix
+ # upper_triangle(k) -> NMatrix
+ # triu -> NMatrix
+ # triu(k) -> NMatrix
+ #
+ # Returns the upper triangular portion of a matrix. This is analogous to the +triu+ method
+ # in MATLAB.
+ #
+ # * *Arguments* :
+ # - +k+ -> Positive integer. How many extra diagonals to include in the upper triangular portion.
+ #
+ def upper_triangle(k = 0)
+ raise(NotImplementedError, "only implemented for 2D matrices") if self.shape.size > 2
+
+ t = self.clone_structure
+ (0...self.shape[0]).each do |i|
+ if i - k < 0
+ t[i, :*] = self[i, :*]
+ else
+ t[i, 0...(i-k)] = 0
+ t[i, (i-k)...self.shape[1]] = self[i, (i-k)...self.shape[1]]
+ end
+ end
+ t
+ end
+ alias :triu :upper_triangle
+
+
+ #
+ # call-seq:
+ # upper_triangle! -> NMatrix
+ # upper_triangle!(k) -> NMatrix
+ # triu! -> NMatrix
+ # triu!(k) -> NMatrix
+ #
+ # Deletes the lower triangular portion of the matrix (in-place) so only the upper portion remains.
+ #
+ # * *Arguments* :
+ # - +k+ -> Integer. How many extra diagonals to include in the deletion.
+ #
+ def upper_triangle!(k = 0)
+ raise(NotImplementedError, "only implemented for 2D matrices") if self.shape.size > 2
+
+ (0...self.shape[0]).each do |i|
+ if i - k >= 0
+ self[i, 0...(i-k)] = 0
+ end
+ end
+ self
+ end
+ alias :triu! :upper_triangle!
+
+
+ #
+ # call-seq:
+ # lower_triangle -> NMatrix
+ # lower_triangle(k) -> NMatrix
+ # tril -> NMatrix
+ # tril(k) -> NMatrix
+ #
+ # Returns the lower triangular portion of a matrix. This is analogous to the +tril+ method
+ # in MATLAB.
+ #
+ # * *Arguments* :
+ # - +k+ -> Integer. How many extra diagonals to include in the lower triangular portion.
+ #
+ def lower_triangle(k = 0)
+ raise(NotImplementedError, "only implemented for 2D matrices") if self.shape.size > 2
+
+ t = self.clone_structure
+ (0...self.shape[0]).each do |i|
+ if i + k >= shape[0]
+ t[i, :*] = self[i, :*]
+ else
+ t[i, (i+k+1)...self.shape[1]] = 0
+ t[i, 0..(i+k)] = self[i, 0..(i+k)]
+ end
+ end
+ t
+ end
+ alias :tril :lower_triangle
+
+
+ #
+ # call-seq:
+ # lower_triangle! -> NMatrix
+ # lower_triangle!(k) -> NMatrix
+ # tril! -> NMatrix
+ # tril!(k) -> NMatrix
+ #
+ # Deletes the upper triangular portion of the matrix (in-place) so only the lower portion remains.
+ #
+ # * *Arguments* :
+ # - +k+ -> Integer. How many extra diagonals to include in the deletion.
+ #
+ def lower_triangle!(k = 0)
+ raise(NotImplementedError, "only implemented for 2D matrices") if self.shape.size > 2
+
+ (0...self.shape[0]).each do |i|
+ if i + k < shape[0]
+ self[i, (i+k+1)...self.shape[1]] = 0
+ end
+ end
+ self
+ end
+ alias :tril! :lower_triangle!
+
+
#
# call-seq:
# layer(layer_number) -> NMatrix
@@ -435,6 +712,43 @@ class NMatrix
end
+ #
+ # call-seq:
+ # sorted_indices -> Array
+ #
+ # Returns an array of the indices ordered by value sorted.
+ #
+ def sorted_indices
+ return method_missing(:sorted_indices) unless vector?
+ ary = self.to_flat_array
+ ary.each_index.sort_by { |i| ary[i] } # from: http://stackoverflow.com/a/17841159/170300
+ end
+
+
+ #
+ # call-seq:
+ # binned_sorted_indices -> Array
+ #
+ # Returns an array of arrays of indices ordered by value sorted. Functions basically like +sorted_indices+, but
+ # groups indices together for those values that are the same.
+ #
+ def binned_sorted_indices
+ return method_missing(:sorted_indices) unless vector?
+ ary = self.to_flat_array
+ ary2 = []
+ last_bin = ary.each_index.sort_by { |i| [ary[i]] }.inject([]) do |result, element|
+ if result.empty? || ary[result[-1]] == ary[element]
+ result << element
+ else
+ ary2 << result
+ [element]
+ end
+ end
+ ary2 << last_bin unless last_bin.empty?
+ ary2
+ end
+
+
def method_missing name, *args, &block #:nodoc:
if name.to_s =~ /^__list_elementwise_.*__$/
raise NotImplementedError, "requested undefined list matrix element-wise operation"
@@ -447,7 +761,7 @@ class NMatrix
def respond_to?(method) #:nodoc:
- if [:shuffle, :shuffle!, :each_with_index].include?(method.intern) # vector-only methods
+ if [:shuffle, :shuffle!, :each_with_index, :sorted_indices, :binned_sorted_indices, :nrm2, :asum].include?(method.intern) # vector-only methods
return vector?
elsif [:each_layer, :layer].include?(method.intern) # 3-or-more dimensions only
return dim > 2
@@ -483,6 +797,35 @@ protected
end
+ #
+ # call-seq:
+ # clone_structure -> NMatrix
+ #
+ # This function is like clone, but it only copies the structure and the default value.
+ # None of the other values are copied. It takes an optional capacity argument. This is
+ # mostly only useful for dense, where you may not want to initialize; for other types,
+ # you should probably use +zeros_like+.
+ #
+ def clone_structure(capacity = nil)
+ opts = {stype: self.stype, default: self.default_value, dtype: self.dtype}
+ opts = {capacity: capacity}.merge(opts) if self.yale?
+ NMatrix.new(self.shape, opts)
+ end
+
+
+ # Clone the structure as needed for a reshape
+ def reshape_clone_structure(new_shape) #:nodoc:
+ raise(ArgumentError, "reshape cannot resize; size of new and old matrices must match") unless self.size == new_shape.inject(1) { |p,i| p *= i }
+
+ opts = {stype: self.stype, default: self.default_value, dtype: self.dtype}
+ if self.yale?
+ # We can generally predict the change in capacity for Yale.
+ opts = {capacity: self.capacity - self.shape[0] + new_shape[0]}.merge(opts)
+ end
+ NMatrix.new(new_shape, opts)
+ end
+
+
# Helper for converting a matrix into an array of arrays recursively
def to_a_rec(dimen = 0) #:nodoc:
return self.flat_map { |v| v } if dimen == self.dim-1
@@ -500,8 +843,50 @@ protected
def __sparse_initial_set__(ary) #:nodoc:
self[0...self.shape[0],0...self.shape[1]] = ary
end
+
+
+ # Function assumes the dimensions and such have already been tested.
+ #
+ # Called from inside NMatrix: nm_eqeq
+ #
+ # There are probably more efficient ways to do this, but currently it's unclear how.
+ # We could use +each_row+, but for list matrices, it's still going to need to make a
+ # reference to each of those rows, and that is going to require a seek.
+ #
+ # It might be more efficient to convert one sparse matrix type to the other with a
+ # cast and then run the comparison. For now, let's assume that people aren't going
+ # to be doing this very often, and we can optimize as needed.
+ def dense_eql_sparse? m #:nodoc:
+ m.each_with_indices do |v,*indices|
+ return false if self[*indices] != v
+ end
+
+ return true
+ end
+ alias :sparse_eql_sparse? :dense_eql_sparse?
+
+
+ #
+ # See the note in #cast about why this is necessary.
+ # If this is a non-dense matrix with a complex dtype and to_dtype is
+ # non-complex, then this will convert the default value to noncomplex.
+ # Returns 0 if dense. Returns existing default_value if there isn't a
+ # mismatch.
+ #
+ def maybe_get_noncomplex_default_value(to_dtype) #:nodoc:
+ default_value = 0
+ unless self.stype == :dense then
+ if self.dtype.to_s.start_with?('complex') and not to_dtype.to_s.start_with?('complex') then
+ default_value = self.default_value.real
+ else
+ default_value = self.default_value
+ end
+ end
+ default_value
+ end
+
end
require_relative './shortcuts.rb'
require_relative './math.rb'
-require_relative './enumerate.rb'
\ No newline at end of file
+require_relative './enumerate.rb'
diff --git a/lib/nmatrix/nvector.rb b/lib/nmatrix/nvector.rb
index af77839..8b62382 100644
--- a/lib/nmatrix/nvector.rb
+++ b/lib/nmatrix/nvector.rb
@@ -9,8 +9,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-# NMatrix is Copyright (c) 2013, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -159,68 +159,6 @@ class NVector < NMatrix
min_so_far
end
- #
- # call-seq:
- # absolute_sum -> Numeric
- #
- # == Arguments
- # - +incx+ -> the skip size (defaults to 1, no skip)
- # - +n+ -> the number of elements to include
- #
- # Return the sum of the contents of the vector. This is the BLAS asum routine.
- def asum incx=1, n=nil
- NMatrix::BLAS::asum(self, incx, self.size / incx)
- end
- alias :absolute_sum :asum
-
- #
- # call-seq:
- # norm2 -> Numeric
- #
- # == Arguments
- # - +incx+ -> the skip size (defaults to 1, no skip)
- # - +n+ -> the number of elements to include
- #
- # Return the 2-norm of the vector. This is the BLAS nrm2 routine.
- def nrm2 incx=1, n=nil
- NMatrix::BLAS::nrm2(self, incx, self.size / incx)
- end
- alias :norm2 :nrm2
-
-
- #
- # call-seq:
- # sorted_indices -> Array
- #
- # Returns an array of the indices ordered by value sorted.
- #
- def sorted_indices
- ary = self.to_a
- ary.each_index.sort_by { |i| ary[i] } # from: http://stackoverflow.com/a/17841159/170300
- end
-
- #
- # call-seq:
- # binned_sorted_indices -> Array
- #
- # Returns an array of arrays of indices ordered by value sorted. Functions basically like +sorted_indices+, but
- # groups indices together for those values that are the same.
- #
- def binned_sorted_indices
- ary = self.to_a
- ary2 = []
- last_bin = ary.each_index.sort_by { |i| [ary[i]] }.inject([]) do |result, element|
- if result.empty? || ary[result[-1]] == ary[element]
- result << element
- else
- ary2 << result
- [element]
- end
- end
- ary2 << last_bin unless last_bin.empty?
- ary2
- end
-
# TODO: Make this actually pretty.
def pretty_print(q = nil) #:nodoc:
diff --git a/lib/nmatrix/rspec.rb b/lib/nmatrix/rspec.rb
index 4acedac..60dac34 100644
--- a/lib/nmatrix/rspec.rb
+++ b/lib/nmatrix/rspec.rb
@@ -8,8 +8,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-# NMatrix is Copyright (c) 2012, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
diff --git a/lib/nmatrix/shortcuts.rb b/lib/nmatrix/shortcuts.rb
index 306e5c4..40acc13 100644
--- a/lib/nmatrix/shortcuts.rb
+++ b/lib/nmatrix/shortcuts.rb
@@ -9,8 +9,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-# NMatrix is Copyright (c) 2013, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -295,6 +295,14 @@ class NMatrix
# call-seq:
# seq(shape) -> NMatrix
# seq(shape, options) -> NMatrix
+ # bindgen(shape) -> NMatrix of :byte
+ # indgen(shape) -> NMatrix of :int64
+ # findgen(shape) -> NMatrix of :float32
+ # dindgen(shape) -> NMatrix of :float64
+ # cindgen(shape) -> NMatrix of :complex64
+ # zindgen(shape) -> NMatrix of :complex128
+ # rindgen(shape) -> NMatrix of :rational128
+ # rbindgen(shape) -> NMatrix of :object
#
# Creates a matrix filled with a sequence of integers starting at zero.
#
@@ -322,66 +330,11 @@ class NMatrix
NMatrix.new(shape, values, {:stype => :dense}.merge(options))
end
- #
- # call-seq:
- # indgen(size) -> NMatrix
- #
- # Returns an integer NMatrix. Equivalent to <tt>seq(n, dtype: :int32)</tt>.
- #
- # * *Arguments* :
- # - +shape+ -> Shape of the sequence.
- # * *Returns* :
- # - NMatrix with dtype +:int32+.
- #
- def indgen(shape)
- NMatrix.seq(shape, dtype: :int32)
- end
-
- #
- # call-seq:
- # findgen(shape) -> NMatrix
- #
- # Returns a float NMatrix. Equivalent to <tt>seq(n, dtype: :float32)</tt>.
- #
- # * *Arguments* :
- # - +shape+ -> Shape of the sequence.
- # * *Returns* :
- # - NMatrix with dtype +:float32+.
- #
- def findgen(shape)
- NMatrix.seq(shape, dtype: :float32)
+ {:bindgen => :byte, :indgen => :int64, :findgen => :float32, :dindgen => :float64,
+ :cindgen => :complex64, :zindgen => :complex128,
+ :rindgen => :rational128, :rbindgen => :object}.each_pair do |meth, dtype|
+ define_method(meth) { |shape| NMatrix.seq(shape, :dtype => dtype) }
end
-
- #
- # call-seq:
- # bindgen(size) -> NMatrix
- #
- # Returns a byte NMatrix. Equivalent to <tt>seq(n, dtype: :byte)</tt>.
- #
- # * *Arguments* :
- # - +size+ -> Shape of the sequence.
- # * *Returns* :
- # - NMatrix with dtype +:byte+.
- #
- def bindgen(shape)
- NMatrix.seq(shape, dtype: :byte)
- end
-
- #
- # call-seq:
- # cindgen(shape) -> NMatrix
- #
- # Returns a complex NMatrix. Equivalent to <tt>seq(n, dtype: :complex64)</tt>.
- #
- # * *Arguments* :
- # - +shape+ -> Shape of the sequence.
- # * *Returns* :
- # - NMatrix with dtype +:complex64+.
- #
- def cindgen(shape)
- NMatrix.seq(shape, dtype: :complex64)
- end
-
end
end
diff --git a/lib/nmatrix/version.rb b/lib/nmatrix/version.rb
index d3b8054..3b6fd01 100644
--- a/lib/nmatrix/version.rb
+++ b/lib/nmatrix/version.rb
@@ -9,8 +9,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-# NMatrix is Copyright (c) 2013, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -26,6 +26,14 @@ class NMatrix
# Note that the format of the VERSION string is needed for NMatrix
# native IO. If you change the format, please make sure that native
# IO can still understand NMatrix::VERSION.
- VERSION = "0.0.9"
+ #VERSION = "0.1.0"
+ module VERSION
+ MAJOR = 0
+ MINOR = 1
+ TINY = 0
+ PRE = "rc1"
+
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
+ end
end
diff --git a/lib/nmatrix/yale_functions.rb b/lib/nmatrix/yale_functions.rb
index a47fadf..07f20c2 100644
--- a/lib/nmatrix/yale_functions.rb
+++ b/lib/nmatrix/yale_functions.rb
@@ -9,8 +9,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
-# NMatrix is Copyright (c) 2013, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -81,7 +81,7 @@ module NMatrix::YaleFunctions
# Returns the diagonal and non-digonal column indices stored in a given row.
def yale_ja_d_keys_at i
ary = yale_nd_row(i, :keys)
- return ary if i >= self.shape[1] || self[i,i].nil? || self[i,i] == 0
+ return ary if i >= self.shape[1] || self[i,i] == self.default_value
ary << i
end
alias :yale_row_as_array :yale_ja_d_keys_at
@@ -112,7 +112,7 @@ module NMatrix::YaleFunctions
# Returns the diagonal and non-diagonal column indices and entries stored in a given row.
def yale_row_as_hash i
h = yale_nd_row(i, :hash)
- return h if i >= self.shape[1] || self[i,i].nil? || self[i,i] == 0
+ return h if i >= self.shape[1] || self[i,i] == self.default_value
h[i] = self[i,i]
end
end
\ No newline at end of file
diff --git a/metadata.yml b/metadata.yml
index 42165d9..9a15974 100644
--- a/metadata.yml
+++ b/metadata.yml
@@ -1,7 +1,7 @@
--- !ruby/object:Gem::Specification
name: nmatrix
version: !ruby/object:Gem::Version
- version: 0.0.9
+ version: 0.1.0.rc1
platform: ruby
authors:
- John Woods
@@ -10,7 +10,7 @@ authors:
autorequire:
bindir: bin
cert_chain: []
-date: 2013-09-19 00:00:00.000000000 Z
+date: 2013-12-28 00:00:00.000000000 Z
dependencies:
- !ruby/object:Gem::Dependency
name: rdoc
@@ -162,6 +162,7 @@ files:
- ext/nmatrix/math/scal.h
- ext/nmatrix/math/swap.h
- ext/nmatrix/math/trsm.h
+- ext/nmatrix/nm_memory.h
- ext/nmatrix/nmatrix.cpp
- ext/nmatrix/nmatrix.h
- ext/nmatrix/ruby_constants.cpp
@@ -169,10 +170,10 @@ files:
- ext/nmatrix/ruby_nmatrix.c
- ext/nmatrix/storage/common.cpp
- ext/nmatrix/storage/common.h
-- ext/nmatrix/storage/dense.cpp
-- ext/nmatrix/storage/dense.h
-- ext/nmatrix/storage/list.cpp
-- ext/nmatrix/storage/list.h
+- ext/nmatrix/storage/dense/dense.cpp
+- ext/nmatrix/storage/dense/dense.h
+- ext/nmatrix/storage/list/list.cpp
+- ext/nmatrix/storage/list/list.h
- ext/nmatrix/storage/storage.cpp
- ext/nmatrix/storage/storage.h
- ext/nmatrix/storage/yale/class.h
@@ -209,6 +210,7 @@ files:
- lib/nmatrix/yale_functions.rb
- nmatrix.gemspec
- scripts/mac-brew-gcc.sh
+- scripts/mac-mavericks-brew-gcc.sh
- spec/00_nmatrix_spec.rb
- spec/01_enum_spec.rb
- spec/02_slice_spec.rb
@@ -231,14 +233,12 @@ files:
- spec/stat_spec.rb
- spec/utm5940.mtx
homepage: http://sciruby.com
-licenses: []
+licenses:
+- BSD 2-clause
metadata: {}
post_install_message: "***********************************************************\nWelcome
- to SciRuby: Tools for Scientific Computing in Ruby!\n\n ***
- WARNING ***\nPlease be aware that NMatrix is in ALPHA status. If you're\nthinking
- of using NMatrix to write mission critical code,\nsuch as for driving a car or flying
- a space shuttle, you\nmay wish to choose other software (for now).\n\nNMatrix requires
- a C compiler, and has been tested only\nwith GCC 4.6+. We are happy to accept contributions\nwhich
+ to SciRuby: Tools for Scientific Computing in Ruby!\n\nNMatrix requires a C compiler,
+ and has been tested only\nwith GCC 4.6+. We are happy to accept contributions\nwhich
improve the portability of this project.\n\nAlso required is ATLAS. Most Linux distributions
and Mac\nversions include ATLAS, but you may wish to compile it\nyourself. The Ubuntu/Debian
apt package for ATLAS WILL \nNOT WORK with NMatrix if LAPACK is also installed.\n\nMore
@@ -255,12 +255,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
version: '1.9'
required_rubygems_version: !ruby/object:Gem::Requirement
requirements:
- - - '>='
+ - - '>'
- !ruby/object:Gem::Version
- version: '0'
+ version: 1.3.1
requirements: []
rubyforge_project:
-rubygems_version: 2.0.2
+rubygems_version: 2.0.3
signing_key:
specification_version: 4
summary: NMatrix is an experimental linear algebra library for Ruby, written mostly
diff --git a/nmatrix.gemspec b/nmatrix.gemspec
index 7622e1d..23e155a 100644
--- a/nmatrix.gemspec
+++ b/nmatrix.gemspec
@@ -5,22 +5,17 @@ require 'nmatrix/version'
Gem::Specification.new do |gem|
gem.name = "nmatrix"
- gem.version = NMatrix::VERSION
+ gem.version = NMatrix::VERSION::STRING
gem.summary = "NMatrix is an experimental linear algebra library for Ruby, written mostly in C."
gem.description = "NMatrix is an experimental linear algebra library for Ruby, written mostly in C."
gem.homepage = 'http://sciruby.com'
gem.authors = ['John Woods', 'Chris Wailes', 'Aleksey Timin']
gem.email = ['john.o.woods at gmail.com']
+ gem.license = 'BSD 2-clause'
gem.post_install_message = <<-EOF
***********************************************************
Welcome to SciRuby: Tools for Scientific Computing in Ruby!
- *** WARNING ***
-Please be aware that NMatrix is in ALPHA status. If you're
-thinking of using NMatrix to write mission critical code,
-such as for driving a car or flying a space shuttle, you
-may wish to choose other software (for now).
-
NMatrix requires a C compiler, and has been tested only
with GCC 4.6+. We are happy to accept contributions
which improve the portability of this project.
diff --git a/scripts/mac-brew-gcc.sh b/scripts/mac-brew-gcc.sh
index 15096a3..68888c9 100755
--- a/scripts/mac-brew-gcc.sh
+++ b/scripts/mac-brew-gcc.sh
@@ -1,5 +1,9 @@
#!/bin/bash
-VERSION="4.7.2"
+
+# Script will not work for GCC 4.8 or 4.9. For those, please see
+# mac-mavericks-brew-gcc.sh
+
+VERSION="4.7.2" # Script should also work with GCC 4.7.1.
PREFIX="/usr/gcc-${VERSION}"
LANGUAGES="c,c++,fortran"
MAKE="make -j 4"
@@ -8,11 +12,9 @@ brew-path() { brew info $1 | head -n3 | tail -n1 | cut -d' ' -f1; }
# Prerequisites
-brew install gmp
-brew install mpfr
-brew install libmpc
+brew install gmp mpfr libmpc
-# Download & install the latest GCC
+# Next, download & install the latest GCC:
mkdir -p $PREFIX
mkdir temp-gcc
@@ -25,11 +27,12 @@ cd gcc-$VERSION
mkdir build
cd build
+# Older versions of brew need brew-path instead of brew --prefix.
../configure \
--prefix=$PREFIX \
- --with-gmp=$(brew-path gmp) \
- --with-mpfr=$(brew-path mpfr) \
- --with-mpc=$(brew-path libmpc) \
+ --with-gmp=$(brew --prefix gmp) \
+ --with-mpfr=$(brew --prefix mpfr) \
+ --with-mpc=$(brew --prefix libmpc) \
--program-suffix=-$VERSION \
--enable-languages=$LANGUAGES \
--with-system-zlib \
diff --git a/scripts/mac-mavericks-brew-gcc.sh b/scripts/mac-mavericks-brew-gcc.sh
new file mode 100644
index 0000000..867d050
--- /dev/null
+++ b/scripts/mac-mavericks-brew-gcc.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+brew-path() { brew info $1 | head -n3 | tail -n1 | cut -d' ' -f1; }
+
+# Try using the following for GCC 4.9:
+#
+# brew install gmp4 mpfr2 libmpc08 isl011 cloog018
+#
+#
+
+brew install gcc49 --enable-fortran
+# Source for this is: http://stackoverflow.com/questions/19535422/os-x-10-9-gcc-links-to-clang
+
+
+# You may wish to re-install your Ruby if you're using rbenv. To do
+# so, make sure you've installed openssl, readline, and libyaml.
+#
+# The commands for this are:
+#
+# CC=gcc-4.8 RUBY_CONFIGURE_OPTS="--with-openssl-dir=`brew --prefix openssl` --with-readline-dir=`brew --prefix readline` --with-gcc=gcc-4.8 --enable-shared" rbenv install --keep 2.0.0-p247
+#
+#
\ No newline at end of file
diff --git a/spec/00_nmatrix_spec.rb b/spec/00_nmatrix_spec.rb
index a9f4dd5..283762a 100644
--- a/spec/00_nmatrix_spec.rb
+++ b/spec/00_nmatrix_spec.rb
@@ -8,8 +8,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-# NMatrix is Copyright (c) 2012, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -29,6 +29,10 @@
require File.dirname(__FILE__) + "/spec_helper.rb"
describe NMatrix do
+ #after :each do
+ # GC.start
+ #end
+
it "creates a matrix with the new constructor" do
n = NMatrix.new([2,2], [0,1,2,3], dtype: :int64)
end
@@ -40,17 +44,15 @@ describe NMatrix do
end
it "calculates exact determinants on small square matrices" do
- a = NMatrix.new(:dense, 2, [1,2,3,4], :int64)
- a.det_exact.should == -2
+ NMatrix.new(2, [1,2,3,4], stype: :dense, dtype: :int64).det_exact.should == -2
end
it "calculates determinants" do
- m = NMatrix.new(3, [-2,2,3,-1,1,3,2,0,-1])
- m.det.should == 6
+ NMatrix.new(3, [-2,2,3,-1,1,3,2,0,-1], stype: :dense, dtype: :int64).det.should == 6
end
it "allows casting to Ruby objects" do
- m = NMatrix.new(:dense, [3,3], [0,0,1,0,2,0,3,4,5], :int64)
+ m = NMatrix.new([3,3], [0,0,1,0,2,0,3,4,5], dtype: :int64, stype: :dense)
n = m.cast(:dense, :object)
n.should == m
end
@@ -367,3 +369,110 @@ describe NMatrix do
end
end
+
+
+describe "NMatrix#upper_triangle" do
+ it "should create a copy with the lower corner set to zero" do
+ n = NMatrix.seq(4)+1
+ n.upper_triangle.should == NMatrix.new(4, [1,2,3,4,0,6,7,8,0,0,11,12,0,0,0,16])
+ n.upper_triangle(2).should == NMatrix.new(4, [1,2,3,4,5,6,7,8,9,10,11,12,0,14,15,16])
+ end
+end
+
+describe "NMatrix#lower_triangle" do
+ it "should create a copy with the lower corner set to zero" do
+ n = NMatrix.seq(4)+1
+ n.lower_triangle.should == NMatrix.new(4, [1,0,0,0,5,6,0,0,9,10,11,0,13,14,15,16])
+ n.lower_triangle(2).should == NMatrix.new(4, [1,2,3,0,5,6,7,8,9,10,11,12,13,14,15,16])
+ end
+end
+
+describe "NMatrix#upper_triangle!" do
+ it "should create a copy with the lower corner set to zero" do
+ n = NMatrix.seq(4)+1
+ n.upper_triangle!.should == NMatrix.new(4, [1,2,3,4,0,6,7,8,0,0,11,12,0,0,0,16])
+ n = NMatrix.seq(4)+1
+ n.upper_triangle!(2).should == NMatrix.new(4, [1,2,3,4,5,6,7,8,9,10,11,12,0,14,15,16])
+ end
+end
+
+describe "NMatrix#lower_triangle!" do
+ it "should create a copy with the lower corner set to zero" do
+ n = NMatrix.seq(4)+1
+ n.lower_triangle!.should == NMatrix.new(4, [1,0,0,0,5,6,0,0,9,10,11,0,13,14,15,16])
+ n = NMatrix.seq(4)+1
+ n.lower_triangle!(2).should == NMatrix.new(4, [1,2,3,0,5,6,7,8,9,10,11,12,13,14,15,16])
+ end
+end
+
+describe "NMatrix#reshape" do
+ it "should change the shape of a matrix without the contents changing" do
+ n = NMatrix.seq(4)+1
+ n.reshape([8,2]).to_flat_array.should == n.to_flat_array
+ end
+
+ it "should permit a change of dimensionality" do
+ n = NMatrix.seq(4)+1
+ n.reshape([8,1,2]).to_flat_array.should == n.to_flat_array
+ end
+
+ it "should prevent a resize" do
+ n = NMatrix.seq(4)+1
+ expect { n.reshape([5,2]) }.to raise_error(ArgumentError)
+ end
+end
+
+describe "NMatrix#transpose" do
+ [:dense, :list, :yale].each do |stype|
+ context(stype) do
+ it "should transpose a #{stype} matrix (2-dimensional)" do
+ n = NMatrix.seq(4, stype: stype)
+ n.transpose.to_a.flatten.should == [0,4,8,12,1,5,9,13,2,6,10,14,3,7,11,15]
+ end
+ end
+ end
+
+ [:dense, :list].each do |stype|
+ context(stype) do
+ it "should transpose a #{stype} matrix (3-dimensional)" do
+ n = NMatrix.new([4,4,1], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], stype: stype)
+ n.transpose([2,1,0]).to_flat_array.should == [0,4,8,12,1,5,9,13,2,6,10,14,3,7,11,15]
+ n.transpose([1,0,2]).to_flat_array.should == [0,4,8,12,1,5,9,13,2,6,10,14,3,7,11,15]
+ n.transpose([0,2,1]).to_flat_array.should == n.to_flat_array # for dense, make this reshape!
+ end
+ end
+ end
+
+end
+
+describe "NMatrix#==" do
+ [:dense, :list, :yale].each do |left|
+ [:dense, :list, :yale].each do |right|
+ next if left == right
+ context ("#{left}?#{right}") do
+ it "should compare two matrices of differing stypes" do
+ n = NMatrix.new([3,4], [0,0,1,2,0,0,3,4,0,0,0,0,5,6,7,0], stype: left)
+ m = NMatrix.new([3,4], [0,0,1,2,0,0,3,4,0,0,0,0,5,6,7,0], stype: right)
+ n.should == m
+ end
+ end
+ end
+ end
+end
+
+describe "NMatrix#concat" do
+ it "should default to horizontal concatenation" do
+ n = NMatrix.new([1,3], [1,2,3])
+ n.concat(n).should == NMatrix.new([1,6], [1,2,3,1,2,3])
+ end
+
+ it "should permit vertical concatenation" do
+ n = NMatrix.new([1,3], [1,2,3])
+ n.vconcat(n).should == NMatrix.new([2,3], [1,2,3])
+ end
+
+ it "should permit depth concatenation on tensors" do
+ n = NMatrix.new([1,3,1], [1,2,3])
+ n.dconcat(n).should == NMatrix.new([1,3,2], [1,1,2,2,3,3])
+ end
+end
diff --git a/spec/01_enum_spec.rb b/spec/01_enum_spec.rb
index 653cf77..e7ce161 100644
--- a/spec/01_enum_spec.rb
+++ b/spec/01_enum_spec.rb
@@ -8,8 +8,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-# NMatrix is Copyright (c) 2012, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -37,6 +37,10 @@ describe "NMatrix enumeration for" do
@m = @n[1..4,1..3]
end
+ #after :each do
+ # GC.start
+ #end
+
if stype == :yale
it "should iterate properly along each row of a slice" do
vv = []
@@ -178,7 +182,17 @@ describe "NMatrix enumeration for" do
end
+ if stype == :list or stype == :dense then
+ it "should correctly map to a matrix with a single element" do
+ nm = N.new([1], [2.0], stype: stype)
+ nm.map { |e| e**2 }.should eq N.new([1], [4.0], stype: stype)
+ end
+ it "should correctly map to a matrix with multiple elements" do
+ nm = N.new([2], [2.0, 2.0], stype: stype)
+ nm.map { |e| e**2 }.should eq N.new([2], [4.0, 4.0], stype: stype)
+ end
+ end
end
end
-end
\ No newline at end of file
+end
diff --git a/spec/02_slice_spec.rb b/spec/02_slice_spec.rb
index 53ebcd5..0ed7d66 100644
--- a/spec/02_slice_spec.rb
+++ b/spec/02_slice_spec.rb
@@ -8,8 +8,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-# NMatrix is Copyright (c) 2012, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -34,10 +34,14 @@ describe "Slice operation" do
[:dense, :list, :yale].each do |stype|
context "for #{stype}" do
before :each do
- GC.start # don't have to do this, but it helps to make sure we've cleaned up our pointers properly.
+ #GC.start # don't have to do this, but it helps to make sure we've cleaned up our pointers properly.
@m = create_matrix(stype)
end
+ #after :each do
+ # GC.start
+ #end
+
it "should correctly return a row of a reference-slice" do
@n = create_rectangular_matrix(stype)
@m = @n[1..4,1..3]
@@ -256,6 +260,10 @@ describe "Slice operation" do
@m[0..2,0..2].should == @m[0...3,0...3]
end
+ it 'should correctly handle :* slice notation' do
+ @m[:*,0].should eq @m[0... at m.shape[0], 0]
+ end
+
if stype == :dense
[:byte,:int8,:int16,:int32,:int64,:float32,:float64,:rational64,:rational128].each do |left_dtype|
[:byte,:int8,:int16,:int32,:int64,:float32,:float64,:rational64,:rational128].each do |right_dtype|
diff --git a/spec/blas_spec.rb b/spec/blas_spec.rb
index caae6fa..5366c5b 100644
--- a/spec/blas_spec.rb
+++ b/spec/blas_spec.rb
@@ -8,8 +8,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-# NMatrix is Copyright (c) 2012, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -29,6 +29,9 @@
require File.join(File.dirname(__FILE__), "spec_helper.rb")
describe NMatrix::BLAS do
+ #after :each do
+ # GC.start
+ #end
[:rational32, :rational64, :rational128, :float32, :float64, :complex64, :complex128].each do |dtype|
context dtype do
diff --git a/spec/elementwise_spec.rb b/spec/elementwise_spec.rb
index dcf7b28..5352539 100644
--- a/spec/elementwise_spec.rb
+++ b/spec/elementwise_spec.rb
@@ -8,8 +8,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-# NMatrix is Copyright (c) 2012, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -29,6 +29,9 @@
require File.join(File.dirname(__FILE__), "spec_helper.rb")
describe NMatrix do
+ #after :each do
+ # GC.start
+ #end
context "yale" do
before :each do
diff --git a/spec/io_spec.rb b/spec/io_spec.rb
index 75b6df5..ff3ac77 100644
--- a/spec/io_spec.rb
+++ b/spec/io_spec.rb
@@ -8,8 +8,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-# NMatrix is Copyright (c) 2012, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -24,9 +24,21 @@
#
# Basic tests for NMatrix::IO.
#
+require "tmpdir" # Used to avoid cluttering the repository.
+
require "./lib/nmatrix"
describe NMatrix::IO do
+ before :each do
+ @tmp_dir = Dir.mktmpdir
+ @test_out = File.join(@tmp_dir, "test-out")
+ end
+
+ after :each do
+ File.delete(@test_out) if File.file?(@test_out)
+ Dir.rmdir(@tmp_dir)
+ end
+
it "repacks a string" do
NMatrix::IO::Matlab.repack("hello", :miUINT8, :byte).should == "hello"
end
@@ -83,57 +95,55 @@ describe NMatrix::IO do
it "reads and writes NMatrix dense" do
n = NMatrix.new(:dense, [4,3], [0,1,2,3,4,5,6,7,8,9,10,11], :int32)
- n.write("test-out")
+ n.write(@test_out)
- m = NMatrix.read("test-out")
+ m = NMatrix.read(@test_out)
n.should == m
end
it "reads and writes NMatrix dense as symmetric" do
n = NMatrix.new(:dense, 3, [0,1,2,1,3,4,2,4,5], :int16)
- n.write("test-out", :symmetric)
+ n.write(@test_out, :symmetric)
- m = NMatrix.read("test-out")
+ m = NMatrix.read(@test_out)
n.should == m
end
it "reads and writes NMatrix dense as skew" do
n = NMatrix.new(:dense, 3, [0,1,2,-1,3,4,-2,-4,5], :float64)
- n.write("test-out", :skew)
+ n.write(@test_out, :skew)
- m = NMatrix.read("test-out")
+ m = NMatrix.read(@test_out)
n.should == m
end
it "reads and writes NMatrix dense as hermitian" do
n = NMatrix.new(:dense, 3, [0,1,2,1,3,4,2,4,5], :complex64)
- n.write("test-out", :hermitian)
+ n.write(@test_out, :hermitian)
- m = NMatrix.read("test-out")
+ m = NMatrix.read(@test_out)
n.should == m
end
it "reads and writes NMatrix dense as upper" do
n = NMatrix.new(:dense, 3, [-1,1,2,3,4,5,6,7,8], :int32)
- n.write("test-out", :upper)
+ n.write(@test_out, :upper)
m = NMatrix.new(:dense, 3, [-1,1,2,0,4,5,0,0,8], :int32) # lower version of the same
- o = NMatrix.read("test-out")
+ o = NMatrix.read(@test_out)
o.should == m
o.should_not == n
end
it "reads and writes NMatrix dense as lower" do
n = NMatrix.new(:dense, 3, [-1,1,2,3,4,5,6,7,8], :int32)
- n.write("test-out", :lower)
+ n.write(@test_out, :lower)
m = NMatrix.new(:dense, 3, [-1,0,0,3,4,0,6,7,8], :int32) # lower version of the same
- o = NMatrix.read("test-out")
+ o = NMatrix.read(@test_out)
o.should == m
o.should_not == n
end
-
-
-end
\ No newline at end of file
+end
diff --git a/spec/lapack_spec.rb b/spec/lapack_spec.rb
index f48e6df..27269cc 100644
--- a/spec/lapack_spec.rb
+++ b/spec/lapack_spec.rb
@@ -8,8 +8,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-# NMatrix is Copyright (c) 2012, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -29,6 +29,10 @@
require File.join(File.dirname(__FILE__), "spec_helper.rb")
describe NMatrix::LAPACK do
+ #after :each do
+ # GC.start
+ #end
+
# where integer math is allowed
[:byte, :int8, :int16, :int32, :int64, :rational32, :rational64, :rational128, :float32, :float64, :complex64, :complex128].each do |dtype|
context dtype do
@@ -55,7 +59,23 @@ describe NMatrix::LAPACK do
# where integer math is not allowed
[:rational32, :rational64, :rational128, :float32, :float64, :complex64, :complex128].each do |dtype|
context dtype do
- it "exposes clapack getrf" do
+
+ it "exposes clapack_gesv" do
+ a = NMatrix[[1.quo(1), 2, 3], [0,1.quo(2),4],[3,3,9]].cast(dtype: dtype)
+ b = NMatrix[[1.quo(1)],[2],[3]].cast(dtype: dtype)
+ err = case dtype
+ when :float32, :complex64
+ 1e-6
+ when :float64, :complex128
+ 1e-8
+ else
+ 1e-64
+ end
+ NMatrix::LAPACK::clapack_gesv(:row,a.shape[0],b.shape[1],a,a.shape[0],b,b.shape[0]).should be_within(err).of(NMatrix[[-1.quo(2)], [0], [1.quo(2)]].cast(dtype: dtype))
+ end
+
+
+ it "exposes clapack_getrf" do
a = NMatrix.new(3, [4,9,2,3,5,7,8,1,6], dtype: dtype)
NMatrix::LAPACK::clapack_getrf(:row, 3, 3, a, 3)
@@ -80,7 +100,7 @@ describe NMatrix::LAPACK do
a[2,2].should be_within(err).of(360.quo(53))
end
- it "exposes clapack potrf" do
+ it "exposes clapack_potrf" do
# first do upper
begin
a = NMatrix.new(:dense, 3, [25,15,-5, 0,18,0, 0,0,11], dtype)
@@ -99,7 +119,7 @@ describe NMatrix::LAPACK do
end
# Together, these calls are basically xGESV from LAPACK: http://www.netlib.org/lapack/double/dgesv.f
- it "exposes clapack getrs" do
+ it "exposes clapack_getrs" do
a = NMatrix.new(3, [-2,4,-3,3,-2,1,0,-4,3], dtype: dtype)
ipiv = NMatrix::LAPACK::clapack_getrf(:row, 3, 3, a, 3)
b = NMatrix.new([3,1], [-1, 17, -9], dtype: dtype)
@@ -111,7 +131,7 @@ describe NMatrix::LAPACK do
b[2].should == -13
end
- it "exposes clapack getri" do
+ it "exposes clapack_getri" do
a = NMatrix.new(:dense, 3, [1,0,4,1,1,6,-3,0,-10], dtype)
ipiv = NMatrix::LAPACK::clapack_getrf(:row, 3, 3, a, 3) # get pivot from getrf, use for getri
@@ -125,7 +145,7 @@ describe NMatrix::LAPACK do
end
end
- it "exposes lapack gesdd" do
+ it "exposes lapack_gesdd" do
if [:float32, :float64].include? dtype
a = NMatrix.new([5,6], %w|8.79 9.93 9.83 5.45 3.16
6.11 6.91 5.04 -0.27 7.98
@@ -160,6 +180,7 @@ describe NMatrix::LAPACK do
#http://software.intel.com/sites/products/documentation/doclib/mkl_sa/11/mkl_lapack_examples/cgesvd_ex.c.htm
pending "Example may be wrong"
else
+ pending "Not implemented for non-LAPACK dtypes"
a = NMatrix.new([4,3], dtype: dtype)
end
err = case dtype
@@ -173,7 +194,7 @@ describe NMatrix::LAPACK do
err = err *5e1
begin
- info = NMatrix::LAPACK::lapack_gesvd(:a, :a, a.shape[0], a.shape[1], a, a.shape[0], s, u, ldu, vt, ldvt, 500)
+ info = NMatrix::LAPACK::lapack_gesdd(:a, a.shape[0], a.shape[1], a, a.shape[0], s, u, ldu, vt, ldvt, 500)
rescue NotImplementedError => e
pending e.to_s
@@ -186,7 +207,7 @@ describe NMatrix::LAPACK do
end
- it "exposes lapack gesvd" do
+ it "exposes lapack_gesvd" do
# http://software.intel.com/sites/products/documentation/doclib/mkl_sa/11/mkl_lapack_examples/dgesvd_ex.c.htm
if [:float32, :float64].include? dtype
a = NMatrix.new([5,6], %w|8.79 9.93 9.83 5.45 3.16
@@ -257,6 +278,133 @@ describe NMatrix::LAPACK do
s.transpose.should be_within(err).of(s_true.row(0))
end
+
+ it "exposes the convenience gesvd method" do
+ # http://software.intel.com/sites/products/documentation/doclib/mkl_sa/11/mkl_lapack_examples/dgesvd_ex.c.htm
+ if [:float32, :float64].include? dtype
+ a = NMatrix.new([5,6], %w|8.79 9.93 9.83 5.45 3.16
+ 6.11 6.91 5.04 -0.27 7.98
+ -9.15 -7.93 4.86 4.85 3.01
+ 9.57 1.64 8.83 0.74 5.80
+ -3.49 4.02 9.80 10.00 4.27
+ 9.84 0.15 -8.99 -6.02 -5.31|.map(&:to_f), dtype: dtype)
+ s_true = NMatrix.new([1,5], [27.468732418221848, 22.643185009774697, 8.558388228482576, 5.985723201512133, 2.014899658715756], dtype: dtype)
+ right_true = NMatrix.new([5,6], [0.5911423764124365, 0.2631678147140568, 0.35543017386282716, 0.3142643627269275, 0.2299383153647484, 0.0, 0.39756679420242547, 0.24379902792633046, -0.22239000068544604, -0.7534661509534584, -0.36358968669749664, 0.0, 0.03347896906244727, -0.6002725806935828, -0.45083926892230763, 0.23344965724471425, -0.3054757327479317, 0.0, 0.4297069031370182, 0.23616680628112555, -0.6858628638738117, 0.3318600182003095, 0.1649276348845103, 0.0, 0.46974792156 [...]
+ #right_true = NMatrix.new([5,6],
+ # %w|-0.59 0.26 0.36 0.31 0.23
+ # -0.40 0.24 -0.22 -0.75 -0.36
+ # -0.03 -0.60 -0.45 0.23 -0.31
+ # -0.43 0.24 -0.69 0.33 0.16
+ # -0.47 -0.35 0.39 0.16 -0.52
+ # 0.29 0.58 -0.02 0.38 -0.65|.map(&:to_f),
+ # dtype)
+ left_true = NMatrix.new([5,5], [0.25138279272049635, 0.3968455517769292, 0.6921510074703637, 0.3661704447722309, 0.4076352386533525, 0.814836686086339, 0.3586615001880027, -0.24888801115928438, -0.3685935379446176, -0.09796256926688672, -0.2606185055842211, 0.7007682094072526, -0.22081144672043734, 0.38593848318854174, -0.49325014285102375, 0.3967237771305971, -0.4507112412166429, 0.2513211496937535, 0.4342486014366711, -0.6226840720358049, -0.21802776368654594, 0.1402099498711 [...]
+ #left_true = NMatrix.new([5,5],
+ # %w|-0.25 -0.40 -0.69 -0.37 -0.41
+ # 0.81 0.36 -0.25 -0.37 -0.10
+ # -0.26 0.70 -0.22 0.39 -0.49
+ # 0.40 -0.45 0.25 0.43 -0.62
+ # -0.22 0.14 0.59 -0.63 -0.44|.map(&:to_f),
+ # dtype)
+ s = NMatrix.new([5,1], 0, dtype: dtype)
+ u = NMatrix.new([5,5], 0, dtype: dtype)
+ ldu = 5
+ vt = NMatrix.new([6,6], 0, dtype: dtype)
+ ldvt= 6
+ elsif [:complex64, :complex128].include? dtype
+ #http://software.intel.com/sites/products/documentation/doclib/mkl_sa/11/mkl_lapack_examples/cgesvd_ex.c.htm
+ pending "Example may be wrong"
+ a = NMatrix.new([4,3], [[ 5.91, -5.69], [ 7.09, 2.72], [ 7.78, -4.06], [ -0.79, -7.21], [ -3.15, -4.08], [ -1.89, 3.27], [ 4.57, -2.07], [ -3.88, -3.30], [ -4.89, 4.20], [ 4.10, -6.70], [ 3.28, -3.84], [ 3.84, 1.19]].map {|e| Complex(*e) } , dtype: dtype)
+ s_true = NMatrix.new([3,1], [17.63, 11.61, 6.78], dtype: dtype)
+ left_true = NMatrix.new([4,4], [[-0.86, 0.0], [0.4, 0.0], [0.32, 0.0], [-0.35, 0.13], [-0.24, -0.21], [-0.63, 0.6], [0.15, 0.32], [0.61, 0.61], [-0.36, 0.1]].map {|e| Complex(*e)}, dtype: dtype)
+ right_true = NMatrix.new([4,3], [[ -0.22, 0.51], [ -0.37, -0.32], [ -0.53, 0.11], [ 0.15, 0.38], [ 0.31, 0.31], [ 0.09, -0.57], [ 0.18, -0.39], [ 0.38, -0.39], [ 0.53, 0.24], [ 0.49, 0.28], [ -0.47, -0.25], [ -0.15, 0.19]].map {|e| Complex *e} , dtype: dtype)
+
+ s = NMatrix.new([3,1], 0, dtype: dtype)
+ u = NMatrix.new([4,4], 0, dtype: dtype)
+ ldu = 4
+ vt = NMatrix.new([3,3], 0, dtype: dtype)
+ ldvt= 3
+ else
+ a = NMatrix.new([4,3], dtype: dtype)
+ end
+ err = case dtype
+ when :float32, :complex64
+ 1e-6
+ when :float64, :complex128
+ 1e-15
+ else
+ 1e-64 # FIXME: should be 0, but be_within(0) does not work.
+ end
+ err = err *5e1
+ begin
+ u, s, vt = a.gesvd
+ rescue NotImplementedError => e
+ pending e.to_s
+ end
+ u.should be_within(err).of(left_true)
+ #FIXME: Is the next line correct?
+ vt[0...right_true.shape[0], 0...right_true.shape[1]-1].should be_within(err).of(right_true[0...right_true.shape[0],0...right_true.shape[1]-1])
+ s.transpose.should be_within(err).of(s_true.row(0))
+
+ end
+ it "exposes the convenience gesdd method" do
+ # http://software.intel.com/sites/products/documentation/doclib/mkl_sa/11/mkl_lapack_examples/dgesvd_ex.c.htm
+ if [:float32, :float64].include? dtype
+ a = NMatrix.new([5,6], %w|8.79 9.93 9.83 5.45 3.16
+ 6.11 6.91 5.04 -0.27 7.98
+ -9.15 -7.93 4.86 4.85 3.01
+ 9.57 1.64 8.83 0.74 5.80
+ -3.49 4.02 9.80 10.00 4.27
+ 9.84 0.15 -8.99 -6.02 -5.31|.map(&:to_f), dtype: dtype)
+ s_true = NMatrix.new([1,5], [27.468732418221848, 22.643185009774697, 8.558388228482576, 5.985723201512133, 2.014899658715756], dtype: dtype)
+ right_true = NMatrix.new([5,6], [0.5911423764124365, 0.2631678147140568, 0.35543017386282716, 0.3142643627269275, 0.2299383153647484, 0.0, 0.39756679420242547, 0.24379902792633046, -0.22239000068544604, -0.7534661509534584, -0.36358968669749664, 0.0, 0.03347896906244727, -0.6002725806935828, -0.45083926892230763, 0.23344965724471425, -0.3054757327479317, 0.0, 0.4297069031370182, 0.23616680628112555, -0.6858628638738117, 0.3318600182003095, 0.1649276348845103, 0.0, 0.46974792156 [...]
+ left_true = NMatrix.new([5,5], [0.25138279272049635, 0.3968455517769292, 0.6921510074703637, 0.3661704447722309, 0.4076352386533525, 0.814836686086339, 0.3586615001880027, -0.24888801115928438, -0.3685935379446176, -0.09796256926688672, -0.2606185055842211, 0.7007682094072526, -0.22081144672043734, 0.38593848318854174, -0.49325014285102375, 0.3967237771305971, -0.4507112412166429, 0.2513211496937535, 0.4342486014366711, -0.6226840720358049, -0.21802776368654594, 0.1402099498711 [...]
+ u = NMatrix.new([5,5], 0, dtype: dtype)
+ ldu = 5
+ vt = NMatrix.new([6,6], 0, dtype: dtype)
+ ldvt= 6
+ elsif [:complex64, :complex128].include? dtype
+ #http://software.intel.com/sites/products/documentation/doclib/mkl_sa/11/mkl_lapack_examples/cgesvd_ex.c.htm
+ pending "Example may be wrong"
+ a = NMatrix.new([4,3], [[ 5.91, -5.69], [ 7.09, 2.72], [ 7.78, -4.06], [ -0.79, -7.21], [ -3.15, -4.08], [ -1.89, 3.27], [ 4.57, -2.07], [ -3.88, -3.30], [ -4.89, 4.20], [ 4.10, -6.70], [ 3.28, -3.84], [ 3.84, 1.19]].map {|e| Complex(*e) } , dtype: dtype)
+ s_true = NMatrix.new([3,1], [17.63, 11.61, 6.78], dtype: dtype)
+ left_true = NMatrix.new([4,4], [[-0.86, 0.0], [0.4, 0.0], [0.32, 0.0], [-0.35, 0.13], [-0.24, -0.21], [-0.63, 0.6], [0.15, 0.32], [0.61, 0.61], [-0.36, 0.1]].map {|e| Complex(*e)}, dtype: dtype)
+ right_true = NMatrix.new([4,3], [[ -0.22, 0.51], [ -0.37, -0.32], [ -0.53, 0.11], [ 0.15, 0.38], [ 0.31, 0.31], [ 0.09, -0.57], [ 0.18, -0.39], [ 0.38, -0.39], [ 0.53, 0.24], [ 0.49, 0.28], [ -0.47, -0.25], [ -0.15, 0.19]].map {|e| Complex *e} , dtype: dtype)
+
+ s = NMatrix.new([3,1], 0, dtype: dtype)
+ u = NMatrix.new([4,4], 0, dtype: dtype)
+ ldu = 4
+ vt = NMatrix.new([3,3], 0, dtype: dtype)
+ ldvt= 3
+ else
+ a = NMatrix.new([4,3], dtype: dtype)
+ end
+ s = NMatrix.new([5,1], 0, dtype: dtype)
+ u = NMatrix.new([5,5], 0, dtype: dtype)
+ ldu = 5
+ vt = NMatrix.new([6,6], 0, dtype: dtype)
+ ldvt= 6
+ err = case dtype
+ when :float32, :complex64
+ 1e-6
+ when :float64, :complex128
+ 1e-15
+ else
+ 1e-64 # FIXME: should be 0, but be_within(0) does not work.
+ end
+ err = err *5e1
+ begin
+
+ u, s, vt = a.gesdd(500)
+
+ rescue NotImplementedError => e
+ pending e.to_s
+ end
+ u.should be_within(err).of(left_true)
+ #FIXME: Is the next line correct?
+ vt[0...right_true.shape[0], 0...right_true.shape[1]-1].should be_within(err).of(right_true[0...right_true.shape[0],0...right_true.shape[1]-1])
+ s.transpose.should be_within(err).of(s_true.row(0))
+ end
it "exposes geev" do
diff --git a/spec/math_spec.rb b/spec/math_spec.rb
index 3d4b029..ea21f4f 100644
--- a/spec/math_spec.rb
+++ b/spec/math_spec.rb
@@ -8,8 +8,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-# NMatrix is Copyright (c) 2012, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -26,10 +26,101 @@
# versions of unfriendly BLAS and LAPACK functions.
#
-# Can we use require_relative here instead?
-require File.join(File.dirname(__FILE__), "spec_helper.rb")
+require 'spec_helper'
describe "math" do
+ #after :each do
+ # GC.start
+ #end
+
+ context "elementwise math functions" do
+
+ [:dense,:list,:yale].each do |stype|
+ context stype do
+
+ [:int64,:float64,:rational128].each do |dtype|
+ context dtype do
+ before :each do
+ @size = [2,2]
+ @m = NMatrix.seq(@size, dtype: dtype, stype: stype)+1
+ @a = @m.to_a.flatten
+ end
+
+ NMatrix::NMMath::METHODS_ARITY_1.each do |meth|
+ #skip inverse regular trig functions
+ next if meth.to_s.start_with?('a') and (not meth.to_s.end_with?('h')) \
+ and NMatrix::NMMath::METHODS_ARITY_1.include?(
+ meth.to_s[1...meth.to_s.length].to_sym)
+ next if meth == :atanh
+
+ it "should correctly apply elementwise #{meth}" do
+
+ @m.send(meth).should eq N.new(@size, @a.map{ |e| Math.send(meth, e) },
+ dtype: :float64, stype: stype)
+ end
+ end
+
+ NMatrix::NMMath::METHODS_ARITY_2.each do |meth|
+ next if meth == :atan2
+ it "should correctly apply elementwise #{meth}" do
+ @m.send(meth, @m).should eq N.new(@size, @a.map{ |e|
+ Math.send(meth, e, e) },
+ dtype: :float64,
+ stype: stype)
+ end
+
+ it "should correctly apply elementwise #{meth} with a scalar first arg" do
+ Math.send(meth, 1, @m).should eq N.new(@size, @a.map { |e| Math.send(meth, 1, e) }, dtype: :float64, stype: stype)
+ end
+
+ it "should correctly apply elementwise #{meth} with a scalar second arg" do
+ @m.send(meth, 1).should eq N.new(@size, @a.map { |e| Math.send(meth, e, 1) }, dtype: :float64, stype: stype)
+ end
+ end
+
+ it "should correctly apply elementwise natural log" do
+ @m.log.should eq N.new(@size, [0, Math.log(2), Math.log(3), Math.log(4)],
+ dtype: :float64, stype: stype)
+ end
+
+ it "should correctly apply elementwise log with arbitrary base" do
+ @m.log(3).should eq N.new(@size, [0, Math.log(2,3), 1, Math.log(4,3)],
+ dtype: :float64, stype: stype)
+ end
+
+ context "inverse trig functions" do
+ before :each do
+ @m = NMatrix.seq(@size, dtype: dtype, stype: stype)/4
+ @a = @m.to_a.flatten
+ end
+ [:asin, :acos, :atan, :atanh].each do |atf|
+
+ it "should correctly apply elementwise #{atf}" do
+ @m.send(atf).should eq N.new(@size,
+ @a.map{ |e| Math.send(atf, e) },
+ dtype: :float64, stype: stype)
+ end
+ end
+
+ it "should correctly apply elementtwise atan2" do
+ @m.atan2(@m*0+1).should eq N.new(@size,
+ @a.map { |e| Math.send(:atan2, e, 1) }, dtype: :float64, stype: stype)
+ end
+
+ it "should correctly apply elementwise atan2 with a scalar first arg" do
+ Math.atan2(1, @m).should eq N.new(@size, @a.map { |e| Math.send(:atan2, 1, e) }, dtype: :float64, stype: stype)
+ end
+
+ it "should correctly apply elementwise atan2 with a scalar second arg" do
+ @m.atan2(1).should eq N.new(@size, @a.map { |e| Math.send(:atan2, e, 1) }, dtype: :float64, stype: stype)
+ end
+ end
+ end
+ end
+
+ end
+ end
+ end
[:float32, :float64, :complex64, :complex128, :rational32, :rational64, :rational128].each do |dtype|
context dtype do
diff --git a/spec/nmatrix_yale_spec.rb b/spec/nmatrix_yale_spec.rb
index 74040f7..5ed1c1a 100644
--- a/spec/nmatrix_yale_spec.rb
+++ b/spec/nmatrix_yale_spec.rb
@@ -8,8 +8,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-# NMatrix is Copyright (c) 2012, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -27,6 +27,10 @@
require "./lib/nmatrix"
describe NMatrix do
+ #after :each do
+ # GC.start
+ #end
+
context :yale do
it "compares two empty matrices" do
@@ -106,8 +110,8 @@ describe NMatrix do
n[0,0] = 0.1
n[0,1] = 0.2
n[1,0] = 0.3
- n.yale_a == [0.1, 0.0, 0.0, 0.2, 0.3]
- n.yale_ija == [3,4,5,1,0]
+ n.yale_a.should == [0.1, 0.0, 0.0, 0.2, 0.3]
+ n.yale_ija.should == [3,4,5,1,0]
end
it "sets when resizing" do
@@ -267,28 +271,19 @@ describe NMatrix do
mn[0,0].should == 541
end
- it "transposes" do
- a = NMatrix.new(4, 0.0, stype: :yale)
- a[0,0] = 1.0
- a[0,1] = 4.0
- a[1,2] = 2.0
- a[1,3] = -4.0
- a[3,1] = 5.0
- a[3,3] = 6.0
- b = a.transpose
-
- b[0,0].should == 1.0
- b[1,0].should == 4.0
- b[2,0].should == 0.0
- b[3,0].should == 0.0
- b[0,1].should == 0.0
- b[1,1].should == 0.0
- b[2,1].should == 2.0
- b[3,1].should == -4.0
- b[0,3].should == 0.0
- b[1,3].should == 5.0
- b[2,3].should == 0.0
- b[3,3].should == 6.0
+ it "calculates the row key intersections of two matrices" do
+ a = NMatrix.new([3,9], [0,1], stype: :yale, dtype: :byte, default: 0)
+ b = NMatrix.new([3,9], [0,0,1,0,1], stype: :yale, dtype: :byte, default: 0)
+ a.extend NMatrix::YaleFunctions
+ b.extend NMatrix::YaleFunctions
+
+ (0...3).each do |ai|
+ (0...3).each do |bi|
+ STDERR.puts (a.yale_ja_d_keys_at(ai) & b.yale_ja_d_keys_at(bi)).inspect
+ (a.yale_ja_d_keys_at(ai) & b.yale_ja_d_keys_at(bi)).should == a.yale_row_keys_intersection(ai, b, bi)
+ end
+ end
+
end
end
end
diff --git a/spec/rspec_monkeys.rb b/spec/rspec_monkeys.rb
index 83909d2..e2f7a1f 100644
--- a/spec/rspec_monkeys.rb
+++ b/spec/rspec_monkeys.rb
@@ -1,3 +1,30 @@
+# = NMatrix
+#
+# A linear algebra library for scientific computation in Ruby.
+# NMatrix is part of SciRuby.
+#
+# NMatrix was originally inspired by and derived from NArray, by
+# Masahiro Tanaka: http://narray.rubyforge.org
+#
+# == Copyright Information
+#
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
+#
+# Please see LICENSE.txt for additional copyright notices.
+#
+# == Contributing
+#
+# By contributing source code to SciRuby, you agree to be bound by
+# our Contributor Agreement:
+#
+# * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
+#
+# == rspec_monkeys.rb
+#
+# A set of monkey patches for RSpec allowing checks of NMatrix types
+#
+
module RSpec::Matchers::BuiltIn
class BeWithin
diff --git a/spec/rspec_spec.rb b/spec/rspec_spec.rb
index 2fb4ecf..7da1a3f 100644
--- a/spec/rspec_spec.rb
+++ b/spec/rspec_spec.rb
@@ -8,8 +8,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-# NMatrix is Copyright (c) 2012, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
diff --git a/spec/shortcuts_spec.rb b/spec/shortcuts_spec.rb
index e74f83e..be411f0 100644
--- a/spec/shortcuts_spec.rb
+++ b/spec/shortcuts_spec.rb
@@ -8,8 +8,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-# NMatrix is Copyright (c) 2012, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -30,6 +30,9 @@ require File.join(File.dirname(__FILE__), "spec_helper.rb")
require 'pry'
describe NMatrix do
+ #after :each do
+ # GC.start
+ #end
it "zeros() creates a matrix of zeros" do
m = NMatrix.zeros(3)
@@ -175,13 +178,8 @@ describe NMatrix do
context "_like constructors" do
before :each do
- STDERR.puts "starting GC"
- GC.start
- STDERR.puts "GC finished"
@nm_1d = NMatrix[5.0,0.0,1.0,2.0,3.0]
- STDERR.puts "@nm_1d"
@nm_2d = NMatrix[[0.0,1.0],[2.0,3.0]]
- STDERR.puts "@nm_2d"
end
it "should create an nmatrix of ones with dimensions and type the same as its argument" do
@@ -190,11 +188,8 @@ describe NMatrix do
end
it "should create an nmatrix of zeros with dimensions and type the same as its argument" do
- STDERR.puts "A"
NMatrix.zeros_like(@nm_1d).should eq NMatrix[0.0, 0.0, 0.0, 0.0, 0.0]
- STDERR.puts "B"
NMatrix.zeros_like(@nm_2d).should eq NMatrix[[0.0, 0.0], [0.0, 0.0]]
- STDERR.puts "C"
end
end
diff --git a/spec/slice_set_spec.rb b/spec/slice_set_spec.rb
index 0919530..acbffc6 100644
--- a/spec/slice_set_spec.rb
+++ b/spec/slice_set_spec.rb
@@ -8,8 +8,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-# NMatrix is Copyright (c) 2012, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -30,6 +30,10 @@ require File.dirname(__FILE__) + "/spec_helper.rb"
describe "Set slice operation" do
include RSpec::Longrun::DSL
+ #after :each do
+ # GC.start
+ #end
+
[:dense, :yale, :list].each do |stype|
context "for #{stype}" do
before :each do
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 39b0ff5..59045a5 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -8,8 +8,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-# NMatrix is Copyright (c) 2012, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -25,6 +25,7 @@
# Common data and helper functions for testing.
require "rspec/longrun"
+#require "narray/narray"
require "./lib/nmatrix"
require "./lib/nmatrix/rspec"
diff --git a/spec/stat_spec.rb b/spec/stat_spec.rb
index 0d60444..ffad66e 100644
--- a/spec/stat_spec.rb
+++ b/spec/stat_spec.rb
@@ -8,8 +8,8 @@
#
# == Copyright Information
#
-# SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
-# NMatrix is Copyright (c) 2012, Ruby Science Foundation
+# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
+# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
@@ -31,162 +31,178 @@ require 'pry'
describe "Statistical functions" do
context "mapping and reduction related functions" do
-
- before :each do
- @nm_1d = NMatrix[5.0,0.0,1.0,2.0,3.0]
- @nm_2d = NMatrix[[0.0,1.0],[2.0,3.0]]
- end
-
- it "behaves like Enumerable#reduce with no argument to reduce" do
- @nm_1d.reduce_along_dim(0) { |acc, el| acc + el }.to_f.should eq 11
- @nm_2d.reduce_along_dim(1) { |acc, el| acc + el }.should eq NMatrix[[1, 5]]
- end
-
- it "should calculate the mean along the specified dimension" do
- @nm_1d.mean.should eq NMatrix[2.2]
- @nm_2d.mean.should eq NMatrix[[1.0,2.0]]
- end
-
- it "should calculate the minimum along the specified dimension" do
- @nm_1d.min.should eq 0.0
- @nm_2d.min.should eq NMatrix[[0.0, 1.0]]
- @nm_2d.min(1).should eq NMatrix[[0.0], [2.0]]
- end
-
- it "should calculate the maximum along the specified dimension" do
- @nm_1d.max.should eq 5.0
- @nm_2d.max.should eq NMatrix[[2.0, 3.0]]
- end
-
- it "should calculate the variance along the specified dimension" do
- @nm_1d.variance.should eq NMatrix[3.7]
- @nm_2d.variance(1).should eq NMatrix[[0.5], [0.5]]
- end
-
- it "should calculate the sum along the specified dimension" do
- @nm_1d.sum.should eq NMatrix[11]
- @nm_2d.sum.should eq NMatrix[[2], [4]]
- end
-
- it "should calculate the standard deviation along the specified dimension" do
- @nm_1d.std.should eq NMatrix[Math.sqrt(3.7)]
- @nm_2d.std(1).should eq NMatrix[[Math.sqrt(0.5)], [Math.sqrt(0.5)]]
- end
-
- it "should raise an ArgumentError when any invalid dimension is provided" do
- expect { @nm_1d.mean(3) }.to raise_exception(RangeError)
- end
-
- it "should convert to float if it contains only a single element" do
- NMatrix[4.0].to_f.should eq 4.0
- NMatrix[[[[4.0]]]].to_f.should eq 4.0
- end
-
- it "should raise an index error if it contains more than a single element" do
- expect { @nm_1d.to_f }.to raise_error(IndexError)
- end
-
- it "should map a block to all elements" do
- @nm_1d.map { |e| e ** 2 }.should eq NMatrix[25.0,0.0,1.0,4.0,9.0]
- @nm_2d.map { |e| e ** 2 }.should eq NMatrix[[0.0,1.0],[4.0,9.0]]
- end
-
- it "should map! a block to all elements in place" do
- fct = Proc.new { |e| e ** 2 }
- expected1 = @nm_1d.map &fct
- expected2 = @nm_2d.map &fct
- @nm_1d.map! &fct
- @nm_1d.should eq expected1
- @nm_2d.map! &fct
- @nm_2d.should eq expected2
- end
-
- it "should return an enumerator for map without a block" do
- @nm_1d.map.should be_a Enumerator
- end
-
- it "should return an enumerator for reduce without a block" do
- @nm_1d.reduce_along_dim(0).should be_a Enumerator
- end
-
- it "should return an enumerator for each_along_dim without a block" do
- @nm_1d.each_along_dim(0).should be_a Enumerator
- end
-
- it "should iterate correctly for map without a block" do
- en = @nm_1d.map
- en.each { |e| e**2 }.should eq @nm_1d.map { |e| e**2 }
- en = @nm_2d.map
- en.each { |e| e**2 }.should eq @nm_2d.map { |e| e**2 }
- end
-
- it "should iterate correctly for reduce without a block" do
- en = @nm_1d.reduce_along_dim(0, 1.0)
- en.each { |a, e| a+e }.to_f.should eq 12
- en = @nm_2d.reduce_along_dim(1, 1.0)
- en.each { |a, e| a+e }.should eq NMatrix[[2.0],[6.0]]
- end
-
- it "should iterate correctly for each_along_dim without a block" do
- res = NMatrix.zeros_like(@nm_1d[0...1])
- en = @nm_1d.each_along_dim(0)
- en.each { |e| res += e }
- res.to_f.should eq 11
-
- res = NMatrix.zeros_like (@nm_2d[0...2, 0])
- en = @nm_2d.each_along_dim(1)
- en.each { |e| res += e }
- res.should eq NMatrix[[1.0], [5.0]]
- end
-
- it "should yield matrices of matching dtype for each_along_dim" do
- m = NMatrix.new([2,3], [1,2,3,3,4,5], dtype: :complex128)
- m.each_along_dim(1) do |sub_m|
- sub_m.dtype.should eq :complex128
- end
- end
-
- it "should reduce to a matrix of matching dtype for reduce_along_dim" do
- m = NMatrix.new([2,3], [1,2,3,3,4,5], dtype: :complex128)
- m.reduce_along_dim(1) do |acc, sub_m|
- sub_m.dtype.should eq :complex128
- acc
- end
-
- m = NMatrix.new([2,3], [1,2,3,3,4,5], dtype: :complex128)
- m.reduce_along_dim(1, 0.0) do |acc, sub_m|
- sub_m.dtype.should eq :complex128
- acc
- end
- end
-
- it "should allow overriding the dtype for reduce_along_dim" do
- m = NMatrix[[1,2,3], [3,4,5], dtype: :complex128]
- m.reduce_along_dim(1, 0.0, :float64) do |acc, sub_m|
- acc.dtype.should eq :float64
- acc
- end
-
- m = NMatrix[[1,2,3], [3,4,5], dtype: :complex128]
- m.reduce_along_dim(1, nil, :float64) do |acc, sub_m|
- acc.dtype.should eq :float64
- acc
+ [:dense, :yale, :list].each do |stype|
+ context "on #{stype} matrices" do
+ before :each do
+ @nm_1d = NMatrix.new([5], [5.0,0.0,1.0,2.0,3.0], stype: stype) unless stype == :yale
+ @nm_2d = NMatrix.new([2,2], [0.0, 1.0, 2.0, 3.0], stype: stype)
+ end
+
+ it "behaves like Enumerable#reduce with no argument to reduce" do
+ @nm_1d.reduce_along_dim(0) { |acc, el| acc + el }.to_f.should eq 11 unless stype == :yale
+ @nm_2d.reduce_along_dim(1) { |acc, el| acc + el }.should eq NMatrix.new([2,1], [1.0, 5.0], stype: stype)
+ end
+
+ it "should calculate the mean along the specified dimension" do
+ unless stype == :yale then
+ puts @nm_1d.mean
+ @nm_1d.mean.should eq NMatrix.new([1], [2.2], stype: stype, dtype: :float64)
+ end
+ @nm_2d.mean.should eq NMatrix[[1.0,2.0], stype: stype]
+ @nm_2d.mean(1).should eq NMatrix[[0.5], [2.5], stype: stype]
+ end
+
+ it "should calculate the minimum along the specified dimension" do
+ @nm_1d.min.should eq 0.0 unless stype == :yale
+ @nm_2d.min.should eq NMatrix[[0.0, 1.0], stype: stype]
+ @nm_2d.min(1).should eq NMatrix[[0.0], [2.0], stype: stype]
+ end
+
+ it "should calculate the maximum along the specified dimension" do
+ @nm_1d.max.should eq 5.0 unless stype == :yale
+ @nm_2d.max.should eq NMatrix[[2.0, 3.0], stype: stype]
+ end
+
+ it "should calculate the variance along the specified dimension" do
+ @nm_1d.variance.should eq NMatrix[3.7, stype: stype] unless stype == :yale
+ @nm_2d.variance(1).should eq NMatrix[[0.5], [0.5], stype: stype]
+ end
+
+ it "should calculate the sum along the specified dimension" do
+ @nm_1d.sum.should eq NMatrix[11.0, stype: stype] unless stype == :yale
+ @nm_2d.sum.should eq NMatrix[[2.0, 4.0], stype: stype]
+ end
+
+ it "should calculate the standard deviation along the specified dimension" do
+ @nm_1d.std.should eq NMatrix[Math.sqrt(3.7), stype: stype] unless stype == :yale
+ @nm_2d.std(1).should eq NMatrix[[Math.sqrt(0.5)], [Math.sqrt(0.5)], stype: stype]
+ end
+
+ it "should raise an ArgumentError when any invalid dimension is provided" do
+ expect { @nm_1d.mean(3) }.to raise_exception(RangeError) unless stype == :yale
+ expect { @nm_2d.mean(3) }.to raise_exception(RangeError)
+ end
+
+ it "should convert to float if it contains only a single element" do
+ NMatrix[4.0, stype: stype].to_f.should eq 4.0 unless stype == :yale
+ NMatrix[[[[4.0]]], stype: stype].to_f.should eq 4.0 unless stype == :yale
+ NMatrix[[4.0], stype: stype].to_f.should eq 4.0
+ end
+
+ it "should raise an index error if it contains more than a single element" do
+ expect { @nm_1d.to_f }.to raise_error(IndexError) unless stype == :yale
+ expect { @nm_2d.to_f }.to raise_error(IndexError)
+ end
+
+ it "should map a block to all elements" do
+ #binding.pry if stype == :list
+ @nm_1d.map { |e| e ** 2 }.should eq NMatrix[25.0,0.0,1.0,4.0,9.0, stype: stype] unless stype == :yale
+ @nm_2d.map { |e| e ** 2 }.should eq NMatrix[[0.0,1.0],[4.0,9.0], stype: stype]
+ end
+
+ it "should map! a block to all elements in place" do
+ fct = Proc.new { |e| e ** 2 }
+ unless stype == :yale then
+ expected1 = @nm_1d.map &fct
+ @nm_1d.map! &fct
+ @nm_1d.should eq expected1
+ end
+ expected2 = @nm_2d.map &fct
+ @nm_2d.map! &fct
+ @nm_2d.should eq expected2
+ end
+
+ it "should return an enumerator for map without a block" do
+ @nm_2d.map.should be_a Enumerator
+ end
+
+ it "should return an enumerator for reduce without a block" do
+ @nm_2d.reduce_along_dim(0).should be_a Enumerator
+ end
+
+ it "should return an enumerator for each_along_dim without a block" do
+ @nm_2d.each_along_dim(0).should be_a Enumerator
+ end
+
+ it "should iterate correctly for map without a block" do
+ en = @nm_1d.map unless stype == :yale
+ en.each { |e| e**2 }.should eq @nm_1d.map { |e| e**2 } unless stype == :yale
+ en = @nm_2d.map
+ en.each { |e| e**2 }.should eq @nm_2d.map { |e| e**2 }
+ end
+
+ it "should iterate correctly for reduce without a block" do
+ unless stype == :yale then
+ en = @nm_1d.reduce_along_dim(0, 1.0)
+ en.each { |a, e| a+e }.to_f.should eq 12
+ end
+ en = @nm_2d.reduce_along_dim(1, 1.0)
+ en.each { |a, e| a+e }.should eq NMatrix[[2.0],[6.0], stype: stype]
+ end
+
+ it "should iterate correctly for each_along_dim without a block" do
+ unless stype == :yale then
+ res = NMatrix.zeros_like(@nm_1d[0...1])
+ en = @nm_1d.each_along_dim(0)
+ en.each { |e| res += e }
+ res.to_f.should eq 11
+ end
+ res = NMatrix.zeros_like (@nm_2d[0...2, 0])
+ en = @nm_2d.each_along_dim(1)
+ en.each { |e| res += e }
+ res.should eq NMatrix[[1.0], [5.0], stype: stype]
+ end
+
+ it "should yield matrices of matching dtype for each_along_dim" do
+ m = NMatrix.new([2,3], [1,2,3,3,4,5], dtype: :complex128, stype: stype)
+ m.each_along_dim(1) do |sub_m|
+ sub_m.dtype.should eq :complex128
+ end
+ end
+
+ it "should reduce to a matrix of matching dtype for reduce_along_dim" do
+ m = NMatrix.new([2,3], [1,2,3,3,4,5], dtype: :complex128, stype: stype)
+ m.reduce_along_dim(1) do |acc, sub_m|
+ sub_m.dtype.should eq :complex128
+ acc
+ end
+
+ m = NMatrix.new([2,3], [1,2,3,3,4,5], dtype: :complex128, stype: stype)
+ m.reduce_along_dim(1, 0.0) do |acc, sub_m|
+ sub_m.dtype.should eq :complex128
+ acc
+ end
+ end
+
+ it "should allow overriding the dtype for reduce_along_dim" do
+ m = NMatrix[[1,2,3], [3,4,5], dtype: :complex128]
+ m.reduce_along_dim(1, 0.0, :float64) do |acc, sub_m|
+ acc.dtype.should eq :float64
+ acc
+ end
+
+ m = NMatrix[[1,2,3], [3,4,5], dtype: :complex128, stype: stype]
+ m.reduce_along_dim(1, nil, :float64) do |acc, sub_m|
+ acc.dtype.should eq :float64
+ acc
+ end
+ end
+
+ it "should convert integer dtypes to float when calculating mean" do
+ m = NMatrix[[1,2,3], [3,4,5], dtype: :int32, stype: stype]
+ m.mean(0).dtype.should eq :float64
+ end
+
+ it "should convert integer dtypes to float when calculating variance" do
+ m = NMatrix[[1,2,3], [3,4,5], dtype: :int32, stype: stype]
+ m.variance(0).dtype.should eq :float64
+ end
+
+ it "should convert integer dtypes to float when calculating standard deviation" do
+ m = NMatrix[[1,2,3], [3,4,5], dtype: :int32, stype: stype]
+ m.std(0).dtype.should eq :float64
+ end
end
end
-
- it "should convert integer dtypes to float when calculating mean" do
- m = NMatrix[[1,2,3], [3,4,5], dtype: :int32]
- m.mean(0).dtype.should eq :float64
- end
-
- it "should convert integer dtypes to float when calculating variance" do
- m = NMatrix[[1,2,3], [3,4,5], dtype: :int32]
- m.variance(0).dtype.should eq :float64
- end
-
- it "should convert integer dtypes to float when calculating standard deviation" do
- m = NMatrix[[1,2,3], [3,4,5], dtype: :int32]
- m.std(0).dtype.should eq :float64
- end
end
-end
\ No newline at end of file
+end
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-nmatrix.git
More information about the Pkg-ruby-extras-commits
mailing list