nmatrix 0.1.0.rc5 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -1
- data/Gemfile +0 -2
- data/History.txt +39 -4
- data/LICENSE.txt +3 -1
- data/Manifest.txt +2 -0
- data/README.rdoc +6 -14
- data/Rakefile +4 -1
- data/ext/nmatrix/data/data.cpp +1 -1
- data/ext/nmatrix/data/data.h +2 -1
- data/ext/nmatrix/data/rational.h +230 -226
- data/ext/nmatrix/extconf.rb +7 -4
- data/ext/nmatrix/math.cpp +259 -172
- data/ext/nmatrix/math/getri.h +2 -2
- data/ext/nmatrix/math/math.h +1 -1
- data/ext/nmatrix/ruby_constants.cpp +0 -1
- data/ext/nmatrix/ruby_nmatrix.c +55 -32
- data/ext/nmatrix/storage/dense/dense.cpp +1 -0
- data/ext/nmatrix/storage/yale/yale.cpp +12 -14
- data/ext/nmatrix/ttable_helper.rb +0 -1
- data/lib/nmatrix.rb +5 -0
- data/lib/nmatrix/homogeneous.rb +98 -0
- data/lib/nmatrix/io/fortran_format.rb +135 -0
- data/lib/nmatrix/io/harwell_boeing.rb +220 -0
- data/lib/nmatrix/io/market.rb +18 -8
- data/lib/nmatrix/io/mat5_reader.rb +16 -111
- data/lib/nmatrix/io/mat_reader.rb +3 -5
- data/lib/nmatrix/io/point_cloud.rb +27 -28
- data/lib/nmatrix/lapack.rb +3 -1
- data/lib/nmatrix/math.rb +112 -43
- data/lib/nmatrix/monkeys.rb +67 -11
- data/lib/nmatrix/nmatrix.rb +56 -33
- data/lib/nmatrix/rspec.rb +2 -2
- data/lib/nmatrix/shortcuts.rb +42 -25
- data/lib/nmatrix/version.rb +4 -4
- data/nmatrix.gemspec +4 -3
- data/spec/03_nmatrix_monkeys_spec.rb +72 -0
- data/spec/blas_spec.rb +4 -0
- data/spec/homogeneous_spec.rb +12 -4
- data/spec/io/fortran_format_spec.rb +88 -0
- data/spec/io/harwell_boeing_spec.rb +98 -0
- data/spec/io/test.rua +9 -0
- data/spec/math_spec.rb +51 -9
- metadata +38 -9
data/ext/nmatrix/math/getri.h
CHANGED
@@ -68,7 +68,7 @@ inline int getri(const enum CBLAS_ORDER order, const int n, DType* a, const int
|
|
68
68
|
return 0;
|
69
69
|
}
|
70
70
|
|
71
|
-
#
|
71
|
+
#if defined (HAVE_CLAPACK_H) || defined (HAVE_ATLAS_CLAPACK_H)
|
72
72
|
template <>
|
73
73
|
inline int getri(const enum CBLAS_ORDER order, const int n, float* a, const int lda, const int* ipiv) {
|
74
74
|
return clapack_sgetri(order, n, a, lda, ipiv);
|
@@ -105,4 +105,4 @@ inline int clapack_getri(const enum CBLAS_ORDER order, const int n, void* a, con
|
|
105
105
|
|
106
106
|
} } // end nm::math
|
107
107
|
|
108
|
-
#endif // GETRI_H
|
108
|
+
#endif // GETRI_H
|
data/ext/nmatrix/math/math.h
CHANGED
@@ -104,10 +104,10 @@ extern "C" {
|
|
104
104
|
* C accessors.
|
105
105
|
*/
|
106
106
|
void nm_math_det_exact(const int M, const void* elements, const int lda, nm::dtype_t dtype, void* result);
|
107
|
+
void nm_math_inverse(const int M, void* A_elements, nm::dtype_t dtype);
|
107
108
|
void nm_math_inverse_exact(const int M, const void* A_elements, const int lda, void* B_elements, const int ldb, nm::dtype_t dtype);
|
108
109
|
void nm_math_transpose_generic(const size_t M, const size_t N, const void* A, const int lda, void* B, const int ldb, size_t element_size);
|
109
110
|
void nm_math_init_blas(void);
|
110
|
-
|
111
111
|
}
|
112
112
|
|
113
113
|
|
data/ext/nmatrix/ruby_nmatrix.c
CHANGED
@@ -134,6 +134,7 @@ DECL_UNARY_RUBY_ACCESSOR(gamma)
|
|
134
134
|
DECL_UNARY_RUBY_ACCESSOR(negate)
|
135
135
|
DECL_UNARY_RUBY_ACCESSOR(floor)
|
136
136
|
DECL_UNARY_RUBY_ACCESSOR(ceil)
|
137
|
+
DECL_UNARY_RUBY_ACCESSOR(round)
|
137
138
|
DECL_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(atan2)
|
138
139
|
DECL_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(ldexp)
|
139
140
|
DECL_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(hypot)
|
@@ -154,7 +155,8 @@ static VALUE matrix_multiply_scalar(NMATRIX* left, VALUE scalar);
|
|
154
155
|
static VALUE matrix_multiply(NMATRIX* left, NMATRIX* right);
|
155
156
|
static VALUE nm_multiply(VALUE left_v, VALUE right_v);
|
156
157
|
static VALUE nm_det_exact(VALUE self);
|
157
|
-
static VALUE
|
158
|
+
static VALUE nm_inverse(VALUE self, VALUE inverse, VALUE bang);
|
159
|
+
static VALUE nm_inverse_exact(VALUE self, VALUE inverse, VALUE lda, VALUE ldb);
|
158
160
|
static VALUE nm_complex_conjugate_bang(VALUE self);
|
159
161
|
static VALUE nm_complex_conjugate(VALUE self);
|
160
162
|
static VALUE nm_reshape_bang(VALUE self, VALUE arg);
|
@@ -208,6 +210,7 @@ void Init_nmatrix() {
|
|
208
210
|
nm_eNotInvertibleError = rb_define_class("NotInvertibleError", rb_eStandardError);
|
209
211
|
|
210
212
|
/*
|
213
|
+
* :nodoc:
|
211
214
|
* Class that holds values in use by the C code.
|
212
215
|
*/
|
213
216
|
cNMatrix_GC_holder = rb_define_class("NMGCHolder", rb_cObject);
|
@@ -260,7 +263,8 @@ void Init_nmatrix() {
|
|
260
263
|
rb_define_method(cNMatrix, "supershape", (METHOD)nm_supershape, 0);
|
261
264
|
rb_define_method(cNMatrix, "offset", (METHOD)nm_offset, 0);
|
262
265
|
rb_define_method(cNMatrix, "det_exact", (METHOD)nm_det_exact, 0);
|
263
|
-
rb_define_protected_method(cNMatrix, "
|
266
|
+
rb_define_protected_method(cNMatrix, "__inverse__", (METHOD)nm_inverse, 2);
|
267
|
+
rb_define_protected_method(cNMatrix, "__inverse_exact__", (METHOD)nm_inverse_exact, 3);
|
264
268
|
rb_define_method(cNMatrix, "complex_conjugate!", (METHOD)nm_complex_conjugate_bang, 0);
|
265
269
|
rb_define_method(cNMatrix, "complex_conjugate", (METHOD)nm_complex_conjugate, 0);
|
266
270
|
rb_define_protected_method(cNMatrix, "reshape_bang", (METHOD)nm_reshape_bang, 1);
|
@@ -316,6 +320,8 @@ void Init_nmatrix() {
|
|
316
320
|
rb_define_method(cNMatrix, "-@", (METHOD)nm_unary_negate,0);
|
317
321
|
rb_define_method(cNMatrix, "floor", (METHOD)nm_unary_floor, 0);
|
318
322
|
rb_define_method(cNMatrix, "ceil", (METHOD)nm_unary_ceil, 0);
|
323
|
+
rb_define_method(cNMatrix, "round", (METHOD)nm_unary_round, 0);
|
324
|
+
|
319
325
|
|
320
326
|
rb_define_method(cNMatrix, "=~", (METHOD)nm_ew_eqeq, 1);
|
321
327
|
rb_define_method(cNMatrix, "!~", (METHOD)nm_ew_neq, 1);
|
@@ -445,11 +451,11 @@ static VALUE nm_capacity(VALUE self) {
|
|
445
451
|
break;
|
446
452
|
|
447
453
|
default:
|
448
|
-
NM_CONSERVATIVE(nm_unregister_value(self));
|
454
|
+
NM_CONSERVATIVE(nm_unregister_value(&self));
|
449
455
|
rb_raise(nm_eStorageTypeError, "unrecognized stype in nm_capacity()");
|
450
456
|
}
|
451
457
|
|
452
|
-
NM_CONSERVATIVE(nm_unregister_value(self));
|
458
|
+
NM_CONSERVATIVE(nm_unregister_value(&self));
|
453
459
|
return cap;
|
454
460
|
}
|
455
461
|
|
@@ -740,11 +746,11 @@ static VALUE nm_each_with_indices(VALUE nmatrix) {
|
|
740
746
|
to_return = nm_list_each_with_indices(nmatrix, false);
|
741
747
|
break;
|
742
748
|
default:
|
743
|
-
NM_CONSERVATIVE(nm_unregister_value(nmatrix));
|
749
|
+
NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
|
744
750
|
rb_raise(nm_eDataTypeError, "Not a proper storage type");
|
745
751
|
}
|
746
752
|
|
747
|
-
NM_CONSERVATIVE(nm_unregister_value(nmatrix));
|
753
|
+
NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
|
748
754
|
return to_return;
|
749
755
|
}
|
750
756
|
|
@@ -771,11 +777,11 @@ static VALUE nm_each_stored_with_indices(VALUE nmatrix) {
|
|
771
777
|
to_return = nm_list_each_with_indices(nmatrix, true);
|
772
778
|
break;
|
773
779
|
default:
|
774
|
-
NM_CONSERVATIVE(nm_unregister_value(nmatrix));
|
780
|
+
NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
|
775
781
|
rb_raise(nm_eDataTypeError, "Not a proper storage type");
|
776
782
|
}
|
777
783
|
|
778
|
-
NM_CONSERVATIVE(nm_unregister_value(nmatrix));
|
784
|
+
NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
|
779
785
|
return to_return;
|
780
786
|
}
|
781
787
|
|
@@ -803,11 +809,11 @@ static VALUE nm_map_stored(VALUE nmatrix) {
|
|
803
809
|
to_return = nm_list_map_stored(nmatrix, Qnil);
|
804
810
|
break;
|
805
811
|
default:
|
806
|
-
NM_CONSERVATIVE(nm_unregister_value(nmatrix));
|
812
|
+
NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
|
807
813
|
rb_raise(nm_eDataTypeError, "Not a proper storage type");
|
808
814
|
}
|
809
815
|
|
810
|
-
NM_CONSERVATIVE(nm_unregister_value(nmatrix));
|
816
|
+
NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
|
811
817
|
return to_return;
|
812
818
|
}
|
813
819
|
|
@@ -833,11 +839,11 @@ static VALUE nm_each_ordered_stored_with_indices(VALUE nmatrix) {
|
|
833
839
|
to_return = nm_list_each_with_indices(nmatrix, true);
|
834
840
|
break;
|
835
841
|
default:
|
836
|
-
NM_CONSERVATIVE(nm_unregister_value(nmatrix));
|
842
|
+
NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
|
837
843
|
rb_raise(nm_eDataTypeError, "Not a proper storage type");
|
838
844
|
}
|
839
845
|
|
840
|
-
NM_CONSERVATIVE(nm_unregister_value(nmatrix));
|
846
|
+
NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
|
841
847
|
return to_return;
|
842
848
|
}
|
843
849
|
|
@@ -932,6 +938,7 @@ DEF_UNARY_RUBY_ACCESSOR(GAMMA, gamma)
|
|
932
938
|
DEF_UNARY_RUBY_ACCESSOR(NEGATE, negate)
|
933
939
|
DEF_UNARY_RUBY_ACCESSOR(FLOOR, floor)
|
934
940
|
DEF_UNARY_RUBY_ACCESSOR(CEIL, ceil)
|
941
|
+
DEF_UNARY_RUBY_ACCESSOR(ROUND, round)
|
935
942
|
|
936
943
|
DEF_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(ATAN2, atan2)
|
937
944
|
DEF_NONCOM_ELEMENTWISE_RUBY_ACCESSOR(LDEXP, ldexp)
|
@@ -1109,24 +1116,8 @@ static VALUE nm_init_new_version(int argc, VALUE* argv, VALUE self) {
|
|
1109
1116
|
VALUE shape_ary, initial_ary, hash;
|
1110
1117
|
//VALUE shape_ary, default_val, capacity, initial_ary, dtype_sym, stype_sym;
|
1111
1118
|
// Mandatory args: shape, dtype, stype
|
1112
|
-
// FIXME: This is the one line of code standing between Ruby 1.9.2 and 1.9.3.
|
1113
|
-
#ifndef OLD_RB_SCAN_ARGS // Ruby 1.9.3 and higher
|
1114
1119
|
rb_scan_args(argc, argv, "11:", &shape_ary, &initial_ary, &hash); // &stype_sym, &dtype_sym, &default_val, &capacity);
|
1115
|
-
|
1116
|
-
if (argc == 3)
|
1117
|
-
rb_scan_args(argc, argv, "12", &shape_ary, &initial_ary, &hash);
|
1118
|
-
else if (argc == 2) {
|
1119
|
-
VALUE unknown_arg;
|
1120
|
-
rb_scan_args(argc, argv, "11", &shape_ary, &unknown_arg);
|
1121
|
-
if (!NIL_P(unknown_arg) && TYPE(unknown_arg) == T_HASH) {
|
1122
|
-
hash = unknown_arg;
|
1123
|
-
initial_ary = Qnil;
|
1124
|
-
} else {
|
1125
|
-
initial_ary = unknown_arg;
|
1126
|
-
hash = Qnil;
|
1127
|
-
}
|
1128
|
-
}
|
1129
|
-
#endif
|
1120
|
+
|
1130
1121
|
NM_CONSERVATIVE(nm_register_value(&shape_ary));
|
1131
1122
|
NM_CONSERVATIVE(nm_register_value(&initial_ary));
|
1132
1123
|
NM_CONSERVATIVE(nm_register_value(&hash));
|
@@ -2947,12 +2938,43 @@ static VALUE matrix_multiply(NMATRIX* left, NMATRIX* right) {
|
|
2947
2938
|
}
|
2948
2939
|
|
2949
2940
|
|
2941
|
+
/*
|
2942
|
+
* Calculate the inverse of a matrix with in-place Gauss-Jordan elimination.
|
2943
|
+
* Inverse will fail if the largest element in any column in zero.
|
2944
|
+
*
|
2945
|
+
* LAPACK free.
|
2946
|
+
*/
|
2947
|
+
static VALUE nm_inverse(VALUE self, VALUE inverse, VALUE bang) {
|
2948
|
+
|
2949
|
+
if (NM_STYPE(self) != nm::DENSE_STORE) {
|
2950
|
+
rb_raise(rb_eNotImpError, "needs exact determinant implementation for this matrix stype");
|
2951
|
+
return Qnil;
|
2952
|
+
}
|
2953
|
+
|
2954
|
+
if (NM_DIM(self) != 2 || NM_SHAPE0(self) != NM_SHAPE1(self)) {
|
2955
|
+
rb_raise(nm_eShapeError, "matrices must be square to have an inverse defined");
|
2956
|
+
return Qnil;
|
2957
|
+
}
|
2958
|
+
|
2959
|
+
if (bang == Qtrue) {
|
2960
|
+
nm_math_inverse(NM_SHAPE0(self), NM_STORAGE_DENSE(self)->elements,
|
2961
|
+
NM_DTYPE(self));
|
2962
|
+
|
2963
|
+
return self;
|
2964
|
+
}
|
2965
|
+
|
2966
|
+
nm_math_inverse(NM_SHAPE0(inverse), NM_STORAGE_DENSE(inverse)->elements,
|
2967
|
+
NM_DTYPE(inverse));
|
2968
|
+
|
2969
|
+
return inverse;
|
2970
|
+
}
|
2971
|
+
|
2950
2972
|
/*
|
2951
2973
|
* Calculate the exact inverse of a 2x2 or 3x3 matrix.
|
2952
2974
|
*
|
2953
2975
|
* Does not test for invertibility!
|
2954
2976
|
*/
|
2955
|
-
static VALUE nm_inverse_exact(VALUE self, VALUE inverse) {
|
2977
|
+
static VALUE nm_inverse_exact(VALUE self, VALUE inverse, VALUE lda, VALUE ldb) {
|
2956
2978
|
|
2957
2979
|
if (NM_STYPE(self) != nm::DENSE_STORE) {
|
2958
2980
|
rb_raise(rb_eNotImpError, "needs exact determinant implementation for this matrix stype");
|
@@ -2964,8 +2986,9 @@ static VALUE nm_inverse_exact(VALUE self, VALUE inverse) {
|
|
2964
2986
|
return Qnil;
|
2965
2987
|
}
|
2966
2988
|
|
2967
|
-
|
2968
|
-
|
2989
|
+
nm_math_inverse_exact(NM_SHAPE0(self),
|
2990
|
+
NM_STORAGE_DENSE(self)->elements, FIX2INT(lda),
|
2991
|
+
NM_STORAGE_DENSE(inverse)->elements, FIX2INT(ldb), NM_DTYPE(self));
|
2969
2992
|
|
2970
2993
|
return inverse;
|
2971
2994
|
}
|
@@ -549,7 +549,7 @@ static char vector_insert_resize(YALE_STORAGE* s, size_t current_size, size_t po
|
|
549
549
|
NM_FREE(s->ija);
|
550
550
|
nm_yale_storage_unregister(s);
|
551
551
|
NM_FREE(s->a);
|
552
|
-
|
552
|
+
|
553
553
|
if (s->dtype == nm::RUBYOBJ)
|
554
554
|
nm_yale_storage_unregister_a(new_a, new_capacity);
|
555
555
|
|
@@ -943,7 +943,7 @@ static VALUE map_stored(VALUE self) {
|
|
943
943
|
NM_CONSERVATIVE(nm_register_value(&self));
|
944
944
|
YALE_STORAGE* s = NM_STORAGE_YALE(self);
|
945
945
|
YaleStorage<D> y(s);
|
946
|
-
|
946
|
+
|
947
947
|
RETURN_SIZED_ENUMERATOR_PRE
|
948
948
|
NM_CONSERVATIVE(nm_unregister_value(&self));
|
949
949
|
RETURN_SIZED_ENUMERATOR(self, 0, 0, nm_yale_stored_enumerator_length);
|
@@ -1014,7 +1014,7 @@ static VALUE stored_diagonal_each_with_indices(VALUE nm) {
|
|
1014
1014
|
RETURN_SIZED_ENUMERATOR_PRE
|
1015
1015
|
NM_CONSERVATIVE(nm_unregister_value(&nm));
|
1016
1016
|
RETURN_SIZED_ENUMERATOR(nm, 0, 0, nm_yale_stored_diagonal_length); // FIXME: need diagonal length
|
1017
|
-
|
1017
|
+
|
1018
1018
|
for (typename YaleStorage<DType>::const_stored_diagonal_iterator d = y.csdbegin(); d != y.csdend(); ++d) {
|
1019
1019
|
rb_yield_values(3, ~d, d.rb_i(), d.rb_j());
|
1020
1020
|
}
|
@@ -1106,10 +1106,8 @@ static bool is_pos_default_value(YALE_STORAGE* s, size_t apos) {
|
|
1106
1106
|
return y.is_pos_default_value(apos);
|
1107
1107
|
}
|
1108
1108
|
|
1109
|
-
|
1110
1109
|
} // end of namespace nm::yale_storage
|
1111
1110
|
|
1112
|
-
|
1113
1111
|
} // end of namespace nm.
|
1114
1112
|
|
1115
1113
|
///////////////////
|
@@ -1123,7 +1121,7 @@ extern "C" {
|
|
1123
1121
|
void nm_init_yale_functions() {
|
1124
1122
|
/*
|
1125
1123
|
* This module stores methods that are useful for debugging Yale matrices,
|
1126
|
-
* i.e. the ones with +:yale+ stype.
|
1124
|
+
* i.e. the ones with +:yale+ stype.
|
1127
1125
|
*/
|
1128
1126
|
cNMatrix_YaleFunctions = rb_define_module_under(cNMatrix, "YaleFunctions");
|
1129
1127
|
|
@@ -1141,10 +1139,13 @@ void nm_init_yale_functions() {
|
|
1141
1139
|
|
1142
1140
|
rb_define_method(cNMatrix_YaleFunctions, "yale_nd_row", (METHOD)nm_nd_row, -1);
|
1143
1141
|
|
1142
|
+
/* Document-const:
|
1143
|
+
* Defines the growth rate of the sparse NMatrix's size. Default is 1.5.
|
1144
|
+
*/
|
1144
1145
|
rb_define_const(cNMatrix_YaleFunctions, "YALE_GROWTH_CONSTANT", rb_float_new(nm::yale_storage::GROWTH_CONSTANT));
|
1145
1146
|
|
1146
1147
|
// This is so the user can easily check the IType size, mostly for debugging.
|
1147
|
-
size_t itype_size
|
1148
|
+
size_t itype_size = sizeof(IType);
|
1148
1149
|
VALUE itype_dtype;
|
1149
1150
|
if (itype_size == sizeof(uint64_t)) {
|
1150
1151
|
itype_dtype = ID2SYM(rb_intern("int64"));
|
@@ -1158,12 +1159,10 @@ void nm_init_yale_functions() {
|
|
1158
1159
|
rb_define_const(cNMatrix, "INDEX_DTYPE", itype_dtype);
|
1159
1160
|
}
|
1160
1161
|
|
1161
|
-
|
1162
1162
|
/////////////////
|
1163
1163
|
// C ACCESSORS //
|
1164
1164
|
/////////////////
|
1165
1165
|
|
1166
|
-
|
1167
1166
|
/* C interface for NMatrix#each_with_indices (Yale) */
|
1168
1167
|
VALUE nm_yale_each_with_indices(VALUE nmatrix) {
|
1169
1168
|
NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::each_with_indices, VALUE, VALUE)
|
@@ -1555,7 +1554,7 @@ static bool is_pos_default_value(YALE_STORAGE* s, size_t apos) {
|
|
1555
1554
|
* Only checks the stored indices; does not care about matrix default value.
|
1556
1555
|
*/
|
1557
1556
|
static VALUE nm_row_keys_intersection(VALUE m1, VALUE ii1, VALUE m2, VALUE ii2) {
|
1558
|
-
|
1557
|
+
|
1559
1558
|
NM_CONSERVATIVE(nm_register_value(&m1));
|
1560
1559
|
NM_CONSERVATIVE(nm_register_value(&m2));
|
1561
1560
|
|
@@ -1658,7 +1657,7 @@ static VALUE nm_a(int argc, VALUE* argv, VALUE self) {
|
|
1658
1657
|
VALUE* vals = NM_ALLOCA_N(VALUE, size);
|
1659
1658
|
|
1660
1659
|
nm_register_values(vals, size);
|
1661
|
-
|
1660
|
+
|
1662
1661
|
if (NM_DTYPE(self) == nm::RUBYOBJ) {
|
1663
1662
|
for (size_t i = 0; i < size; ++i) {
|
1664
1663
|
vals[i] = reinterpret_cast<VALUE*>(s->a)[i];
|
@@ -1786,7 +1785,7 @@ static VALUE nm_ia(VALUE self) {
|
|
1786
1785
|
vals[i] = INT2FIX(s->ija[i]);
|
1787
1786
|
}
|
1788
1787
|
|
1789
|
-
NM_CONSERVATIVE(nm_unregister_value(&self));
|
1788
|
+
NM_CONSERVATIVE(nm_unregister_value(&self));
|
1790
1789
|
|
1791
1790
|
return rb_ary_new4(s->shape[0]+1, vals);
|
1792
1791
|
}
|
@@ -1887,11 +1886,10 @@ static VALUE nm_ija(int argc, VALUE* argv, VALUE self) {
|
|
1887
1886
|
static VALUE nm_nd_row(int argc, VALUE* argv, VALUE self) {
|
1888
1887
|
|
1889
1888
|
NM_CONSERVATIVE(nm_register_value(&self));
|
1890
|
-
|
1891
1889
|
if (NM_SRC(self) != NM_STORAGE(self)) {
|
1892
1890
|
NM_CONSERVATIVE(nm_unregister_value(&self));
|
1893
1891
|
rb_raise(rb_eNotImpError, "must be called on a real matrix and not a slice");
|
1894
|
-
}
|
1892
|
+
}
|
1895
1893
|
|
1896
1894
|
VALUE i_, as;
|
1897
1895
|
rb_scan_args(argc, argv, "11", &i_, &as);
|
data/lib/nmatrix.rb
CHANGED
@@ -34,6 +34,11 @@ else
|
|
34
34
|
require "nmatrix.so"
|
35
35
|
end
|
36
36
|
|
37
|
+
require 'nmatrix/io/mat_reader'
|
38
|
+
require 'nmatrix/io/mat5_reader'
|
39
|
+
require 'nmatrix/io/market'
|
40
|
+
require 'nmatrix/io/point_cloud'
|
41
|
+
|
37
42
|
require 'nmatrix/nmatrix.rb'
|
38
43
|
require 'nmatrix/version.rb'
|
39
44
|
require 'nmatrix/blas.rb'
|
data/lib/nmatrix/homogeneous.rb
CHANGED
@@ -140,4 +140,102 @@ class NMatrix
|
|
140
140
|
n
|
141
141
|
end
|
142
142
|
end
|
143
|
+
|
144
|
+
#
|
145
|
+
# call-seq:
|
146
|
+
# quaternion -> NMatrix
|
147
|
+
#
|
148
|
+
# Find the quaternion for a 3D rotation matrix.
|
149
|
+
#
|
150
|
+
# Code borrowed from: http://courses.cms.caltech.edu/cs171/quatut.pdf
|
151
|
+
#
|
152
|
+
# * *Returns* :
|
153
|
+
# - A length-4 NMatrix representing the corresponding quaternion.
|
154
|
+
#
|
155
|
+
# Examples:
|
156
|
+
#
|
157
|
+
# n.quaternion # => [1, 0, 0, 0]
|
158
|
+
#
|
159
|
+
def quaternion
|
160
|
+
raise(ShapeError, "Expected square matrix") if self.shape[0] != self.shape[1]
|
161
|
+
raise(ShapeError, "Expected 3x3 rotation (or 4x4 homogeneous) matrix") if self.shape[0] > 4 || self.shape[0] < 3
|
162
|
+
|
163
|
+
q = NMatrix.new([4], dtype: self.dtype == :float32 ? :float32: :float64)
|
164
|
+
rotation_trace = self[0,0] + self[1,1] + self[2,2]
|
165
|
+
if rotation_trace >= 0
|
166
|
+
self_w = self.shape[0] == 4 ? self[3,3] : 1.0
|
167
|
+
root_of_homogeneous_trace = Math.sqrt(rotation_trace + self_w)
|
168
|
+
q[0] = root_of_homogeneous_trace * 0.5
|
169
|
+
s = 0.5 / root_of_homogeneous_trace
|
170
|
+
q[1] = (self[2,1] - self[1,2]) * s
|
171
|
+
q[2] = (self[0,2] - self[2,0]) * s
|
172
|
+
q[3] = (self[1,0] - self[0,1]) * s
|
173
|
+
else
|
174
|
+
h = 0
|
175
|
+
h = 1 if self[1,1] > self[0,0]
|
176
|
+
h = 2 if self[2,2] > self[h,h]
|
177
|
+
|
178
|
+
case_macro = Proc.new do |i,j,k,ii,jj,kk|
|
179
|
+
qq = NMatrix.new([4], dtype: :float64)
|
180
|
+
self_w = self.shape[0] == 4 ? self[3,3] : 1.0
|
181
|
+
s = Math.sqrt( (self[ii,ii] - (self[jj,jj] + self[kk,kk])) + self_w)
|
182
|
+
qq[i] = s*0.5
|
183
|
+
s = 0.5 / s
|
184
|
+
qq[j] = (self[ii,jj] + self[jj,ii]) * s
|
185
|
+
qq[k] = (self[kk,ii] + self[ii,kk]) * s
|
186
|
+
qq[0] = (self[kk,jj] - self[jj,kk]) * s
|
187
|
+
qq
|
188
|
+
end
|
189
|
+
|
190
|
+
case h
|
191
|
+
when 0
|
192
|
+
q = case_macro.call(1,2,3, 0,1,2)
|
193
|
+
when 1
|
194
|
+
q = case_macro.call(2,3,1, 1,2,0)
|
195
|
+
when 2
|
196
|
+
q = case_macro.call(3,1,2, 2,0,1)
|
197
|
+
end
|
198
|
+
|
199
|
+
self_w = self.shape[0] == 4 ? self[3,3] : 1.0
|
200
|
+
if self_w != 1
|
201
|
+
s = 1.0 / Math.sqrt(self_w)
|
202
|
+
q[0] *= s
|
203
|
+
q[1] *= s
|
204
|
+
q[2] *= s
|
205
|
+
q[3] *= s
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
q
|
210
|
+
end
|
211
|
+
|
212
|
+
#
|
213
|
+
# call-seq:
|
214
|
+
# angle_vector -> [angle, about_vector]
|
215
|
+
#
|
216
|
+
# Find the angle vector for a quaternion. Assumes the quaternion has unit length.
|
217
|
+
#
|
218
|
+
# Source: http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/
|
219
|
+
#
|
220
|
+
# * *Returns* :
|
221
|
+
# - An angle (in radians) describing the rotation about the +about_vector+.
|
222
|
+
# - A length-3 NMatrix representing the corresponding quaternion.
|
223
|
+
#
|
224
|
+
# Examples:
|
225
|
+
#
|
226
|
+
# q.angle_vector # => [1, 0, 0, 0]
|
227
|
+
#
|
228
|
+
def angle_vector
|
229
|
+
raise(ShapeError, "Expected length-4 vector or matrix (quaternion)") if self.shape[0] != 4
|
230
|
+
raise("Expected unit quaternion") if self[0] > 1
|
231
|
+
|
232
|
+
xyz = NMatrix.new([3], dtype: self.dtype)
|
233
|
+
|
234
|
+
angle = 2 * Math.acos(self[0])
|
235
|
+
s = Math.sqrt(1.0 - self[0]*self[0])
|
236
|
+
|
237
|
+
xyz[0..2] = self[1..3]
|
238
|
+
xyz /= s if s >= 0.001 # avoid divide by zero
|
239
|
+
return [angle, xyz]
|
240
|
+
end
|
143
241
|
end
|