nmatrix 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/Gemfile +5 -0
  4. data/History.txt +97 -0
  5. data/Manifest.txt +34 -7
  6. data/README.rdoc +13 -13
  7. data/Rakefile +36 -26
  8. data/ext/nmatrix/data/data.cpp +15 -2
  9. data/ext/nmatrix/data/data.h +4 -0
  10. data/ext/nmatrix/data/ruby_object.h +5 -14
  11. data/ext/nmatrix/extconf.rb +3 -2
  12. data/ext/nmatrix/{util/math.cpp → math.cpp} +296 -6
  13. data/ext/nmatrix/math/asum.h +143 -0
  14. data/ext/nmatrix/math/geev.h +82 -0
  15. data/ext/nmatrix/math/gemm.h +267 -0
  16. data/ext/nmatrix/math/gemv.h +208 -0
  17. data/ext/nmatrix/math/ger.h +96 -0
  18. data/ext/nmatrix/math/gesdd.h +80 -0
  19. data/ext/nmatrix/math/gesvd.h +78 -0
  20. data/ext/nmatrix/math/getf2.h +86 -0
  21. data/ext/nmatrix/math/getrf.h +240 -0
  22. data/ext/nmatrix/math/getri.h +107 -0
  23. data/ext/nmatrix/math/getrs.h +125 -0
  24. data/ext/nmatrix/math/idamax.h +86 -0
  25. data/ext/nmatrix/{util → math}/lapack.h +60 -356
  26. data/ext/nmatrix/math/laswp.h +165 -0
  27. data/ext/nmatrix/math/long_dtype.h +52 -0
  28. data/ext/nmatrix/math/math.h +1154 -0
  29. data/ext/nmatrix/math/nrm2.h +181 -0
  30. data/ext/nmatrix/math/potrs.h +125 -0
  31. data/ext/nmatrix/math/rot.h +141 -0
  32. data/ext/nmatrix/math/rotg.h +115 -0
  33. data/ext/nmatrix/math/scal.h +73 -0
  34. data/ext/nmatrix/math/swap.h +73 -0
  35. data/ext/nmatrix/math/trsm.h +383 -0
  36. data/ext/nmatrix/nmatrix.cpp +176 -152
  37. data/ext/nmatrix/nmatrix.h +1 -2
  38. data/ext/nmatrix/ruby_constants.cpp +9 -4
  39. data/ext/nmatrix/ruby_constants.h +1 -0
  40. data/ext/nmatrix/storage/dense.cpp +57 -41
  41. data/ext/nmatrix/storage/list.cpp +52 -50
  42. data/ext/nmatrix/storage/storage.cpp +59 -43
  43. data/ext/nmatrix/storage/yale.cpp +352 -333
  44. data/ext/nmatrix/storage/yale.h +4 -0
  45. data/lib/nmatrix.rb +2 -2
  46. data/lib/nmatrix/blas.rb +4 -4
  47. data/lib/nmatrix/enumerate.rb +241 -0
  48. data/lib/nmatrix/lapack.rb +54 -1
  49. data/lib/nmatrix/math.rb +462 -0
  50. data/lib/nmatrix/nmatrix.rb +210 -486
  51. data/lib/nmatrix/nvector.rb +0 -62
  52. data/lib/nmatrix/rspec.rb +75 -0
  53. data/lib/nmatrix/shortcuts.rb +136 -108
  54. data/lib/nmatrix/version.rb +1 -1
  55. data/spec/blas_spec.rb +20 -12
  56. data/spec/elementwise_spec.rb +22 -13
  57. data/spec/io_spec.rb +1 -0
  58. data/spec/lapack_spec.rb +197 -0
  59. data/spec/nmatrix_spec.rb +39 -38
  60. data/spec/nvector_spec.rb +3 -9
  61. data/spec/rspec_monkeys.rb +29 -0
  62. data/spec/rspec_spec.rb +34 -0
  63. data/spec/shortcuts_spec.rb +14 -16
  64. data/spec/slice_spec.rb +242 -186
  65. data/spec/spec_helper.rb +19 -0
  66. metadata +33 -5
  67. data/ext/nmatrix/util/math.h +0 -2612
@@ -47,7 +47,7 @@ extern "C" {
47
47
 
48
48
  #include "types.h"
49
49
  #include "data/data.h"
50
- #include "util/math.h"
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* c, VALUE self);
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
- free(slice->coords);
585
- free(slice->lengths);
586
- free(slice);
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(((YALE_STORAGE*)(NM_STORAGE(self)))->capacity);
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
- free(mat);
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
- nm_yale_storage_delete
667
+ nm_yale_storage_delete_ref
654
668
  };
655
669
  ttable[mat->stype](mat->storage);
656
670
 
657
- free(mat);
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
- * complex_conjugate -> NMatrix
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(klass, nm_dense_storage_mark, nm_delete, nm);
1427
+ return Data_Wrap_Struct(cNMatrix, nm_dense_storage_mark, nm_delete, nm);
1412
1428
  case nm::YALE_STORE:
1413
- return Data_Wrap_Struct(klass, nm_yale_storage_mark, nm_delete, nm);
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
- // Refs only allowed for dense and list matrices.
1451
- if (NM_STYPE(self) == nm::DENSE_STORE) {
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 = argc - 1; // last arg is the value
1518
+ size_t dim = NM_DIM(self); // last arg is the value
1512
1519
 
1513
- if (argc <= 1) {
1514
- rb_raise(rb_eArgError, "Expected coordinates and r-value");
1515
-
1516
- } else if (NM_DIM(self) == dim) {
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[dim], NM_DTYPE(self));
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
- free(value);
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
- free(value);
1537
+ xfree(value);
1533
1538
  value = nm_list_storage_remove(NM_STORAGE(self), slice);
1534
- free(value);
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
- free(value);
1547
+ xfree(value);
1543
1548
  break;
1544
1549
  }
1545
1550
  free_slice(slice);
1546
1551
 
1547
- return argv[dim];
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, "for matrix-vector multiplication, please use an NVector instead of an Array for now");
1573
+ rb_raise(rb_eNotImpError, "please convert array to nx1 or 1xn NMatrix first");
1577
1574
 
1578
- //if (RDATA(right_v)->dfree != (RUBY_DATA_FUNC)nm_delete) {
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
- * This function may lie slightly for NVectors, which are internally stored as
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) == (size_t)(argc)) {
1703
- SLICE* slice = get_slice((size_t)(argc), argv, self);
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)](NM_STORAGE(self), slice) );
1713
- else result = rubyobj_from_cval( ttable[NM_STYPE(self)](NM_STORAGE(self), slice), NM_DTYPE(self) ).rval;
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 = ALLOC(NMATRIX);
1719
- mat->stype = NM_STYPE(self);
1720
- mat->storage = (STORAGE*)((*slice_func)( NM_STORAGE(self), slice ));
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 = Data_Wrap_Struct(klass, mark_table[mat->stype], delete_func, mat);
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
- // FIXME: Needs to work for other types
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
- * Documentation goes here.
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* c, VALUE self) {
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 exl;
2022
+ int excl;
2014
2023
 
2015
2024
  SLICE* slice = alloc_slice(dim);
2016
2025
  slice->single = true;
2017
2026
 
2018
- for (r = 0; r < dim; ++r) {
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(c[r])) { // this used CLASS_OF before, which is inefficient for fixnum
2037
+ } else if (FIXNUM_P(v)) { // this used CLASS_OF before, which is inefficient for fixnum
2021
2038
 
2022
- slice->coords[r] = FIX2UINT(c[r]);
2039
+ slice->coords[r] = FIX2UINT(v);
2023
2040
  slice->lengths[r] = 1;
2041
+ t++;
2024
2042
 
2025
- } else if (CLASS_OF(c[r]) == rb_cRange) {
2026
- rb_range_values(c[r], &beg, &end, &exl);
2027
- slice->coords[r] = FIX2UINT(beg);
2028
- slice->lengths[r] = FIX2UINT(end) - slice->coords[r] + 1;
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
- // Exclude last element for a...b range
2031
- if (exl)
2032
- slice->lengths[r] -= 1;
2048
+ if (RHASH_EMPTY_P(v)) t++; // go on to the next
2033
2049
 
2034
- slice->single = false;
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, "cannot slice using class %s, needs a number or range or something", rb_obj_classname(c[r]));
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] > NM_SHAPE(self,r))
2041
- rb_raise(rb_eArgError, "out of range");
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; if dim == 1, this should probably be an NVector instead, but that still has dim 2.
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(klass, nm_dense_storage_mark, nm_dense_storage_delete, nm);
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 NVector object as a VALUE.
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;