nmatrix 0.2.3 → 0.2.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8dd60ade9441cfcdc2c70cd18a60571110c18145
4
- data.tar.gz: 3ae0a112c051f510718a2d7afad97327bb6808c2
3
+ metadata.gz: f0860add5c9502fe90139d362beaf61c116b07a9
4
+ data.tar.gz: c3880411c97cdf56a9bff9e2b35df05cf218f2c7
5
5
  SHA512:
6
- metadata.gz: 6d9fcb8e0ba7ebb62cd30fa7aba1aafe0bcf1dba7ce7e77a12f3e249280212c668313c94b3e80ce40528829061ef0a6e8b140eda8d8f66175023133712393f72
7
- data.tar.gz: ba0a384a026c56c01595b7de0b3a3a741bbe021d2b44e358a14c8f5d665c020262d0b400f6eade6a20ee91d65f90e47d730cc015430db5a0539ad4d684ceb6eb
6
+ metadata.gz: d9e8f203ce54fb5a53fef9011b6ba6fed3b4080e0d28364625ef63892cb5381153cb0aef6e5e6d1ad9d286f0df588f591263cd9f50872df543fba146da58ce73
7
+ data.tar.gz: 0c4e726871ca3c69d4b522acd9c5c05caba55bcce54339330762ca39d100c9159730ae16430f3319e9042e09b9a76b6d7cb54cb1b0bdfe77e1d25ad1f1693e79
@@ -110,7 +110,7 @@ class RubyObject {
110
110
  inline operator VALUE() const { return rval; }
111
111
  //inline operator uint32_t() const { return NUM2ULONG(this->rval); }
112
112
  inline operator int64_t() const { RETURN_OBJ2NUM(NUM2LONG) }
113
- inline operator uint64_t() const { RETURN_OBJ2NUM(NUM2ULONG) }
113
+ //inline operator uint64_t() const { RETURN_OBJ2NUM(NUM2ULONG) }
114
114
  inline operator double() const { RETURN_OBJ2NUM(NUM2DBL) }
115
115
  inline operator float() const { RETURN_OBJ2NUM(NUM2DBL) }
116
116
 
@@ -188,14 +188,14 @@ extern "C" {
188
188
  // Math Functions //
189
189
  ////////////////////
190
190
 
191
- namespace nm {
191
+ namespace nm {
192
192
  namespace math {
193
193
 
194
194
  /*
195
195
  * Calculate the determinant for a dense matrix (A [elements]) of size 2 or 3. Return the result.
196
196
  */
197
197
  template <typename DType>
198
- void det_exact(const int M, const void* A_elements, const int lda, void* result_arg) {
198
+ void det_exact_from_dense(const int M, const void* A_elements, const int lda, void* result_arg) {
199
199
  DType* result = reinterpret_cast<DType*>(result_arg);
200
200
  const DType* A = reinterpret_cast<const DType*>(A_elements);
201
201
 
@@ -203,7 +203,6 @@ namespace nm {
203
203
 
204
204
  if (M == 2) {
205
205
  *result = A[0] * A[lda+1] - A[1] * A[lda];
206
-
207
206
  } else if (M == 3) {
208
207
  x = A[lda+1] * A[2*lda+2] - A[lda+2] * A[2*lda+1]; // ei - fh
209
208
  y = A[lda] * A[2*lda+2] - A[lda+2] * A[2*lda]; // fg - di
@@ -220,9 +219,99 @@ namespace nm {
220
219
 
221
220
  //we can't do det_exact on byte, because it will want to return a byte (unsigned), but determinants can be negative, even if all elements of the matrix are positive
222
221
  template <>
223
- void det_exact<uint8_t>(const int M, const void* A_elements, const int lda, void* result_arg) {
222
+ void det_exact_from_dense<uint8_t>(const int M, const void* A_elements, const int lda, void* result_arg) {
224
223
  rb_raise(nm_eDataTypeError, "cannot call det_exact on unsigned type");
225
224
  }
225
+ /*
226
+ * Calculate the determinant for a yale matrix (storage) of size 2 or 3. Return the result.
227
+ */
228
+ template <typename DType>
229
+ void det_exact_from_yale(const int M, const YALE_STORAGE* storage, const int lda, void* result_arg) {
230
+ DType* result = reinterpret_cast<DType*>(result_arg);
231
+ IType* ija = reinterpret_cast<IType *>(storage->ija);
232
+ DType* a = reinterpret_cast<DType*>(storage->a);
233
+ IType col_pos = storage->shape[0] + 1;
234
+ if (M == 2) {
235
+ if (ija[2] - ija[0] == 2) {
236
+ *result = a[0] * a[1] - a[col_pos] * a[col_pos+1];
237
+ }
238
+ else { *result = a[0] * a[1]; }
239
+ } else if (M == 3) {
240
+ DType m[3][3];
241
+ for (int i = 0; i < 3; ++i) {
242
+ m[i][i] = a[i];
243
+ switch(ija[i+1] - ija[i]) {
244
+ case 2:
245
+ m[i][ija[col_pos]] = a[col_pos];
246
+ m[i][ija[col_pos+1]] = a[col_pos+1];
247
+ col_pos += 2;
248
+ break;
249
+ case 1:
250
+ m[i][(i+1)%3] = m[i][(i+2)%3] = 0;
251
+ m[i][ija[col_pos]] = a[col_pos];
252
+ ++col_pos;
253
+ break;
254
+ case 0:
255
+ m[i][(i+1)%3] = m[i][(i+2)%3] = 0;
256
+ break;
257
+ default:
258
+ rb_raise(rb_eArgError, "some value in IJA is incorrect!");
259
+ }
260
+ }
261
+ *result =
262
+ m[0][0] * m[1][1] * m[2][2] + m[0][1] * m[1][2] * m[2][0] + m[0][2] * m[1][0] * m[2][1]
263
+ - m[0][0] * m[1][2] * m[2][1] - m[0][1] * m[1][0] * m[2][2] - m[0][2] * m[1][1] * m[2][0];
264
+
265
+ } else if (M < 2) {
266
+ rb_raise(rb_eArgError, "can only calculate exact determinant of a square matrix of size 2 or larger");
267
+ } else {
268
+ rb_raise(rb_eNotImpError, "exact determinant calculation needed for matrices larger than 3x3");
269
+ }
270
+ }
271
+
272
+ /*
273
+ * Solve a system of linear equations using forward-substution followed by
274
+ * back substution from the LU factorization of the matrix of co-efficients.
275
+ * Replaces x_elements with the result. Works only with non-integer, non-object
276
+ * data types.
277
+ *
278
+ * args - r -> The number of rows of the matrix.
279
+ * lu_elements -> Elements of the LU decomposition of the co-efficients
280
+ * matrix, as a contiguos array.
281
+ * b_elements -> Elements of the the right hand sides, as a contiguous array.
282
+ * x_elements -> The array that will contain the results of the computation.
283
+ * pivot -> Positions of permuted rows.
284
+ */
285
+ template <typename DType>
286
+ void solve(const int r, const void* lu_elements, const void* b_elements, void* x_elements, const int* pivot) {
287
+ int ii = 0, ip;
288
+ DType sum;
289
+
290
+ const DType* matrix = reinterpret_cast<const DType*>(lu_elements);
291
+ const DType* b = reinterpret_cast<const DType*>(b_elements);
292
+ DType* x = reinterpret_cast<DType*>(x_elements);
293
+
294
+ for (int i = 0; i < r; ++i) { x[i] = b[i]; }
295
+ for (int i = 0; i < r; ++i) { // forward substitution loop
296
+ ip = pivot[i];
297
+ sum = x[ip];
298
+ x[ip] = x[i];
299
+
300
+ if (ii != 0) {
301
+ for (int j = ii - 1;j < i; ++j) { sum = sum - matrix[i * r + j] * x[j]; }
302
+ }
303
+ else if (sum != 0.0) {
304
+ ii = i + 1;
305
+ }
306
+ x[i] = sum;
307
+ }
308
+
309
+ for (int i = r - 1; i >= 0; --i) { // back substitution loop
310
+ sum = x[i];
311
+ for (int j = i + 1; j < r; j++) { sum = sum - matrix[i * r + j] * x[j]; }
312
+ x[i] = sum/matrix[i * r + i];
313
+ }
314
+ }
226
315
 
227
316
  /*
228
317
  * Calculates in-place inverse of A_elements. Uses Gauss-Jordan elimination technique.
@@ -246,18 +335,18 @@ namespace nm {
246
335
  for (int row = k + 1; row < M; ++row) {
247
336
  typename MagnitudeDType<DType>::type big;
248
337
  big = magnitude( matrix[M*row + k] ); // element below the temp pivot
249
-
338
+
250
339
  if ( big > akk ) {
251
340
  interchange = row;
252
- akk = big;
341
+ akk = big;
253
342
  }
254
- }
343
+ }
255
344
 
256
345
  if (interchange != k) { // check if rows need flipping
257
346
  DType temp;
258
347
 
259
348
  for (int col = 0; col < M; ++col) {
260
- NM_SWAP(matrix[interchange*M + col], matrix[k*M + col], temp);
349
+ NM_SWAP(matrix[interchange*M + col], matrix[k*M + col], temp);
261
350
  }
262
351
  }
263
352
 
@@ -271,7 +360,7 @@ namespace nm {
271
360
  DType pivot = matrix[k * (M + 1)];
272
361
  matrix[k * (M + 1)] = (DType)(1); // set diagonal as 1 for in-place inversion
273
362
 
274
- for (int col = 0; col < M; ++col) {
363
+ for (int col = 0; col < M; ++col) {
275
364
  // divide each element in the kth row with the pivot
276
365
  matrix[k*M + col] = matrix[k*M + col] / pivot;
277
366
  }
@@ -280,7 +369,7 @@ namespace nm {
280
369
  if (kk == k) continue;
281
370
 
282
371
  DType dum = matrix[k + M*kk];
283
- matrix[k + M*kk] = (DType)(0); // prepare for inplace inversion
372
+ matrix[k + M*kk] = (DType)(0); // prepare for inplace inversion
284
373
  for (int col = 0; col < M; ++col) {
285
374
  matrix[M*kk + col] = matrix[M*kk + col] - matrix[M*k + col] * dum;
286
375
  }
@@ -295,7 +384,7 @@ namespace nm {
295
384
 
296
385
  for (int row = 0; row < M; ++row) {
297
386
  NM_SWAP(matrix[row * M + row_index[k]], matrix[row * M + col_index[k]],
298
- temp);
387
+ temp);
299
388
  }
300
389
  }
301
390
  }
@@ -321,14 +410,14 @@ namespace nm {
321
410
  DType sum_of_squares, *p_row, *psubdiag, *p_a, scale, innerproduct;
322
411
  int i, k, col;
323
412
 
324
- // For each column use a Householder transformation to zero all entries
413
+ // For each column use a Householder transformation to zero all entries
325
414
  // below the subdiagonal.
326
- for (psubdiag = a + nrows, col = 0; col < nrows - 2; psubdiag += nrows + 1,
415
+ for (psubdiag = a + nrows, col = 0; col < nrows - 2; psubdiag += nrows + 1,
327
416
  col++) {
328
417
  // Calculate the signed square root of the sum of squares of the
329
418
  // elements below the diagonal.
330
419
 
331
- for (p_a = psubdiag, sum_of_squares = 0.0, i = col + 1; i < nrows;
420
+ for (p_a = psubdiag, sum_of_squares = 0.0, i = col + 1; i < nrows;
332
421
  p_a += nrows, i++) {
333
422
  sum_of_squares += *p_a * *p_a;
334
423
  }
@@ -358,7 +447,7 @@ namespace nm {
358
447
  *p_a -= u[k] * innerproduct;
359
448
  }
360
449
  }
361
-
450
+
362
451
  // Postmultiply QA by Q
363
452
  for (p_row = a, i = 0; i < nrows; p_row += nrows, i++) {
364
453
  for (innerproduct = 0.0, k = col + 1; k < nrows; k++) {
@@ -375,33 +464,34 @@ namespace nm {
375
464
  delete[] u;
376
465
  }
377
466
 
467
+ void raise_not_invertible_error() {
468
+ rb_raise(nm_eNotInvertibleError,
469
+ "matrix must have non-zero determinant to be invertible (not getting this error does not mean matrix is invertible if you're dealing with floating points)");
470
+ }
471
+
378
472
  /*
379
473
  * Calculate the exact inverse for a dense matrix (A [elements]) of size 2 or 3. Places the result in B_elements.
380
474
  */
381
475
  template <typename DType>
382
- void inverse_exact(const int M, const void* A_elements, const int lda, void* B_elements, const int ldb) {
476
+ void inverse_exact_from_dense(const int M, const void* A_elements,
477
+ const int lda, void* B_elements, const int ldb) {
478
+
383
479
  const DType* A = reinterpret_cast<const DType*>(A_elements);
384
480
  DType* B = reinterpret_cast<DType*>(B_elements);
385
481
 
386
482
  if (M == 2) {
387
483
  DType det = A[0] * A[lda+1] - A[1] * A[lda];
388
- if (det == 0) {
389
- rb_raise(nm_eNotInvertibleError,
390
- "matrix must have non-zero determinant to be invertible (not getting this error does not mean matrix is invertible if you're dealing with floating points)");
391
- }
484
+ if (det == 0) { raise_not_invertible_error(); }
392
485
  B[0] = A[lda+1] / det;
393
486
  B[1] = -A[1] / det;
394
487
  B[ldb] = -A[lda] / det;
395
- B[ldb+1] = -A[0] / det;
488
+ B[ldb+1] = A[0] / det;
396
489
 
397
490
  } else if (M == 3) {
398
491
  // Calculate the exact determinant.
399
492
  DType det;
400
- det_exact<DType>(M, A_elements, lda, reinterpret_cast<void*>(&det));
401
- if (det == 0) {
402
- rb_raise(nm_eNotInvertibleError,
403
- "matrix must have non-zero determinant to be invertible (not getting this error does not mean matrix is invertible if you're dealing with floating points)");
404
- }
493
+ det_exact_from_dense<DType>(M, A_elements, lda, reinterpret_cast<void*>(&det));
494
+ if (det == 0) { raise_not_invertible_error(); }
405
495
 
406
496
  B[0] = ( A[lda+1] * A[2*lda+2] - A[lda+2] * A[2*lda+1]) / det; // A = ei - fh
407
497
  B[1] = (- A[1] * A[2*lda+2] + A[2] * A[2*lda+1]) / det; // D = -bi + ch
@@ -419,6 +509,113 @@ namespace nm {
419
509
  }
420
510
  }
421
511
 
512
+ template <typename DType>
513
+ void inverse_exact_from_yale(const int M, const YALE_STORAGE* storage,
514
+ const int lda, YALE_STORAGE* inverse, const int ldb) {
515
+
516
+ // inverse is a clone of storage
517
+ const DType* a = reinterpret_cast<const DType*>(storage->a);
518
+ const IType* ija = reinterpret_cast<const IType *>(storage->ija);
519
+ DType* b = reinterpret_cast<DType*>(inverse->a);
520
+ IType* ijb = reinterpret_cast<IType *>(inverse->ija);
521
+ IType col_pos = storage->shape[0] + 1;
522
+ // Calculate the exact determinant.
523
+ DType det;
524
+
525
+ if (M == 2) {
526
+ IType ndnz = ija[2] - ija[0];
527
+ if (ndnz == 2) {
528
+ det = a[0] * a[1] - a[col_pos] * a[col_pos+1];
529
+ }
530
+ else { det = a[0] * a[1]; }
531
+ if (det == 0) { raise_not_invertible_error(); }
532
+ b[0] = a[1] / det;
533
+ b[1] = a[0] / det;
534
+ if (ndnz == 2) {
535
+ b[col_pos] = -a[col_pos] / det;
536
+ b[col_pos+1] = -a[col_pos+1] / det;
537
+ }
538
+ else if (ndnz == 1) {
539
+ b[col_pos] = -a[col_pos] / det;
540
+ }
541
+
542
+ } else if (M == 3) {
543
+ DType *A = new DType[lda*3];
544
+ for (int i = 0; i < lda; ++i) {
545
+ A[i*3+i] = a[i];
546
+ switch (ija[i+1] - ija[i]) {
547
+ case 2:
548
+ A[i*3 + ija[col_pos]] = a[col_pos];
549
+ A[i*3 + ija[col_pos+1]] = a[col_pos+1];
550
+ col_pos += 2;
551
+ break;
552
+ case 1:
553
+ A[i*3 + (i+1)%3] = A[i*3 + (i+2)%3] = 0;
554
+ A[i*3 + ija[col_pos]] = a[col_pos];
555
+ col_pos += 1;
556
+ break;
557
+ case 0:
558
+ A[i*3 + (i+1)%3] = A[i*3 + (i+2)%3] = 0;
559
+ break;
560
+ default:
561
+ rb_raise(rb_eArgError, "some value in IJA is incorrect!");
562
+ }
563
+ }
564
+ det =
565
+ A[0] * A[lda+1] * A[2*lda+2] + A[1] * A[lda+2] * A[2*lda] + A[2] * A[lda] * A[2*lda+1]
566
+ - A[0] * A[lda+2] * A[2*lda+1] - A[1] * A[lda] * A[2*lda+2] - A[2] * A[lda+1] * A[2*lda];
567
+ if (det == 0) { raise_not_invertible_error(); }
568
+
569
+ DType *B = new DType[3*ldb];
570
+ B[0] = ( A[lda+1] * A[2*lda+2] - A[lda+2] * A[2*lda+1]) / det; // A = ei - fh
571
+ B[1] = (- A[1] * A[2*lda+2] + A[2] * A[2*lda+1]) / det; // D = -bi + ch
572
+ B[2] = ( A[1] * A[lda+2] - A[2] * A[lda+1]) / det; // G = bf - ce
573
+ B[ldb] = (- A[lda] * A[2*lda+2] + A[lda+2] * A[2*lda]) / det; // B = -di + fg
574
+ B[ldb+1] = ( A[0] * A[2*lda+2] - A[2] * A[2*lda]) / det; // E = ai - cg
575
+ B[ldb+2] = (- A[0] * A[lda+2] + A[2] * A[lda]) / det; // H = -af + cd
576
+ B[2*ldb] = ( A[lda] * A[2*lda+1] - A[lda+1] * A[2*lda]) / det; // C = dh - eg
577
+ B[2*ldb+1]= ( -A[0] * A[2*lda+1] + A[1] * A[2*lda]) / det; // F = -ah + bg
578
+ B[2*ldb+2]= ( A[0] * A[lda+1] - A[1] * A[lda]) / det; // I = ae - bd
579
+
580
+ // Calculate the size of ijb and b, then reallocate them.
581
+ IType ndnz = 0;
582
+ for (int i = 0; i < 3; ++i) {
583
+ for (int j = 0; j < 3; ++j) {
584
+ if (j != i && B[i*ldb + j] != 0) { ++ndnz; }
585
+ }
586
+ }
587
+ inverse->ndnz = ndnz;
588
+ col_pos = 4; // shape[0] + 1
589
+ inverse->capacity = 4 + ndnz;
590
+ NM_REALLOC_N(inverse->a, DType, 4 + ndnz);
591
+ NM_REALLOC_N(inverse->ija, IType, 4 + ndnz);
592
+ b = reinterpret_cast<DType*>(inverse->a);
593
+ ijb = reinterpret_cast<IType *>(inverse->ija);
594
+
595
+ for (int i = 0; i < 3; ++i) {
596
+ ijb[i] = col_pos;
597
+ for (int j = 0; j < 3; ++j) {
598
+ if (j == i) {
599
+ b[i] = B[i*ldb + j];
600
+ }
601
+ else if (B[i*ldb + j] != 0) {
602
+ b[col_pos] = B[i*ldb + j];
603
+ ijb[col_pos] = j;
604
+ ++col_pos;
605
+ }
606
+ }
607
+ }
608
+ b[3] = 0;
609
+ ijb[3] = col_pos;
610
+ delete [] B;
611
+ delete [] A;
612
+ } else if (M == 1) {
613
+ b[0] = 1 / a[0];
614
+ } else {
615
+ rb_raise(rb_eNotImpError, "exact inverse calculation needed for matrices larger than 3x3");
616
+ }
617
+ }
618
+
422
619
  /*
423
620
  * Function signature conversion for calling CBLAS' gemm functions as directly as possible.
424
621
  *
@@ -1068,14 +1265,43 @@ static VALUE nm_clapack_laswp(VALUE self, VALUE n, VALUE a, VALUE lda, VALUE k1,
1068
1265
 
1069
1266
 
1070
1267
  /*
1071
- * C accessor for calculating an exact determinant.
1268
+ * C accessor for calculating an exact determinant. Dense matrix version.
1072
1269
  */
1073
- void nm_math_det_exact(const int M, const void* elements, const int lda, nm::dtype_t dtype, void* result) {
1074
- NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::math::det_exact, void, const int M, const void* A_elements, const int lda, void* result_arg);
1270
+ void nm_math_det_exact_from_dense(const int M, const void* elements, const int lda,
1271
+ nm::dtype_t dtype, void* result) {
1272
+ NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::math::det_exact_from_dense, void, const int M,
1273
+ const void* A_elements, const int lda, void* result_arg);
1075
1274
 
1076
1275
  ttable[dtype](M, elements, lda, result);
1077
1276
  }
1078
1277
 
1278
+ /*
1279
+ * C accessor for calculating an exact determinant. Yale matrix version.
1280
+ */
1281
+ void nm_math_det_exact_from_yale(const int M, const YALE_STORAGE* storage, const int lda,
1282
+ nm::dtype_t dtype, void* result) {
1283
+ NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::math::det_exact_from_yale, void, const int M,
1284
+ const YALE_STORAGE* storage, const int lda, void* result_arg);
1285
+
1286
+ ttable[dtype](M, storage, lda, result);
1287
+ }
1288
+
1289
+ /*
1290
+ * C accessor for solving a system of linear equations.
1291
+ */
1292
+ void nm_math_solve(VALUE lu, VALUE b, VALUE x, VALUE ipiv) {
1293
+ int* pivot = new int[RARRAY_LEN(ipiv)];
1294
+
1295
+ for (int i = 0; i < RARRAY_LEN(ipiv); ++i) {
1296
+ pivot[i] = FIX2INT(rb_ary_entry(ipiv, i));
1297
+ }
1298
+
1299
+ NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::math::solve, void, const int, const void*, const void*, void*, const int*);
1300
+
1301
+ ttable[NM_DTYPE(x)](NM_SHAPE0(b), NM_STORAGE_DENSE(lu)->elements,
1302
+ NM_STORAGE_DENSE(b)->elements, NM_STORAGE_DENSE(x)->elements, pivot);
1303
+ }
1304
+
1079
1305
  /*
1080
1306
  * C accessor for reducing a matrix to hessenberg form.
1081
1307
  */
@@ -1087,7 +1313,7 @@ void nm_math_hessenberg(VALUE a) {
1087
1313
  NULL, NULL, // does not support Complex
1088
1314
  NULL // no support for Ruby Object
1089
1315
  };
1090
-
1316
+
1091
1317
  ttable[NM_DTYPE(a)](NM_SHAPE0(a), NM_STORAGE_DENSE(a)->elements);
1092
1318
  }
1093
1319
  /*
@@ -1100,14 +1326,29 @@ void nm_math_inverse(const int M, void* a_elements, nm::dtype_t dtype) {
1100
1326
  }
1101
1327
 
1102
1328
  /*
1103
- * C accessor for calculating an exact inverse.
1329
+ * C accessor for calculating an exact inverse. Dense matrix version.
1104
1330
  */
1105
- 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) {
1106
- NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::math::inverse_exact, void, const int, const void*, const int, void*, const int);
1331
+ void nm_math_inverse_exact_from_dense(const int M, const void* A_elements,
1332
+ const int lda, void* B_elements, const int ldb, nm::dtype_t dtype) {
1333
+
1334
+ NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::math::inverse_exact_from_dense, void,
1335
+ const int, const void*, const int, void*, const int);
1107
1336
 
1108
1337
  ttable[dtype](M, A_elements, lda, B_elements, ldb);
1109
1338
  }
1110
1339
 
1340
+ /*
1341
+ * C accessor for calculating an exact inverse. Yale matrix version.
1342
+ */
1343
+ void nm_math_inverse_exact_from_yale(const int M, const YALE_STORAGE* storage,
1344
+ const int lda, YALE_STORAGE* inverse, const int ldb, nm::dtype_t dtype) {
1345
+
1346
+ NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::math::inverse_exact_from_yale, void,
1347
+ const int, const YALE_STORAGE*, const int, YALE_STORAGE*, const int);
1348
+
1349
+ ttable[dtype](M, storage, lda, inverse, ldb);
1350
+ }
1351
+
1111
1352
  /*
1112
1353
  * Transpose an array of elements that represent a row-major dense matrix. Does not allocate anything, only does an memcpy.
1113
1354
  */