nmatrix 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/Gemfile +5 -0
- data/History.txt +97 -0
- data/Manifest.txt +34 -7
- data/README.rdoc +13 -13
- data/Rakefile +36 -26
- data/ext/nmatrix/data/data.cpp +15 -2
- data/ext/nmatrix/data/data.h +4 -0
- data/ext/nmatrix/data/ruby_object.h +5 -14
- data/ext/nmatrix/extconf.rb +3 -2
- data/ext/nmatrix/{util/math.cpp → math.cpp} +296 -6
- data/ext/nmatrix/math/asum.h +143 -0
- data/ext/nmatrix/math/geev.h +82 -0
- data/ext/nmatrix/math/gemm.h +267 -0
- data/ext/nmatrix/math/gemv.h +208 -0
- data/ext/nmatrix/math/ger.h +96 -0
- data/ext/nmatrix/math/gesdd.h +80 -0
- data/ext/nmatrix/math/gesvd.h +78 -0
- data/ext/nmatrix/math/getf2.h +86 -0
- data/ext/nmatrix/math/getrf.h +240 -0
- data/ext/nmatrix/math/getri.h +107 -0
- data/ext/nmatrix/math/getrs.h +125 -0
- data/ext/nmatrix/math/idamax.h +86 -0
- data/ext/nmatrix/{util → math}/lapack.h +60 -356
- data/ext/nmatrix/math/laswp.h +165 -0
- data/ext/nmatrix/math/long_dtype.h +52 -0
- data/ext/nmatrix/math/math.h +1154 -0
- data/ext/nmatrix/math/nrm2.h +181 -0
- data/ext/nmatrix/math/potrs.h +125 -0
- data/ext/nmatrix/math/rot.h +141 -0
- data/ext/nmatrix/math/rotg.h +115 -0
- data/ext/nmatrix/math/scal.h +73 -0
- data/ext/nmatrix/math/swap.h +73 -0
- data/ext/nmatrix/math/trsm.h +383 -0
- data/ext/nmatrix/nmatrix.cpp +176 -152
- data/ext/nmatrix/nmatrix.h +1 -2
- data/ext/nmatrix/ruby_constants.cpp +9 -4
- data/ext/nmatrix/ruby_constants.h +1 -0
- data/ext/nmatrix/storage/dense.cpp +57 -41
- data/ext/nmatrix/storage/list.cpp +52 -50
- data/ext/nmatrix/storage/storage.cpp +59 -43
- data/ext/nmatrix/storage/yale.cpp +352 -333
- data/ext/nmatrix/storage/yale.h +4 -0
- data/lib/nmatrix.rb +2 -2
- data/lib/nmatrix/blas.rb +4 -4
- data/lib/nmatrix/enumerate.rb +241 -0
- data/lib/nmatrix/lapack.rb +54 -1
- data/lib/nmatrix/math.rb +462 -0
- data/lib/nmatrix/nmatrix.rb +210 -486
- data/lib/nmatrix/nvector.rb +0 -62
- data/lib/nmatrix/rspec.rb +75 -0
- data/lib/nmatrix/shortcuts.rb +136 -108
- data/lib/nmatrix/version.rb +1 -1
- data/spec/blas_spec.rb +20 -12
- data/spec/elementwise_spec.rb +22 -13
- data/spec/io_spec.rb +1 -0
- data/spec/lapack_spec.rb +197 -0
- data/spec/nmatrix_spec.rb +39 -38
- data/spec/nvector_spec.rb +3 -9
- data/spec/rspec_monkeys.rb +29 -0
- data/spec/rspec_spec.rb +34 -0
- data/spec/shortcuts_spec.rb +14 -16
- data/spec/slice_spec.rb +242 -186
- data/spec/spec_helper.rb +19 -0
- metadata +33 -5
- data/ext/nmatrix/util/math.h +0 -2612
data/ext/nmatrix/nmatrix.cpp
CHANGED
@@ -47,7 +47,7 @@ extern "C" {
|
|
47
47
|
|
48
48
|
#include "types.h"
|
49
49
|
#include "data/data.h"
|
50
|
-
#include "
|
50
|
+
#include "math/math.h"
|
51
51
|
#include "util/io.h"
|
52
52
|
#include "storage/storage.h"
|
53
53
|
#include "storage/list.h"
|
@@ -340,13 +340,17 @@ static VALUE nm_dtype(VALUE self);
|
|
340
340
|
static VALUE nm_itype(VALUE self);
|
341
341
|
static VALUE nm_stype(VALUE self);
|
342
342
|
static VALUE nm_default_value(VALUE self);
|
343
|
+
static size_t effective_dim(STORAGE* s);
|
344
|
+
static VALUE nm_effective_dim(VALUE self);
|
343
345
|
static VALUE nm_dim(VALUE self);
|
346
|
+
static VALUE nm_offset(VALUE self);
|
344
347
|
static VALUE nm_shape(VALUE self);
|
348
|
+
static VALUE nm_supershape(int argc, VALUE* argv, VALUE self);
|
345
349
|
static VALUE nm_capacity(VALUE self);
|
346
350
|
static VALUE nm_each_with_indices(VALUE nmatrix);
|
347
351
|
static VALUE nm_each_stored_with_indices(VALUE nmatrix);
|
348
352
|
|
349
|
-
static SLICE* get_slice(size_t dim, VALUE*
|
353
|
+
static SLICE* get_slice(size_t dim, int argc, VALUE* arg, size_t* shape);
|
350
354
|
static VALUE nm_xslice(int argc, VALUE* argv, void* (*slice_func)(STORAGE*, SLICE*), void (*delete_func)(NMATRIX*), VALUE self);
|
351
355
|
static VALUE nm_mset(int argc, VALUE* argv, VALUE self);
|
352
356
|
static VALUE nm_mget(int argc, VALUE* argv, VALUE self);
|
@@ -396,7 +400,6 @@ static VALUE nm_eqeq(VALUE left, VALUE right);
|
|
396
400
|
static VALUE matrix_multiply_scalar(NMATRIX* left, VALUE scalar);
|
397
401
|
static VALUE matrix_multiply(NMATRIX* left, NMATRIX* right);
|
398
402
|
static VALUE nm_multiply(VALUE left_v, VALUE right_v);
|
399
|
-
static VALUE nm_factorize_lu(VALUE self);
|
400
403
|
static VALUE nm_det_exact(VALUE self);
|
401
404
|
static VALUE nm_complex_conjugate_bang(VALUE self);
|
402
405
|
|
@@ -419,12 +422,14 @@ static double get_time(void);
|
|
419
422
|
///////////////////
|
420
423
|
|
421
424
|
void Init_nmatrix() {
|
425
|
+
|
426
|
+
|
422
427
|
///////////////////////
|
423
428
|
// Class Definitions //
|
424
429
|
///////////////////////
|
425
430
|
|
426
431
|
cNMatrix = rb_define_class("NMatrix", rb_cObject);
|
427
|
-
cNVector = rb_define_class("NVector", cNMatrix);
|
432
|
+
//cNVector = rb_define_class("NVector", cNMatrix);
|
428
433
|
|
429
434
|
// Special exceptions
|
430
435
|
|
@@ -479,12 +484,14 @@ void Init_nmatrix() {
|
|
479
484
|
rb_define_method(cNMatrix, "[]=", (METHOD)nm_mset, -1);
|
480
485
|
rb_define_method(cNMatrix, "is_ref?", (METHOD)nm_is_ref, 0);
|
481
486
|
rb_define_method(cNMatrix, "dimensions", (METHOD)nm_dim, 0);
|
487
|
+
rb_define_method(cNMatrix, "effective_dimensions", (METHOD)nm_effective_dim, 0);
|
482
488
|
|
483
489
|
rb_define_protected_method(cNMatrix, "__list_to_hash__", (METHOD)nm_to_hash, 0); // handles list and dense, which are n-dimensional
|
484
490
|
|
485
491
|
rb_define_method(cNMatrix, "shape", (METHOD)nm_shape, 0);
|
492
|
+
rb_define_method(cNMatrix, "supershape", (METHOD)nm_supershape, -1);
|
493
|
+
rb_define_method(cNMatrix, "offset", (METHOD)nm_offset, 0);
|
486
494
|
rb_define_method(cNMatrix, "det_exact", (METHOD)nm_det_exact, 0);
|
487
|
-
//rb_define_method(cNMatrix, "transpose!", (METHOD)nm_transpose_self, 0);
|
488
495
|
rb_define_method(cNMatrix, "complex_conjugate!", (METHOD)nm_complex_conjugate_bang, 0);
|
489
496
|
|
490
497
|
rb_define_protected_method(cNMatrix, "__dense_each__", (METHOD)nm_dense_each, 0);
|
@@ -521,8 +528,6 @@ void Init_nmatrix() {
|
|
521
528
|
// Matrix Math Methods //
|
522
529
|
/////////////////////////
|
523
530
|
rb_define_method(cNMatrix, "dot", (METHOD)nm_multiply, 1);
|
524
|
-
rb_define_method(cNMatrix, "factorize_lu", (METHOD)nm_factorize_lu, 0);
|
525
|
-
|
526
531
|
|
527
532
|
rb_define_method(cNMatrix, "symmetric?", (METHOD)nm_symmetric, 0);
|
528
533
|
rb_define_method(cNMatrix, "hermitian?", (METHOD)nm_hermitian, 0);
|
@@ -534,6 +539,7 @@ void Init_nmatrix() {
|
|
534
539
|
/////////////
|
535
540
|
|
536
541
|
rb_define_alias(cNMatrix, "dim", "dimensions");
|
542
|
+
rb_define_alias(cNMatrix, "effective_dim", "effective_dimensions");
|
537
543
|
rb_define_alias(cNMatrix, "equal?", "eql?");
|
538
544
|
|
539
545
|
///////////////////////
|
@@ -558,6 +564,11 @@ void Init_nmatrix() {
|
|
558
564
|
// IO module //
|
559
565
|
///////////////
|
560
566
|
nm_init_io();
|
567
|
+
|
568
|
+
/////////////////////////////////////////////////
|
569
|
+
// Force compilation of necessary constructors //
|
570
|
+
/////////////////////////////////////////////////
|
571
|
+
nm_init_data();
|
561
572
|
}
|
562
573
|
|
563
574
|
|
@@ -581,9 +592,9 @@ static SLICE* alloc_slice(size_t dim) {
|
|
581
592
|
* Slice destructor.
|
582
593
|
*/
|
583
594
|
static void free_slice(SLICE* slice) {
|
584
|
-
|
585
|
-
|
586
|
-
|
595
|
+
xfree(slice->coords);
|
596
|
+
xfree(slice->lengths);
|
597
|
+
xfree(slice);
|
587
598
|
}
|
588
599
|
|
589
600
|
|
@@ -605,13 +616,16 @@ static VALUE nm_alloc(VALUE klass) {
|
|
605
616
|
* Find the capacity of an NMatrix. The capacity only differs from the size for
|
606
617
|
* Yale matrices, which occasionally allocate more space than they need. For
|
607
618
|
* list and dense, capacity gives the number of elements in the matrix.
|
619
|
+
*
|
620
|
+
* If you call this on a slice, it may behave unpredictably. Most likely it'll
|
621
|
+
* just return the original matrix's capacity.
|
608
622
|
*/
|
609
623
|
static VALUE nm_capacity(VALUE self) {
|
610
624
|
VALUE cap;
|
611
625
|
|
612
626
|
switch(NM_STYPE(self)) {
|
613
627
|
case nm::YALE_STORE:
|
614
|
-
cap = UINT2NUM(
|
628
|
+
cap = UINT2NUM(reinterpret_cast<YALE_STORAGE*>(NM_STORAGE_YALE(self)->src)->capacity);
|
615
629
|
break;
|
616
630
|
|
617
631
|
case nm::DENSE_STORE:
|
@@ -640,7 +654,7 @@ void nm_delete(NMATRIX* mat) {
|
|
640
654
|
};
|
641
655
|
ttable[mat->stype](mat->storage);
|
642
656
|
|
643
|
-
|
657
|
+
xfree(mat);
|
644
658
|
}
|
645
659
|
|
646
660
|
/*
|
@@ -650,11 +664,11 @@ void nm_delete_ref(NMATRIX* mat) {
|
|
650
664
|
static void (*ttable[nm::NUM_STYPES])(STORAGE*) = {
|
651
665
|
nm_dense_storage_delete_ref,
|
652
666
|
nm_list_storage_delete_ref,
|
653
|
-
|
667
|
+
nm_yale_storage_delete_ref
|
654
668
|
};
|
655
669
|
ttable[mat->stype](mat->storage);
|
656
670
|
|
657
|
-
|
671
|
+
xfree(mat);
|
658
672
|
}
|
659
673
|
|
660
674
|
/*
|
@@ -844,9 +858,11 @@ static VALUE nm_hermitian(VALUE self) {
|
|
844
858
|
return is_symmetric(self, true);
|
845
859
|
}
|
846
860
|
|
861
|
+
|
862
|
+
|
847
863
|
/*
|
848
864
|
* call-seq:
|
849
|
-
*
|
865
|
+
* complex_conjugate -> NMatrix
|
850
866
|
*
|
851
867
|
* Transform the matrix (in-place) to its complex conjugate. Only works on complex matrices.
|
852
868
|
*
|
@@ -1381,8 +1397,6 @@ static VALUE nm_read(int argc, VALUE* argv, VALUE self) {
|
|
1381
1397
|
size_t* shape = ALLOC_N(size_t, dim);
|
1382
1398
|
read_padded_shape(f, dim, shape, itype);
|
1383
1399
|
|
1384
|
-
VALUE klass = dim == 1 ? cNVector : cNMatrix;
|
1385
|
-
|
1386
1400
|
STORAGE* s;
|
1387
1401
|
if (stype == nm::DENSE_STORE) {
|
1388
1402
|
s = nm_dense_storage_create(dtype, shape, dim, NULL, 0);
|
@@ -1406,11 +1420,13 @@ static VALUE nm_read(int argc, VALUE* argv, VALUE self) {
|
|
1406
1420
|
NMATRIX* nm = nm_create(stype, s);
|
1407
1421
|
|
1408
1422
|
// Return the appropriate matrix object (Ruby VALUE)
|
1423
|
+
// FIXME: This should probably return CLASS_OF(self) instead of cNMatrix, but I don't know how that works for
|
1424
|
+
// FIXME: class methods.
|
1409
1425
|
switch(stype) {
|
1410
1426
|
case nm::DENSE_STORE:
|
1411
|
-
return Data_Wrap_Struct(
|
1427
|
+
return Data_Wrap_Struct(cNMatrix, nm_dense_storage_mark, nm_delete, nm);
|
1412
1428
|
case nm::YALE_STORE:
|
1413
|
-
return Data_Wrap_Struct(
|
1429
|
+
return Data_Wrap_Struct(cNMatrix, nm_yale_storage_mark, nm_delete, nm);
|
1414
1430
|
default:
|
1415
1431
|
return Qnil;
|
1416
1432
|
}
|
@@ -1447,16 +1463,8 @@ static VALUE nm_init_yale_from_old_yale(VALUE shape, VALUE dtype, VALUE ia, VALU
|
|
1447
1463
|
* Check to determine whether matrix is a reference to another matrix.
|
1448
1464
|
*/
|
1449
1465
|
static VALUE nm_is_ref(VALUE self) {
|
1450
|
-
|
1451
|
-
|
1452
|
-
return (NM_DENSE_SRC(self) == NM_STORAGE(self)) ? Qfalse : Qtrue;
|
1453
|
-
}
|
1454
|
-
|
1455
|
-
if (NM_STYPE(self) == nm::LIST_STORE) {
|
1456
|
-
return (NM_LIST_SRC(self) == NM_STORAGE(self)) ? Qfalse : Qtrue;
|
1457
|
-
}
|
1458
|
-
|
1459
|
-
return Qfalse;
|
1466
|
+
if (NM_SRC(self) == NM_STORAGE(self)) return Qfalse;
|
1467
|
+
else return Qtrue;
|
1460
1468
|
}
|
1461
1469
|
|
1462
1470
|
/*
|
@@ -1475,7 +1483,6 @@ static VALUE nm_mget(int argc, VALUE* argv, VALUE self) {
|
|
1475
1483
|
nm_list_storage_get,
|
1476
1484
|
nm_yale_storage_get
|
1477
1485
|
};
|
1478
|
-
|
1479
1486
|
return nm_xslice(argc, argv, ttable[NM_STYPE(self)], nm_delete, self);
|
1480
1487
|
}
|
1481
1488
|
|
@@ -1508,30 +1515,28 @@ static VALUE nm_mref(int argc, VALUE* argv, VALUE self) {
|
|
1508
1515
|
* n[3,3] = n[2,3] = 5.0
|
1509
1516
|
*/
|
1510
1517
|
static VALUE nm_mset(int argc, VALUE* argv, VALUE self) {
|
1511
|
-
size_t dim =
|
1518
|
+
size_t dim = NM_DIM(self); // last arg is the value
|
1512
1519
|
|
1513
|
-
if (argc
|
1514
|
-
rb_raise(rb_eArgError, "
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
SLICE* slice = get_slice(dim, argv, self);
|
1520
|
+
if ((size_t)(argc) > NM_DIM(self)+1) {
|
1521
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for %u)", argc, effective_dim(NM_STORAGE(self))+1);
|
1522
|
+
} else {
|
1523
|
+
SLICE* slice = get_slice(dim, argc-1, argv, NM_STORAGE(self)->shape);
|
1519
1524
|
|
1520
|
-
void* value = rubyobj_to_cval(argv[
|
1525
|
+
void* value = rubyobj_to_cval(argv[argc-1], NM_DTYPE(self));
|
1521
1526
|
|
1522
1527
|
// FIXME: Can't use a function pointer table here currently because these functions have different
|
1523
1528
|
// signatures (namely the return type).
|
1524
1529
|
switch(NM_STYPE(self)) {
|
1525
1530
|
case nm::DENSE_STORE:
|
1526
1531
|
nm_dense_storage_set(NM_STORAGE(self), slice, value);
|
1527
|
-
|
1532
|
+
xfree(value);
|
1528
1533
|
break;
|
1529
1534
|
case nm::LIST_STORE:
|
1530
1535
|
// Remove if it's a zero, insert otherwise
|
1531
1536
|
if (!std::memcmp(value, NM_STORAGE_LIST(self)->default_val, DTYPE_SIZES[NM_DTYPE(self)])) {
|
1532
|
-
|
1537
|
+
xfree(value);
|
1533
1538
|
value = nm_list_storage_remove(NM_STORAGE(self), slice);
|
1534
|
-
|
1539
|
+
xfree(value);
|
1535
1540
|
} else {
|
1536
1541
|
nm_list_storage_insert(NM_STORAGE(self), slice, value);
|
1537
1542
|
// no need to free value here since it was inserted directly into the list.
|
@@ -1539,17 +1544,12 @@ static VALUE nm_mset(int argc, VALUE* argv, VALUE self) {
|
|
1539
1544
|
break;
|
1540
1545
|
case nm::YALE_STORE:
|
1541
1546
|
nm_yale_storage_set(NM_STORAGE(self), slice, value);
|
1542
|
-
|
1547
|
+
xfree(value);
|
1543
1548
|
break;
|
1544
1549
|
}
|
1545
1550
|
free_slice(slice);
|
1546
1551
|
|
1547
|
-
return argv[
|
1548
|
-
|
1549
|
-
} else if (NM_DIM(self) < dim) {
|
1550
|
-
rb_raise(rb_eArgError, "Coordinates given exceed number of matrix dimensions");
|
1551
|
-
} else {
|
1552
|
-
rb_raise(rb_eNotImpError, "Slicing not supported yet");
|
1552
|
+
return argv[argc-1];
|
1553
1553
|
}
|
1554
1554
|
return Qnil;
|
1555
1555
|
}
|
@@ -1564,19 +1564,15 @@ static VALUE nm_mset(int argc, VALUE* argv, VALUE self) {
|
|
1564
1564
|
static VALUE nm_multiply(VALUE left_v, VALUE right_v) {
|
1565
1565
|
NMATRIX *left, *right;
|
1566
1566
|
|
1567
|
-
// left has to be of type NMatrix.
|
1568
|
-
CheckNMatrixType(left_v);
|
1569
|
-
|
1570
1567
|
UnwrapNMatrix( left_v, left );
|
1571
1568
|
|
1572
1569
|
if (NM_RUBYVAL_IS_NUMERIC(right_v))
|
1573
1570
|
return matrix_multiply_scalar(left, right_v);
|
1574
1571
|
|
1575
1572
|
else if (TYPE(right_v) == T_ARRAY)
|
1576
|
-
rb_raise(rb_eNotImpError, "
|
1573
|
+
rb_raise(rb_eNotImpError, "please convert array to nx1 or 1xn NMatrix first");
|
1577
1574
|
|
1578
|
-
//
|
1579
|
-
else { // both are matrices
|
1575
|
+
else { // both are matrices (probably)
|
1580
1576
|
CheckNMatrixType(right_v);
|
1581
1577
|
UnwrapNMatrix( right_v, right );
|
1582
1578
|
|
@@ -1593,50 +1589,6 @@ static VALUE nm_multiply(VALUE left_v, VALUE right_v) {
|
|
1593
1589
|
return Qnil;
|
1594
1590
|
}
|
1595
1591
|
|
1596
|
-
/*
|
1597
|
-
* call-seq:
|
1598
|
-
* matrix.factorize_lu -> ...
|
1599
|
-
*
|
1600
|
-
* LU factorization of a matrix.
|
1601
|
-
*
|
1602
|
-
* FIXME: For some reason, getrf seems to require that the matrix be transposed first -- and then you have to transpose the
|
1603
|
-
* FIXME: result again. Ideally, this would be an in-place factorize instead, and would be called nm_factorize_lu_bang.
|
1604
|
-
*/
|
1605
|
-
static VALUE nm_factorize_lu(VALUE self) {
|
1606
|
-
if (NM_STYPE(self) != nm::DENSE_STORE) {
|
1607
|
-
rb_raise(rb_eNotImpError, "only implemented for dense storage");
|
1608
|
-
}
|
1609
|
-
|
1610
|
-
if (NM_DIM(self) != 2) {
|
1611
|
-
rb_raise(rb_eNotImpError, "matrix is not 2-dimensional");
|
1612
|
-
}
|
1613
|
-
|
1614
|
-
VALUE copy = nm_init_transposed(self);
|
1615
|
-
|
1616
|
-
static int (*ttable[nm::NUM_DTYPES])(const enum CBLAS_ORDER, const int m, const int n, void* a, const int lda, int* ipiv) = {
|
1617
|
-
NULL, NULL, NULL, NULL, NULL, // integers not allowed due to division
|
1618
|
-
nm::math::clapack_getrf<float>,
|
1619
|
-
nm::math::clapack_getrf<double>,
|
1620
|
-
#ifdef HAVE_CLAPACK_H
|
1621
|
-
clapack_cgetrf, clapack_zgetrf, // call directly, same function signature!
|
1622
|
-
#else
|
1623
|
-
nm::math::clapack_getrf<nm::Complex64>,
|
1624
|
-
nm::math::clapack_getrf<nm::Complex128>,
|
1625
|
-
#endif
|
1626
|
-
nm::math::clapack_getrf<nm::Rational32>,
|
1627
|
-
nm::math::clapack_getrf<nm::Rational64>,
|
1628
|
-
nm::math::clapack_getrf<nm::Rational128>,
|
1629
|
-
nm::math::clapack_getrf<nm::RubyObject>
|
1630
|
-
};
|
1631
|
-
|
1632
|
-
int* ipiv = ALLOCA_N(int, std::min(NM_SHAPE0(copy), NM_SHAPE1(copy)));
|
1633
|
-
|
1634
|
-
// In-place factorize
|
1635
|
-
ttable[NM_DTYPE(copy)](CblasRowMajor, NM_SHAPE0(copy), NM_SHAPE1(copy), NM_STORAGE_DENSE(copy)->elements, NM_SHAPE1(copy), ipiv);
|
1636
|
-
|
1637
|
-
// Transpose the result
|
1638
|
-
return nm_init_transposed(copy);
|
1639
|
-
}
|
1640
1592
|
|
1641
1593
|
/*
|
1642
1594
|
* call-seq:
|
@@ -1647,8 +1599,7 @@ static VALUE nm_factorize_lu(VALUE self) {
|
|
1647
1599
|
* In other words, if you set your matrix to be 3x4, the dim is 2. If the
|
1648
1600
|
* matrix was initialized as 3x4x3, the dim is 3.
|
1649
1601
|
*
|
1650
|
-
*
|
1651
|
-
* dim 2 (and have an orientation), but act as if they're dim 1.
|
1602
|
+
* Use #effective_dim to get the dimension of an NMatrix which acts as a vector (e.g., a column or row).
|
1652
1603
|
*/
|
1653
1604
|
static VALUE nm_dim(VALUE self) {
|
1654
1605
|
return INT2FIX(NM_STORAGE(self)->dim);
|
@@ -1662,11 +1613,57 @@ static VALUE nm_dim(VALUE self) {
|
|
1662
1613
|
*/
|
1663
1614
|
static VALUE nm_shape(VALUE self) {
|
1664
1615
|
STORAGE* s = NM_STORAGE(self);
|
1665
|
-
size_t index;
|
1666
1616
|
|
1667
1617
|
// Copy elements into a VALUE array and then use those to create a Ruby array with rb_ary_new4.
|
1668
1618
|
VALUE* shape = ALLOCA_N(VALUE, s->dim);
|
1669
|
-
for (index = 0; index < s->dim; ++index)
|
1619
|
+
for (size_t index = 0; index < s->dim; ++index)
|
1620
|
+
shape[index] = INT2FIX(s->shape[index]);
|
1621
|
+
|
1622
|
+
return rb_ary_new4(s->dim, shape);
|
1623
|
+
}
|
1624
|
+
|
1625
|
+
|
1626
|
+
/*
|
1627
|
+
* call-seq:
|
1628
|
+
* offset -> Array
|
1629
|
+
*
|
1630
|
+
* Get the offset (slice position) of a matrix. Typically all zeros, unless you have a reference slice.
|
1631
|
+
*/
|
1632
|
+
static VALUE nm_offset(VALUE self) {
|
1633
|
+
STORAGE* s = NM_STORAGE(self);
|
1634
|
+
|
1635
|
+
// Copy elements into a VALUE array and then use those to create a Ruby array with rb_ary_new4.
|
1636
|
+
VALUE* offset = ALLOCA_N(VALUE, s->dim);
|
1637
|
+
for (size_t index = 0; index < s->dim; ++index)
|
1638
|
+
offset[index] = INT2FIX(s->offset[index]);
|
1639
|
+
|
1640
|
+
return rb_ary_new4(s->dim, offset);
|
1641
|
+
}
|
1642
|
+
|
1643
|
+
|
1644
|
+
/*
|
1645
|
+
* call-seq:
|
1646
|
+
* supershape(n) -> Array
|
1647
|
+
* supershape -> Array
|
1648
|
+
*
|
1649
|
+
* Get the shape of a slice's nth-order parent. If the slice doesn't have n orders, returns the shape
|
1650
|
+
* of the original ancestor.
|
1651
|
+
*/
|
1652
|
+
static VALUE nm_supershape(int argc, VALUE* argv, VALUE self) {
|
1653
|
+
VALUE n; rb_scan_args(argc, argv, "01", &n);
|
1654
|
+
|
1655
|
+
STORAGE* s = NM_STORAGE(self);
|
1656
|
+
if (s->src == s) return nm_shape(self); // easy case (not a slice)
|
1657
|
+
int order = n == Qnil ? 1 : FIX2INT(n);
|
1658
|
+
|
1659
|
+
if (order <= 0) rb_raise(rb_eRangeError, "expected argument to be positive");
|
1660
|
+
|
1661
|
+
for (; order > 0; --order) {
|
1662
|
+
s = s->src; // proceed to next parent
|
1663
|
+
}
|
1664
|
+
|
1665
|
+
VALUE* shape = ALLOCA_N(VALUE, s->dim);
|
1666
|
+
for (size_t index = 0; index < s->dim; ++index)
|
1670
1667
|
shape[index] = INT2FIX(s->shape[index]);
|
1671
1668
|
|
1672
1669
|
return rb_ary_new4(s->dim, shape);
|
@@ -1693,14 +1690,41 @@ static VALUE nm_symmetric(VALUE self) {
|
|
1693
1690
|
return is_symmetric(self, false);
|
1694
1691
|
}
|
1695
1692
|
|
1693
|
+
|
1694
|
+
/*
|
1695
|
+
* Gets the dimension of a matrix which might be a vector (have one or more shape components of size 1).
|
1696
|
+
*/
|
1697
|
+
static size_t effective_dim(STORAGE* s) {
|
1698
|
+
size_t d = 0;
|
1699
|
+
for (size_t i = 0; i < s->dim; ++i) {
|
1700
|
+
if (s->shape[i] != 1) d++;
|
1701
|
+
}
|
1702
|
+
return d;
|
1703
|
+
}
|
1704
|
+
|
1705
|
+
|
1706
|
+
/*
|
1707
|
+
* call-seq:
|
1708
|
+
* effective_dim -> Fixnum
|
1709
|
+
*
|
1710
|
+
* Returns the number of dimensions that don't have length 1. Guaranteed to be less than or equal to #dim.
|
1711
|
+
*/
|
1712
|
+
static VALUE nm_effective_dim(VALUE self) {
|
1713
|
+
return INT2FIX(effective_dim(NM_STORAGE(self)));
|
1714
|
+
}
|
1715
|
+
|
1716
|
+
|
1696
1717
|
/*
|
1697
1718
|
* Get a slice of an NMatrix.
|
1698
1719
|
*/
|
1699
1720
|
static VALUE nm_xslice(int argc, VALUE* argv, void* (*slice_func)(STORAGE*, SLICE*), void (*delete_func)(NMATRIX*), VALUE self) {
|
1700
1721
|
VALUE result = Qnil;
|
1722
|
+
STORAGE* s = NM_STORAGE(self);
|
1701
1723
|
|
1702
|
-
if (NM_DIM(self)
|
1703
|
-
|
1724
|
+
if (NM_DIM(self) < (size_t)(argc)) {
|
1725
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for %u)", argc, effective_dim(s));
|
1726
|
+
} else {
|
1727
|
+
SLICE* slice = get_slice(NM_DIM(self), argc, argv, s->shape);
|
1704
1728
|
|
1705
1729
|
if (slice->single) {
|
1706
1730
|
static void* (*ttable[nm::NUM_STYPES])(STORAGE*, SLICE*) = {
|
@@ -1709,30 +1733,20 @@ static VALUE nm_xslice(int argc, VALUE* argv, void* (*slice_func)(STORAGE*, SLIC
|
|
1709
1733
|
nm_yale_storage_ref
|
1710
1734
|
};
|
1711
1735
|
|
1712
|
-
if (NM_DTYPE(self) == nm::RUBYOBJ) result = *reinterpret_cast<VALUE*>( ttable[NM_STYPE(self)](
|
1713
|
-
else result = rubyobj_from_cval( ttable[NM_STYPE(self)](
|
1736
|
+
if (NM_DTYPE(self) == nm::RUBYOBJ) result = *reinterpret_cast<VALUE*>( ttable[NM_STYPE(self)](s, slice) );
|
1737
|
+
else result = rubyobj_from_cval( ttable[NM_STYPE(self)](s, slice), NM_DTYPE(self) ).rval;
|
1714
1738
|
|
1715
1739
|
} else {
|
1716
1740
|
STYPE_MARK_TABLE(mark_table);
|
1717
1741
|
|
1718
|
-
NMATRIX* mat
|
1719
|
-
mat->stype
|
1720
|
-
mat->storage
|
1721
|
-
|
1722
|
-
// Do we want an NVector instead of an NMatrix?
|
1723
|
-
VALUE klass = cNMatrix, orient = Qnil;
|
1724
|
-
// FIXME: Generalize for n dimensional slicing somehow
|
1725
|
-
if (mat->storage->shape[0] == 1 || mat->storage->shape[1] == 1) klass = cNVector;
|
1742
|
+
NMATRIX* mat = ALLOC(NMATRIX);
|
1743
|
+
mat->stype = NM_STYPE(self);
|
1744
|
+
mat->storage = (STORAGE*)((*slice_func)( s, slice ));
|
1726
1745
|
|
1727
|
-
result
|
1746
|
+
result = Data_Wrap_Struct(CLASS_OF(self), mark_table[mat->stype], delete_func, mat);
|
1728
1747
|
}
|
1729
1748
|
|
1730
1749
|
free_slice(slice);
|
1731
|
-
|
1732
|
-
} else if (NM_DIM(self) < (size_t)(argc)) {
|
1733
|
-
rb_raise(rb_eArgError, "Coordinates given exceed number of matrix dimensions");
|
1734
|
-
} else {
|
1735
|
-
rb_raise(rb_eNotImpError, "This type of slicing not supported yet");
|
1736
1750
|
}
|
1737
1751
|
|
1738
1752
|
return result;
|
@@ -1814,12 +1828,7 @@ static VALUE elementwise_op(nm::ewop_t op, VALUE left_val, VALUE right_val) {
|
|
1814
1828
|
* Check to determine whether matrix is a reference to another matrix.
|
1815
1829
|
*/
|
1816
1830
|
bool is_ref(const NMATRIX* matrix) {
|
1817
|
-
|
1818
|
-
if (matrix->stype != nm::DENSE_STORE) {
|
1819
|
-
return false;
|
1820
|
-
}
|
1821
|
-
|
1822
|
-
return ((DENSE_STORAGE*)(matrix->storage))->src != matrix->storage;
|
1831
|
+
return matrix->storage->src != matrix->storage;
|
1823
1832
|
}
|
1824
1833
|
|
1825
1834
|
/*
|
@@ -2005,40 +2014,57 @@ nm::dtype_t nm_dtype_guess(VALUE v) {
|
|
2005
2014
|
|
2006
2015
|
|
2007
2016
|
/*
|
2008
|
-
*
|
2017
|
+
* Allocate and return a SLICE object, which will contain the appropriate coordinate and length information for
|
2018
|
+
* accessing some part of a matrix.
|
2009
2019
|
*/
|
2010
|
-
static SLICE* get_slice(size_t dim, VALUE*
|
2011
|
-
size_t r;
|
2020
|
+
static SLICE* get_slice(size_t dim, int argc, VALUE* arg, size_t* shape) {
|
2012
2021
|
VALUE beg, end;
|
2013
|
-
int
|
2022
|
+
int excl;
|
2014
2023
|
|
2015
2024
|
SLICE* slice = alloc_slice(dim);
|
2016
2025
|
slice->single = true;
|
2017
2026
|
|
2018
|
-
|
2027
|
+
// r is the shape position; t is the slice position. They may differ when we're dealing with a
|
2028
|
+
// matrix where the effective dimension is less than the dimension (e.g., a vector).
|
2029
|
+
for (size_t r = 0, t = 0; r < dim; ++r) {
|
2030
|
+
VALUE v = t == argc ? Qnil : arg[t];
|
2031
|
+
|
2032
|
+
// if the current shape indicates a vector and fewer args were supplied than necessary, just use 0
|
2033
|
+
if (argc - t + r < dim && shape[r] == 1) {
|
2034
|
+
slice->coords[r] = 0;
|
2035
|
+
slice->lengths[r] = 1;
|
2019
2036
|
|
2020
|
-
if (FIXNUM_P(
|
2037
|
+
} else if (FIXNUM_P(v)) { // this used CLASS_OF before, which is inefficient for fixnum
|
2021
2038
|
|
2022
|
-
slice->coords[r] = FIX2UINT(
|
2039
|
+
slice->coords[r] = FIX2UINT(v);
|
2023
2040
|
slice->lengths[r] = 1;
|
2041
|
+
t++;
|
2024
2042
|
|
2025
|
-
} else if (
|
2026
|
-
|
2027
|
-
|
2028
|
-
|
2043
|
+
} else if (TYPE(arg[t]) == T_HASH) { // 3:5 notation (inclusive)
|
2044
|
+
VALUE begin_end = rb_funcall(v, rb_intern("shift"), 0); // rb_hash_shift
|
2045
|
+
slice->coords[r] = FIX2UINT(rb_ary_entry(begin_end, 0));
|
2046
|
+
slice->lengths[r] = FIX2UINT(rb_ary_entry(begin_end, 1)) - slice->coords[r];
|
2029
2047
|
|
2030
|
-
|
2031
|
-
if (exl)
|
2032
|
-
slice->lengths[r] -= 1;
|
2048
|
+
if (RHASH_EMPTY_P(v)) t++; // go on to the next
|
2033
2049
|
|
2034
|
-
|
2050
|
+
slice->single = false;
|
2051
|
+
|
2052
|
+
} else if (CLASS_OF(v) == rb_cRange) {
|
2053
|
+
rb_range_values(arg[t], &beg, &end, &excl);
|
2054
|
+
slice->coords[r] = FIX2UINT(beg);
|
2055
|
+
// Exclude last element for a...b range
|
2056
|
+
slice->lengths[r] = FIX2UINT(end) - slice->coords[r] + (excl ? 0 : 1);
|
2057
|
+
|
2058
|
+
slice->single = false;
|
2059
|
+
|
2060
|
+
t++;
|
2035
2061
|
|
2036
2062
|
} else {
|
2037
|
-
rb_raise(rb_eArgError, "
|
2063
|
+
rb_raise(rb_eArgError, "expected Fixnum, Range, or Hash for slice component instead of %s", rb_obj_classname(v));
|
2038
2064
|
}
|
2039
2065
|
|
2040
|
-
if (slice->coords[r] + slice->lengths[r] >
|
2041
|
-
rb_raise(
|
2066
|
+
if (slice->coords[r] > shape[r] || slice->coords[r] + slice->lengths[r] > shape[r])
|
2067
|
+
rb_raise(rb_eRangeError, "slice is larger than matrix in dimension %u (slice component %u)", r, t);
|
2042
2068
|
}
|
2043
2069
|
|
2044
2070
|
return slice;
|
@@ -2264,24 +2290,23 @@ static VALUE nm_det_exact(VALUE self) {
|
|
2264
2290
|
*
|
2265
2291
|
* Returns a properly-wrapped Ruby object as a VALUE.
|
2266
2292
|
*
|
2293
|
+
* *** Note that this function is for API only. Please do not use it internally.
|
2294
|
+
*
|
2267
2295
|
* TODO: Add a column-major option for libraries that use column-major matrices.
|
2268
2296
|
*/
|
2269
2297
|
VALUE rb_nmatrix_dense_create(nm::dtype_t dtype, size_t* shape, size_t dim, void* elements, size_t length) {
|
2270
2298
|
NMATRIX* nm;
|
2271
|
-
VALUE klass;
|
2272
2299
|
size_t nm_dim;
|
2273
2300
|
size_t* shape_copy;
|
2274
2301
|
|
2275
|
-
// Do not allow a dim of 1
|
2302
|
+
// Do not allow a dim of 1. Treat it as a column or row matrix.
|
2276
2303
|
if (dim == 1) {
|
2277
|
-
klass = cNVector;
|
2278
2304
|
nm_dim = 2;
|
2279
2305
|
shape_copy = ALLOC_N(size_t, nm_dim);
|
2280
2306
|
shape_copy[0] = shape[0];
|
2281
2307
|
shape_copy[1] = 1;
|
2282
2308
|
|
2283
2309
|
} else {
|
2284
|
-
klass = cNMatrix;
|
2285
2310
|
nm_dim = dim;
|
2286
2311
|
shape_copy = ALLOC_N(size_t, nm_dim);
|
2287
2312
|
memcpy(shape_copy, shape, sizeof(size_t)*nm_dim);
|
@@ -2295,7 +2320,7 @@ VALUE rb_nmatrix_dense_create(nm::dtype_t dtype, size_t* shape, size_t dim, void
|
|
2295
2320
|
nm = nm_create(nm::DENSE_STORE, nm_dense_storage_create(dtype, shape_copy, dim, elements_copy, length));
|
2296
2321
|
|
2297
2322
|
// tell Ruby about the matrix and its storage, particularly how to garbage collect it.
|
2298
|
-
return Data_Wrap_Struct(
|
2323
|
+
return Data_Wrap_Struct(cNMatrix, nm_dense_storage_mark, nm_dense_storage_delete, nm);
|
2299
2324
|
}
|
2300
2325
|
|
2301
2326
|
/*
|
@@ -2303,9 +2328,8 @@ VALUE rb_nmatrix_dense_create(nm::dtype_t dtype, size_t* shape, size_t dim, void
|
|
2303
2328
|
*
|
2304
2329
|
* Basically just a convenience wrapper for rb_nmatrix_dense_create().
|
2305
2330
|
*
|
2306
|
-
* Returns a properly-wrapped Ruby
|
2307
|
-
*
|
2308
|
-
* TODO: Add a transpose option for setting the orientation of the vector?
|
2331
|
+
* Returns a properly-wrapped Ruby NMatrix object as a VALUE. Included for backwards compatibility
|
2332
|
+
* for when NMatrix had an NVector class.
|
2309
2333
|
*/
|
2310
2334
|
VALUE rb_nvector_dense_create(nm::dtype_t dtype, void* elements, size_t length) {
|
2311
2335
|
size_t dim = 1, shape = length;
|