numo-linalg-alt 0.6.0 → 0.7.0

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
  SHA256:
3
- metadata.gz: 94dfd3c199541e6dc2b71968e11450af79613c6dba382c3ac0cd64541f2d8461
4
- data.tar.gz: 9a7bc15d245eeb94a97c524a474f10345b78a7693ab766b0cbea3353791e22b1
3
+ metadata.gz: 42941b98550789f5cca4d1eb91a1dd88442a7f89574ef38b8260ec3882ec4eba
4
+ data.tar.gz: 15a4fa8b6147463a8fd91f9fbd16f688ebf5cc01becdf4f0589e515b7f00fd99
5
5
  SHA512:
6
- metadata.gz: 2cc8b7019527b642d0827f3f1b1943e7f16889ec83af93c8ba85b6b2209f6fb17c33b457c362679ea296f48b64c6bc03811b2bb6f4ace28d9a859d18d049fcea
7
- data.tar.gz: d68a26e612d144410100b56706157ef7434a6844c96d4540748cb08ebbe11843445de84667c9406e591274f3622e72f4176231f9af979a3782893b72fbd5abc7
6
+ metadata.gz: 85e6b6deefa0cd3fca1204cf2ccf2c337386061e79403dd420e51745869c9f4548bedd72bf9ca78b9f5506fdc9808b635810fb7a515f3e318bcbd46bd8c65e45
7
+ data.tar.gz: a109ac617d606e70fa8aec1f2c721ab588316b845628b912b7cc381c62bf770326a31c97e750a9308163695ba7c7d9593084060fc766e07e081db8c7559e29c7
data/CHANGELOG.md CHANGED
@@ -1,3 +1,34 @@
1
+ ## [[0.7.0](https://github.com/yoshoku/numo-linalg-alt/compare/v0.6.0...v0.7.0)] - 2025-11-11
2
+
3
+ **Breaking changes**
4
+
5
+ improve error handling for LAPACK functions:
6
+
7
+ - add `LapackError` class under `Numo::Linalg` module.
8
+ - This exception class is raised when invalid arguments are passed to a LAPACK function or
9
+ when the algorithm does not execute successfully. In previous versions, `StandardError`
10
+ was raised in these cases.
11
+ ```ruby
12
+ > Numo::Linalg.inv(Numo::DFloat[[3, 1], [9, 3]])
13
+ /numo-linalg-alt/lib/numo/linalg.rb:418:in 'Numo::Linalg.inv': The matrix is singular, and
14
+ the inverse matrix could not be computed. (Numo::Linalg::LapackError)
15
+ ```
16
+ - change `solve`, `lu_fact`, `ldl`, and `qz` methods to issue a warning instead of raising an
17
+ error when the algorithm completes but produces a result that may affect further computations,
18
+ such as when the resulting matrix is sigular.
19
+ ```ruby
20
+ > Numo::Linalg.solve(Numo::DFloat.zeros(2, 2), Numo::DFloat.ones(2))
21
+ the factorization has been completed, but the factor is singular, so the solution could not be computed.
22
+ =>
23
+ Numo::DFloat#shape=[2]
24
+ [1, 1]
25
+ ```
26
+ - change `det` method to return zero instead of raising an error when the input matrix is singular.
27
+ ```ruby
28
+ Numo::Linalg.det(Numo::DFloat[[1, 2], [2, 4]])
29
+ => -0.0
30
+ ```
31
+
1
32
  ## [[0.6.0](https://github.com/yoshoku/numo-linalg-alt/compare/v0.5.0...v0.6.0)] - 2025-11-02
2
33
 
3
34
  - add `--with-blas` and `--with-lapacke` options for selecting backend libraries.
@@ -5,6 +5,6 @@ module Numo
5
5
  # Numo::Linalg Alternative (numo-linalg-alt) is an alternative to Numo::Linalg.
6
6
  module Linalg
7
7
  # The version of numo-linalg-alt you install.
8
- VERSION = '0.6.0'
8
+ VERSION = '0.7.0'
9
9
  end
10
10
  end
data/lib/numo/linalg.rb CHANGED
@@ -8,6 +8,9 @@ require_relative 'linalg/linalg'
8
8
  module Numo
9
9
  # Numo::Linalg Alternative (numo-linalg-alt) is an alternative to Numo::Linalg.
10
10
  module Linalg # rubocop:disable Metrics/ModuleLength
11
+ # Exception class for errors occurred in LAPACK functions.
12
+ class LapackError < StandardError; end
13
+
11
14
  module_function
12
15
 
13
16
  # Computes the eigenvalues and eigenvectors of a symmetric / Hermitian matrix
@@ -341,7 +344,7 @@ module Numo
341
344
 
342
345
  fnc = :"#{bchr}potrs"
343
346
  x, info = Numo::Linalg::Lapack.send(fnc, a, b.dup, uplo: uplo)
344
- raise "the #{-info}-th argument of potrs had illegal value" if info.negative?
347
+ raise LapackError, "the #{-info}-th argument of potrs had illegal value" if info.negative?
345
348
 
346
349
  x
347
350
  end
@@ -366,8 +369,12 @@ module Numo
366
369
 
367
370
  getrf = :"#{bchr}getrf"
368
371
  lu, piv, info = Numo::Linalg::Lapack.send(getrf, a.dup)
369
- raise "the #{-info}-th argument of getrf had illegal value" if info.negative?
370
- raise 'the factor U is singular, and the inverse matrix could not be computed.' if info.positive?
372
+ raise LapackError, "the #{-info}-th argument of getrf had illegal value" if info.negative?
373
+
374
+ # info > 0 means the factor U has a zero diagonal element and is singular.
375
+ # In this case, the determinant is zero. The method should simply return 0.0.
376
+ # Therefore, the error is not raised here.
377
+ # raise 'the factor U is singular, ...' if info.positive?
371
378
 
372
379
  det_l = 1
373
380
  det_u = lu.diagonal.prod
@@ -405,12 +412,11 @@ module Numo
405
412
  getri = :"#{bchr}getri"
406
413
 
407
414
  lu, piv, info = Numo::Linalg::Lapack.send(getrf, a.dup)
408
- raise "the #{-info}-th argument of getrf had illegal value" if info.negative?
409
- raise 'the factor U is singular, and the inverse matrix could not be computed.' if info.positive?
415
+ raise LapackError, "the #{-info}-th argument of getrf had illegal value" if info.negative?
410
416
 
411
417
  a_inv, info = Numo::Linalg::Lapack.send(getri, lu, piv)
412
- raise "the #{-info}-th argument of getrf had illegal value" if info.negative?
413
- raise 'the factor U is singular, and the inverse matrix could not be computed.' if info.positive?
418
+ raise LapackError, "the #{-info}-th argument of getrf had illegal value" if info.negative?
419
+ raise LapackError, 'The matrix is singular, and the inverse matrix could not be computed.' if info.positive?
414
420
 
415
421
  a_inv
416
422
  end
@@ -611,7 +617,7 @@ module Numo
611
617
 
612
618
  fnc = :"#{bchr}gerqf"
613
619
  rq, tau, info = Numo::Linalg::Lapack.send(fnc, a.dup)
614
- raise "the #{-info}-th argument of #{fnc} had illegal value" if info.negative?
620
+ raise LapackError, "the #{-info}-th argument of #{fnc} had illegal value" if info.negative?
615
621
 
616
622
  m, n = rq.shape
617
623
  r = rq.triu(n - m).dup
@@ -629,7 +635,7 @@ module Numo
629
635
  end
630
636
 
631
637
  q, info = Numo::Linalg::Lapack.send(fnc, tmp, tau)
632
- raise "the #{-info}-th argument of #{fnc} had illegal value" if info.negative?
638
+ raise LapackError, "the #{-info}-th argument of #{fnc} had illegal value" if info.negative?
633
639
 
634
640
  [r, q]
635
641
  end
@@ -674,8 +680,21 @@ module Numo
674
680
  aa, bb, _alpha, _beta, q, z, _sdim, info = Numo::Linalg::Lapack.send(fnc, a.dup, b.dup)
675
681
  end
676
682
 
677
- raise "the #{-info}-th argument of #{fnc} had illegal value" if info.negative?
678
- raise 'the QZ algorithm failed.' if info.positive?
683
+ n = a.shape[0]
684
+ raise LapackError, "the #{-info}-th argument of #{fnc} had illegal value" if info.negative?
685
+ raise LapackError, 'something other than QZ iteration failed.' if info == n + 1
686
+ raise LapackError, "reordering failed in #{bchr}tgsen" if info == n + 3
687
+
688
+ if info == n + 2
689
+ raise LapackError, 'after reordering, roundoff changed values of some eigenvalues ' \
690
+ 'so that leading eigenvalues in the Generalized Schur form no ' \
691
+ 'longer satisfy the sorting condition.'
692
+ end
693
+
694
+ if info.positive? && info <= n
695
+ warn('the QZ iteration failed. (a, b) are not in Schur form, ' \
696
+ "but alpha[i] and beta[i] for i = #{info},...,n are correct.")
697
+ end
679
698
 
680
699
  [aa, bb, q, z]
681
700
  end
@@ -731,10 +750,15 @@ module Numo
731
750
  end
732
751
 
733
752
  n = a.shape[0]
734
- raise "the #{-info}-th argument of #{fnc} had illegal value" if info.negative?
735
- raise 'the QR algorithm failed to compute all the eigenvalues.' if info.positive? && info <= n
736
- raise 'the eigenvalues could not be reordered.' if info == n + 1
737
- raise 'after reordering, roundoff changed values of some complex eigenvalues.' if info == n + 2
753
+ raise LapackError, "the #{-info}-th argument of #{fnc} had illegal value" if info.negative?
754
+ raise LapackError, 'the QR algorithm failed to compute all the eigenvalues.' if info.positive? && info <= n
755
+ raise LapackError, 'the eigenvalues could not be reordered.' if info == n + 1
756
+
757
+ if info == n + 2
758
+ raise LapackError, 'after reordering, roundoff changed values of some eigenvalues ' \
759
+ 'so that leading eigenvalues in the Schur form no longer satisfy ' \
760
+ 'the sorting condition.'
761
+ end
738
762
 
739
763
  [b, v, sdim]
740
764
  end
@@ -778,12 +802,12 @@ module Numo
778
802
  func = :"#{bchr}gebal"
779
803
  b, ilo, ihi, _, info = Numo::Linalg::Lapack.send(func, a.dup)
780
804
 
781
- raise "the #{-info}-th argument of #{func} had illegal value" if info.negative?
805
+ raise LapackError, "the #{-info}-th argument of #{func} had illegal value" if info.negative?
782
806
 
783
807
  func = :"#{bchr}gehrd"
784
808
  hq, tau, info = Numo::Linalg::Lapack.send(func, b, ilo: ilo, ihi: ihi)
785
809
 
786
- raise "the #{-info}-th argument of #{func} had illegal value" if info.negative?
810
+ raise LapackError, "the #{-info}-th argument of #{func} had illegal value" if info.negative?
787
811
 
788
812
  h = hq.triu(-1)
789
813
  return h unless calc_q
@@ -791,7 +815,7 @@ module Numo
791
815
  func = %w[d s].include?(bchr) ? :"#{bchr}orghr" : :"#{bchr}unghr"
792
816
  q, info = Numo::Linalg::Lapack.send(func, hq, tau, ilo: ilo, ihi: ihi)
793
817
 
794
- raise "the #{-info}-th argument of #{func} had illegal value" if info.negative?
818
+ raise LapackError, "the #{-info}-th argument of #{func} had illegal value" if info.negative?
795
819
 
796
820
  [h, q]
797
821
  end
@@ -830,8 +854,12 @@ module Numo
830
854
 
831
855
  gesv = :"#{bchr}gesv"
832
856
  _lu, x, _ipiv, info = Numo::Linalg::Lapack.send(gesv, a.dup, b.dup)
833
- raise "the #{-info}-th argument of getrf had illegal value" if info.negative?
834
- raise 'the factor U is singular, and the solution could not be computed.' if info.positive?
857
+ raise LapackError, "the #{-info}-th argument of getrf had illegal value" if info.negative?
858
+
859
+ if info.positive?
860
+ warn('the factorization has been completed, but the factor is singular, ' \
861
+ 'so the solution could not be computed.')
862
+ end
835
863
 
836
864
  x
837
865
  end
@@ -870,7 +898,7 @@ module Numo
870
898
  trtrs = :"#{bchr}trtrs"
871
899
  uplo = lower ? 'L' : 'U'
872
900
  x, info = Numo::Linalg::Lapack.send(trtrs, a, b.dup, uplo: uplo)
873
- raise "wrong value is given to the #{info}-th argument of #{trtrs} used internally" if info.negative?
901
+ raise LapackError, "wrong value is given to the #{info}-th argument of #{trtrs} used internally" if info.negative?
874
902
 
875
903
  x
876
904
  end
@@ -926,9 +954,9 @@ module Numo
926
954
  raise ArgumentError, "invalid driver: #{driver}"
927
955
  end
928
956
 
929
- raise "the #{info.abs}-th argument had illegal value" if info.negative?
930
- raise 'input array has a NAN entry' if info == -4
931
- raise 'svd did not converge' if info.positive?
957
+ raise LapackError, "the #{info.abs}-th argument had illegal value" if info.negative?
958
+ raise LapackError, 'the input array has a NAN entry' if info == -4
959
+ raise LapackError, 'the did not converge' if info.positive?
932
960
 
933
961
  [s, u, vt]
934
962
  end
@@ -1015,9 +1043,9 @@ module Numo
1015
1043
  raise ArgumentError, "invalid driver: #{driver}"
1016
1044
  end
1017
1045
 
1018
- raise "the #{info.abs}-th argument had illegal value" if info.negative?
1019
- raise 'input array has a NAN entry' if info == -4
1020
- raise 'svd did not converge' if info.positive?
1046
+ raise LapackError, "the #{info.abs}-th argument had illegal value" if info.negative?
1047
+ raise LapackError, 'the input array has a NAN entry' if info == -4
1048
+ raise LapackError, 'the decomposition did not converge' if info.positive?
1021
1049
 
1022
1050
  s
1023
1051
  end
@@ -1143,8 +1171,12 @@ module Numo
1143
1171
  getrf = :"#{bchr}getrf"
1144
1172
  lu, piv, info = Numo::Linalg::Lapack.send(getrf, a.dup)
1145
1173
 
1146
- raise "the #{info.abs}-th argument of getrf had illegal value" if info.negative?
1147
- raise "the U(#{info}, #{info}) is exactly zero. The factorization has been completed." if info.positive?
1174
+ raise LapackError, "the #{info.abs}-th argument of getrf had illegal value" if info.negative?
1175
+
1176
+ if info.positive?
1177
+ warn("the factorization has been completed, but the factor U[#{info - 1}, #{info - 1}] is " \
1178
+ 'exactly zero, indicating that the matrix is singular.')
1179
+ end
1148
1180
 
1149
1181
  [lu, piv]
1150
1182
  end
@@ -1182,7 +1214,7 @@ module Numo
1182
1214
  getrs = :"#{bchr}getrs"
1183
1215
  x, info = Numo::Linalg::Lapack.send(getrs, lu, ipiv, b.dup)
1184
1216
 
1185
- raise "the #{info.abs}-th argument of getrs had illegal value" if info.negative?
1217
+ raise LapackError, "the #{info.abs}-th argument of getrs had illegal value" if info.negative?
1186
1218
 
1187
1219
  x
1188
1220
  end
@@ -1203,8 +1235,12 @@ module Numo
1203
1235
  fnc = :"#{bchr}potrf"
1204
1236
  c, info = Numo::Linalg::Lapack.send(fnc, a.dup, uplo: uplo)
1205
1237
 
1206
- raise "the #{info}-th leading minor of the array is not positive definite, and the factorization could not be completed." if info.positive?
1207
- raise "the #{-info}-th argument of #{fnc} had illegal value" if info.negative?
1238
+ raise LapackError, "the #{-info}-th argument of #{fnc} had illegal value" if info.negative?
1239
+
1240
+ if info.positive?
1241
+ raise LapackError, "the leading principal minor of order #{info} is not positive, " \
1242
+ 'and the factorization could not be completed.'
1243
+ end
1208
1244
 
1209
1245
  c
1210
1246
  end
@@ -1300,7 +1336,7 @@ module Numo
1300
1336
  fnc = :"#{bchr}gebal"
1301
1337
  b, lo, hi, prm_scl, info = Numo::Linalg::Lapack.send(fnc, a.dup, job: job)
1302
1338
 
1303
- raise "the #{info.abs}-th argument of #{fnc} had illegal value" if info.negative?
1339
+ raise LapackError, "the #{info.abs}-th argument of #{fnc} had illegal value" if info.negative?
1304
1340
 
1305
1341
  # convert from Fortran style index to Ruby style index.
1306
1342
  lo -= 1
@@ -1377,8 +1413,8 @@ module Numo
1377
1413
  wr, wi, vl, vr, info = Numo::Linalg::Lapack.send(fnc, a.dup, jobvl: jobvl, jobvr: jobvr)
1378
1414
  end
1379
1415
 
1380
- raise "the #{info.abs}-th argument of #{fnc} had illegal value" if info.negative?
1381
- raise 'the QR algorithm failed to compute all the eigenvalues.' if info.positive?
1416
+ raise LapackError, "the #{info.abs}-th argument of #{fnc} had illegal value" if info.negative?
1417
+ raise LapackError, 'the QR algorithm failed to compute all the eigenvalues.' if info.positive?
1382
1418
 
1383
1419
  if %w[d s].include?(bchr)
1384
1420
  w = wr + (wi * 1.0i)
@@ -1422,8 +1458,8 @@ module Numo
1422
1458
  w = wr + (wi * 1.0i)
1423
1459
  end
1424
1460
 
1425
- raise "the #{info.abs}-th argument of #{fnc} had illegal value" if info.negative?
1426
- raise 'the QR algorithm failed to compute all the eigenvalues.' if info.positive?
1461
+ raise LapackError, "the #{info.abs}-th argument of #{fnc} had illegal value" if info.negative?
1462
+ raise LapackError, 'the QR algorithm failed to compute all the eigenvalues.' if info.positive?
1427
1463
 
1428
1464
  w
1429
1465
  end
@@ -1473,8 +1509,12 @@ module Numo
1473
1509
  lud = a.dup
1474
1510
  ipiv, info = Numo::Linalg::Lapack.send(fnc, lud, uplo: uplo)
1475
1511
 
1476
- raise "the #{info.abs}-th argument of #{fnc} had illegal value" if info.negative?
1477
- raise 'the factorization has been completed' if info.positive?
1512
+ raise LapackError, "the #{info.abs}-th argument of #{fnc} had illegal value" if info.negative?
1513
+
1514
+ if info.positive?
1515
+ warn("the factorization has been completed, but the D[#{info - 1}, #{info - 1}] is " \
1516
+ 'exactly zero, indicating that the block diagonal matrix is singular.')
1517
+ end
1478
1518
 
1479
1519
  _lud_permutation(lud, ipiv, uplo: uplo, hermitian: hermitian)
1480
1520
  end
@@ -1562,8 +1602,8 @@ module Numo
1562
1602
  fnc = :"#{bchr}gelsd"
1563
1603
  s, rank, info = Numo::Linalg::Lapack.send(fnc, a.dup, x, rcond: rcond)
1564
1604
 
1565
- raise "the #{info.abs}-th argument of #{fnc} had illegal value" if info.negative?
1566
- raise 'the algorithm for computing the SVD failed to converge' if info.positive?
1605
+ raise LapackError, "the #{info.abs}-th argument of #{fnc} had illegal value" if info.negative?
1606
+ raise LapackError, 'the algorithm for computing the SVD failed to converge' if info.positive?
1567
1607
 
1568
1608
  resids = x.class[]
1569
1609
  if m > n
@@ -1765,8 +1805,8 @@ module Numo
1765
1805
  fnc = :"#{bchr}getri"
1766
1806
  inv, info = Numo::Linalg::Lapack.send(fnc, lu.dup, ipiv)
1767
1807
 
1768
- raise "the #{info.abs}-th argument of #{fnc} had illegal value" if info.negative?
1769
- raise 'the matrix is singular and its inverse could not be computed' if info.positive?
1808
+ raise LapackError, "the #{info.abs}-th argument of #{fnc} had illegal value" if info.negative?
1809
+ raise LapackError, 'the matrix is singular and its inverse could not be computed' if info.positive?
1770
1810
 
1771
1811
  inv
1772
1812
  end
@@ -1796,8 +1836,12 @@ module Numo
1796
1836
  fnc = :"#{bchr}potri"
1797
1837
  inv, info = Numo::Linalg::Lapack.send(fnc, a.dup, uplo: uplo)
1798
1838
 
1799
- raise "the #{info.abs}-th argument of #{fnc} had illegal value" if info.negative?
1800
- raise "the (#{info}, #info)-th element of the factor U or L is zero, and the inverse could not be computed." if info.positive?
1839
+ raise LapackError, "the #{info.abs}-th argument of #{fnc} had illegal value" if info.negative?
1840
+
1841
+ if info.positive?
1842
+ raise LapackError, "the (#{info - 1}, #{info - 1})-th element of the factor U or L is zero, " \
1843
+ 'and the inverse could not be computed.'
1844
+ end
1801
1845
 
1802
1846
  inv
1803
1847
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: numo-linalg-alt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - yoshoku
@@ -145,7 +145,7 @@ metadata:
145
145
  homepage_uri: https://github.com/yoshoku/numo-linalg-alt
146
146
  source_code_uri: https://github.com/yoshoku/numo-linalg-alt
147
147
  changelog_uri: https://github.com/yoshoku/numo-linalg-alt/blob/main/CHANGELOG.md
148
- documentation_uri: https://gemdocs.org/gems/numo-linalg-alt/0.6.0/
148
+ documentation_uri: https://gemdocs.org/gems/numo-linalg-alt/0.7.0/
149
149
  rubygems_mfa_required: 'true'
150
150
  rdoc_options: []
151
151
  require_paths:
@@ -161,7 +161,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
161
161
  - !ruby/object:Gem::Version
162
162
  version: '0'
163
163
  requirements: []
164
- rubygems_version: 3.6.9
164
+ rubygems_version: 3.7.2
165
165
  specification_version: 4
166
166
  summary: Numo::Linalg Alternative (numo-linalg-alt) is an alternative to Numo::Linalg.
167
167
  test_files: []