nmatrix 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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;
|