blocksolver 0.8.2__cp38-cp38-win_amd64.whl → 0.8.4__cp38-cp38-win_amd64.whl
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.
- blocksolver/__init__.py +1 -1
- blocksolver/_blqmr.cp38-win_amd64.pyd +0 -0
- blocksolver/blqmr.py +219 -80
- {blocksolver-0.8.2.dist-info → blocksolver-0.8.4.dist-info}/METADATA +1 -1
- blocksolver-0.8.4.dist-info/RECORD +7 -0
- blocksolver-0.8.2.dist-info/RECORD +0 -7
- {blocksolver-0.8.2.dist-info → blocksolver-0.8.4.dist-info}/WHEEL +0 -0
blocksolver/__init__.py
CHANGED
|
Binary file
|
blocksolver/blqmr.py
CHANGED
|
@@ -50,7 +50,7 @@ try:
|
|
|
50
50
|
from numba import njit
|
|
51
51
|
|
|
52
52
|
HAS_NUMBA = True
|
|
53
|
-
except ImportError:
|
|
53
|
+
except (ImportError, Exception) as e:
|
|
54
54
|
HAS_NUMBA = False
|
|
55
55
|
|
|
56
56
|
def njit(*args, **kwargs):
|
|
@@ -437,8 +437,101 @@ class BLQMRWorkspace:
|
|
|
437
437
|
# Preconditioner Factory
|
|
438
438
|
# =============================================================================
|
|
439
439
|
|
|
440
|
+
# Type alias for precond_type
|
|
441
|
+
PrecondType = Optional[Union[str, int]]
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def _parse_precond_type_for_fortran(precond_type: PrecondType) -> int:
|
|
445
|
+
"""
|
|
446
|
+
Convert precond_type to Fortran integer code.
|
|
447
|
+
|
|
448
|
+
Returns
|
|
449
|
+
-------
|
|
450
|
+
int
|
|
451
|
+
0 = no preconditioning
|
|
452
|
+
2 = ILU
|
|
453
|
+
3 = diagonal/Jacobi
|
|
454
|
+
"""
|
|
455
|
+
if precond_type is None or precond_type == "" or precond_type is False:
|
|
456
|
+
return 0
|
|
457
|
+
|
|
458
|
+
if isinstance(precond_type, int):
|
|
459
|
+
return precond_type
|
|
460
|
+
|
|
461
|
+
if isinstance(precond_type, str):
|
|
462
|
+
precond_lower = precond_type.lower()
|
|
463
|
+
if precond_lower in ("ilu", "ilu0", "ilut"):
|
|
464
|
+
return 2
|
|
465
|
+
elif precond_lower in ("diag", "jacobi"):
|
|
466
|
+
return 3
|
|
467
|
+
else:
|
|
468
|
+
# Unknown string, default to no preconditioning
|
|
469
|
+
warnings.warn(
|
|
470
|
+
f"Unknown precond_type '{precond_type}' for Fortran backend, using no preconditioning"
|
|
471
|
+
)
|
|
472
|
+
return 0
|
|
473
|
+
|
|
474
|
+
return 0
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def _get_preconditioner_for_native(A, precond_type: PrecondType, M1_provided):
|
|
478
|
+
"""
|
|
479
|
+
Create preconditioner for native Python backend.
|
|
480
|
+
|
|
481
|
+
Parameters
|
|
482
|
+
----------
|
|
483
|
+
A : sparse matrix
|
|
484
|
+
System matrix
|
|
485
|
+
precond_type : None, '', str, or int
|
|
486
|
+
Preconditioner type specification
|
|
487
|
+
M1_provided : preconditioner or None
|
|
488
|
+
User-provided preconditioner (takes precedence)
|
|
440
489
|
|
|
441
|
-
|
|
490
|
+
Returns
|
|
491
|
+
-------
|
|
492
|
+
M1 : preconditioner or None
|
|
493
|
+
"""
|
|
494
|
+
# If user provided M1, use it
|
|
495
|
+
if M1_provided is not None:
|
|
496
|
+
return M1_provided
|
|
497
|
+
|
|
498
|
+
# No preconditioning requested
|
|
499
|
+
if precond_type is None or precond_type == "" or precond_type is False:
|
|
500
|
+
return None
|
|
501
|
+
|
|
502
|
+
# Integer codes (for compatibility)
|
|
503
|
+
if isinstance(precond_type, int):
|
|
504
|
+
if precond_type == 0:
|
|
505
|
+
return None
|
|
506
|
+
elif precond_type == 2:
|
|
507
|
+
precond_str = "ilu"
|
|
508
|
+
elif precond_type == 3:
|
|
509
|
+
precond_str = "diag"
|
|
510
|
+
else:
|
|
511
|
+
precond_str = "ilu" # Default to ILU for other integers
|
|
512
|
+
else:
|
|
513
|
+
precond_str = precond_type
|
|
514
|
+
|
|
515
|
+
# Create preconditioner
|
|
516
|
+
try:
|
|
517
|
+
return make_preconditioner(A, precond_str)
|
|
518
|
+
except Exception as e:
|
|
519
|
+
# Fallback chain: try diag if ilu fails
|
|
520
|
+
if precond_str not in ("diag", "jacobi"):
|
|
521
|
+
try:
|
|
522
|
+
warnings.warn(
|
|
523
|
+
f"Preconditioner '{precond_str}' failed: {e}, falling back to diagonal"
|
|
524
|
+
)
|
|
525
|
+
return make_preconditioner(A, "diag")
|
|
526
|
+
except Exception:
|
|
527
|
+
pass
|
|
528
|
+
warnings.warn(f"All preconditioners failed, proceeding without preconditioning")
|
|
529
|
+
return None
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
def make_preconditioner(
|
|
533
|
+
A: sparse.spmatrix, precond_type: str = "diag", split: bool = False, **kwargs
|
|
534
|
+
):
|
|
442
535
|
"""
|
|
443
536
|
Create a preconditioner for iterative solvers.
|
|
444
537
|
|
|
@@ -449,25 +542,32 @@ def make_preconditioner(A: sparse.spmatrix, precond_type: str = "diag", **kwargs
|
|
|
449
542
|
precond_type : str
|
|
450
543
|
'diag' or 'jacobi': Diagonal (Jacobi) preconditioner
|
|
451
544
|
'ilu' or 'ilu0': Incomplete LU with minimal fill
|
|
452
|
-
'ilut': Incomplete LU with threshold
|
|
453
|
-
'lu': Full LU factorization
|
|
454
|
-
|
|
545
|
+
'ilut': Incomplete LU with threshold
|
|
546
|
+
'lu': Full LU factorization
|
|
547
|
+
split : bool
|
|
548
|
+
If True, return sqrt(D) for split preconditioning (M1=M2=sqrt(D))
|
|
549
|
+
If False, return D for left preconditioning
|
|
455
550
|
**kwargs : dict
|
|
456
|
-
Additional parameters
|
|
457
|
-
- drop_tol: Drop tolerance (default: 1e-4 for ilut, 0 for ilu0)
|
|
458
|
-
- fill_factor: Fill factor (default: 10 for ilut, 1 for ilu0)
|
|
551
|
+
Additional parameters
|
|
459
552
|
|
|
460
553
|
Returns
|
|
461
554
|
-------
|
|
462
555
|
M : preconditioner object
|
|
463
|
-
|
|
556
|
+
For split Jacobi, use as: blqmr(A, b, M1=M, M2=M)
|
|
464
557
|
"""
|
|
465
558
|
if precond_type in ("diag", "jacobi"):
|
|
466
559
|
diag = A.diagonal().copy()
|
|
467
560
|
diag[np.abs(diag) < 1e-14] = 1.0
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
561
|
+
|
|
562
|
+
if split:
|
|
563
|
+
# For split preconditioning: return sqrt(D)
|
|
564
|
+
# Usage: M1 = M2 = sqrt(D), gives D^{-1/2} A D^{-1/2}
|
|
565
|
+
sqrt_diag = np.sqrt(diag)
|
|
566
|
+
return sparse.diags(sqrt_diag, format="csr")
|
|
567
|
+
else:
|
|
568
|
+
# For left preconditioning: return D
|
|
569
|
+
# Usage: M1 = D, M2 = None, gives D^{-1} A
|
|
570
|
+
return sparse.diags(diag, format="csr")
|
|
471
571
|
|
|
472
572
|
elif precond_type == "ilu0":
|
|
473
573
|
# ILU(0) - no fill-in, fast but may be poor quality
|
|
@@ -569,19 +669,43 @@ def _blqmr_python_impl(
|
|
|
569
669
|
ws = workspace
|
|
570
670
|
ws.reset()
|
|
571
671
|
|
|
572
|
-
# Setup preconditioner
|
|
573
|
-
|
|
672
|
+
# Setup preconditioner - distinguish split vs left-only
|
|
673
|
+
use_split_precond = False
|
|
674
|
+
precond = None
|
|
675
|
+
precond_M1 = None
|
|
676
|
+
precond_M2 = None
|
|
677
|
+
|
|
678
|
+
if M1 is not None and M2 is not None:
|
|
679
|
+
# Split preconditioning: M1⁻¹ A M2⁻¹
|
|
680
|
+
use_split_precond = True
|
|
574
681
|
if isinstance(M1, (_ILUPreconditioner, _LUPreconditioner)):
|
|
575
|
-
|
|
682
|
+
precond_M1 = SparsePreconditioner(M1, None)
|
|
576
683
|
elif sparse.issparse(M1):
|
|
577
|
-
|
|
684
|
+
precond_M1 = SparsePreconditioner(M1, None)
|
|
578
685
|
elif hasattr(M1, "solve"):
|
|
579
|
-
|
|
580
|
-
precond = M1 # Use directly
|
|
686
|
+
precond_M1 = M1
|
|
581
687
|
else:
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
688
|
+
precond_M1 = DensePreconditioner(M1, None)
|
|
689
|
+
|
|
690
|
+
if isinstance(M2, (_ILUPreconditioner, _LUPreconditioner)):
|
|
691
|
+
precond_M2 = SparsePreconditioner(M2, None)
|
|
692
|
+
elif sparse.issparse(M2):
|
|
693
|
+
precond_M2 = SparsePreconditioner(M2, None)
|
|
694
|
+
elif hasattr(M2, "solve"):
|
|
695
|
+
precond_M2 = M2
|
|
696
|
+
else:
|
|
697
|
+
precond_M2 = DensePreconditioner(M2, None)
|
|
698
|
+
|
|
699
|
+
elif M1 is not None:
|
|
700
|
+
# Left-only preconditioning: M1⁻¹ A
|
|
701
|
+
if isinstance(M1, (_ILUPreconditioner, _LUPreconditioner)):
|
|
702
|
+
precond = SparsePreconditioner(M1, None)
|
|
703
|
+
elif sparse.issparse(M1):
|
|
704
|
+
precond = SparsePreconditioner(M1, None)
|
|
705
|
+
elif hasattr(M1, "solve"):
|
|
706
|
+
precond = M1
|
|
707
|
+
else:
|
|
708
|
+
precond = DensePreconditioner(M1, None)
|
|
585
709
|
|
|
586
710
|
if x0 is None:
|
|
587
711
|
x = np.zeros((n, m), dtype=dtype)
|
|
@@ -608,7 +732,14 @@ def _blqmr_python_impl(
|
|
|
608
732
|
else:
|
|
609
733
|
np.subtract(B, A @ x, out=ws.vt)
|
|
610
734
|
|
|
611
|
-
|
|
735
|
+
# Apply preconditioner to initial residual
|
|
736
|
+
if use_split_precond:
|
|
737
|
+
# For split preconditioning, initial residual is just M1⁻¹ * (b - A*x0)
|
|
738
|
+
# because we're solving M1⁻¹ A M2⁻¹ y = M1⁻¹ b with y = M2*x
|
|
739
|
+
ws.vt[:] = precond_M1.solve(ws.vt)
|
|
740
|
+
if np.any(np.isnan(ws.vt)):
|
|
741
|
+
return x, 2, 1.0, 0, np.array([])
|
|
742
|
+
elif precond is not None:
|
|
612
743
|
precond.solve(ws.vt, out=ws.vt)
|
|
613
744
|
if np.any(np.isnan(ws.vt)):
|
|
614
745
|
return x, 2, 1.0, 0, np.array([])
|
|
@@ -667,7 +798,16 @@ def _blqmr_python_impl(
|
|
|
667
798
|
np.matmul(A, ws.v[:, :, t3], out=ws.Av)
|
|
668
799
|
|
|
669
800
|
# Apply preconditioner
|
|
670
|
-
if
|
|
801
|
+
if use_split_precond:
|
|
802
|
+
# Split preconditioning: M1⁻¹ * A * M2⁻¹ * v
|
|
803
|
+
tmp = precond_M2.solve(ws.v[:, :, t3]) # M2⁻¹ * v
|
|
804
|
+
if A_is_sparse:
|
|
805
|
+
tmp = A @ tmp # A * M2⁻¹ * v
|
|
806
|
+
else:
|
|
807
|
+
tmp = np.matmul(A, tmp)
|
|
808
|
+
ws.vt[:] = precond_M1.solve(tmp) - ws.v[:, :, t3n] @ ws.beta[:, :, t3].T
|
|
809
|
+
elif precond is not None:
|
|
810
|
+
# Left-only preconditioning: M⁻¹ * A * v
|
|
671
811
|
precond.solve(ws.Av, out=ws.vt)
|
|
672
812
|
ws.vt[:] = ws.vt - ws.v[:, :, t3n] @ ws.beta[:, :, t3].T
|
|
673
813
|
else:
|
|
@@ -768,6 +908,11 @@ def _blqmr_python_impl(
|
|
|
768
908
|
break
|
|
769
909
|
|
|
770
910
|
resv = resv[:iter_count]
|
|
911
|
+
|
|
912
|
+
# For split preconditioning, recover x = M2⁻¹ * y
|
|
913
|
+
if use_split_precond:
|
|
914
|
+
x = precond_M2.solve(x)
|
|
915
|
+
|
|
771
916
|
result = x.real if not is_complex_input else x
|
|
772
917
|
return result, flag, relres, iter_count, resv
|
|
773
918
|
|
|
@@ -787,7 +932,7 @@ def blqmr_solve(
|
|
|
787
932
|
tol: float = 1e-6,
|
|
788
933
|
maxiter: Optional[int] = None,
|
|
789
934
|
droptol: float = 0.001,
|
|
790
|
-
|
|
935
|
+
precond_type: PrecondType = "ilu",
|
|
791
936
|
zero_based: bool = True,
|
|
792
937
|
) -> BLQMRResult:
|
|
793
938
|
"""
|
|
@@ -813,8 +958,12 @@ def blqmr_solve(
|
|
|
813
958
|
Maximum iterations. Default is n.
|
|
814
959
|
droptol : float, default 0.001
|
|
815
960
|
Drop tolerance for ILU preconditioner (Fortran only).
|
|
816
|
-
|
|
817
|
-
|
|
961
|
+
precond_type : None, '', or str, default 'ilu'
|
|
962
|
+
Preconditioner type:
|
|
963
|
+
- None or '': No preconditioning
|
|
964
|
+
- 'ilu', 'ilu0', 'ilut': Incomplete LU
|
|
965
|
+
- 'diag', 'jacobi': Diagonal (Jacobi)
|
|
966
|
+
- For Fortran: integers 2 (ILU) or 3 (diagonal) also accepted
|
|
818
967
|
zero_based : bool, default True
|
|
819
968
|
If True, Ap and Ai use 0-based indexing (Python/C convention).
|
|
820
969
|
If False, uses 1-based indexing (Fortran convention).
|
|
@@ -839,7 +988,7 @@ def blqmr_solve(
|
|
|
839
988
|
tol=tol,
|
|
840
989
|
maxiter=maxiter,
|
|
841
990
|
droptol=droptol,
|
|
842
|
-
|
|
991
|
+
precond_type=precond_type,
|
|
843
992
|
zero_based=zero_based,
|
|
844
993
|
)
|
|
845
994
|
else:
|
|
@@ -851,13 +1000,13 @@ def blqmr_solve(
|
|
|
851
1000
|
x0=x0,
|
|
852
1001
|
tol=tol,
|
|
853
1002
|
maxiter=maxiter,
|
|
854
|
-
|
|
1003
|
+
precond_type=precond_type,
|
|
855
1004
|
zero_based=zero_based,
|
|
856
1005
|
)
|
|
857
1006
|
|
|
858
1007
|
|
|
859
1008
|
def _blqmr_solve_fortran(
|
|
860
|
-
Ap, Ai, Ax, b, *, x0, tol, maxiter, droptol,
|
|
1009
|
+
Ap, Ai, Ax, b, *, x0, tol, maxiter, droptol, precond_type, zero_based
|
|
861
1010
|
) -> BLQMRResult:
|
|
862
1011
|
"""Fortran backend for blqmr_solve."""
|
|
863
1012
|
n = len(Ap) - 1
|
|
@@ -877,10 +1026,10 @@ def _blqmr_solve_fortran(
|
|
|
877
1026
|
Ap = Ap + 1
|
|
878
1027
|
Ai = Ai + 1
|
|
879
1028
|
|
|
880
|
-
|
|
1029
|
+
pcond_type = _parse_precond_type_for_fortran(precond_type)
|
|
881
1030
|
|
|
882
1031
|
x, flag, niter, relres = _blqmr.blqmr_solve_real(
|
|
883
|
-
n, nnz, Ap, Ai, Ax, b, maxiter, tol, droptol,
|
|
1032
|
+
n, nnz, Ap, Ai, Ax, b, maxiter, tol, droptol, pcond_type
|
|
884
1033
|
)
|
|
885
1034
|
|
|
886
1035
|
return BLQMRResult(
|
|
@@ -889,7 +1038,7 @@ def _blqmr_solve_fortran(
|
|
|
889
1038
|
|
|
890
1039
|
|
|
891
1040
|
def _blqmr_solve_native_csc(
|
|
892
|
-
Ap, Ai, Ax, b, *, x0, tol, maxiter,
|
|
1041
|
+
Ap, Ai, Ax, b, *, x0, tol, maxiter, precond_type, zero_based
|
|
893
1042
|
) -> BLQMRResult:
|
|
894
1043
|
"""Native Python backend for blqmr_solve with CSC input."""
|
|
895
1044
|
n = len(Ap) - 1
|
|
@@ -900,15 +1049,7 @@ def _blqmr_solve_native_csc(
|
|
|
900
1049
|
|
|
901
1050
|
A = sparse.csc_matrix((Ax, Ai, Ap), shape=(n, n))
|
|
902
1051
|
|
|
903
|
-
M1 = None
|
|
904
|
-
if use_precond:
|
|
905
|
-
try:
|
|
906
|
-
M1 = make_preconditioner(A, "ilu")
|
|
907
|
-
except Exception:
|
|
908
|
-
try:
|
|
909
|
-
M1 = make_preconditioner(A, "diag") # FIX: Changed A_sp to A
|
|
910
|
-
except Exception:
|
|
911
|
-
M1 = None # Fall back to no preconditioning
|
|
1052
|
+
M1 = _get_preconditioner_for_native(A, precond_type, None)
|
|
912
1053
|
|
|
913
1054
|
x, flag, relres, niter, resv = _blqmr_python_impl(
|
|
914
1055
|
A, b, tol=tol, maxiter=maxiter, M1=M1, x0=x0
|
|
@@ -929,13 +1070,18 @@ def blqmr_solve_multi(
|
|
|
929
1070
|
tol: float = 1e-6,
|
|
930
1071
|
maxiter: Optional[int] = None,
|
|
931
1072
|
droptol: float = 0.001,
|
|
932
|
-
|
|
1073
|
+
precond_type: PrecondType = "ilu",
|
|
933
1074
|
zero_based: bool = True,
|
|
934
1075
|
) -> BLQMRResult:
|
|
935
1076
|
"""
|
|
936
1077
|
Solve sparse linear system AX = B with multiple right-hand sides.
|
|
937
1078
|
|
|
938
1079
|
Uses Fortran extension if available, otherwise falls back to pure Python.
|
|
1080
|
+
|
|
1081
|
+
Parameters
|
|
1082
|
+
----------
|
|
1083
|
+
precond_type : None, '', or str, default 'ilu'
|
|
1084
|
+
Preconditioner type (see blqmr_solve for details)
|
|
939
1085
|
"""
|
|
940
1086
|
n = len(Ap) - 1
|
|
941
1087
|
|
|
@@ -951,7 +1097,7 @@ def blqmr_solve_multi(
|
|
|
951
1097
|
tol=tol,
|
|
952
1098
|
maxiter=maxiter,
|
|
953
1099
|
droptol=droptol,
|
|
954
|
-
|
|
1100
|
+
precond_type=precond_type,
|
|
955
1101
|
zero_based=zero_based,
|
|
956
1102
|
)
|
|
957
1103
|
else:
|
|
@@ -962,13 +1108,13 @@ def blqmr_solve_multi(
|
|
|
962
1108
|
B,
|
|
963
1109
|
tol=tol,
|
|
964
1110
|
maxiter=maxiter,
|
|
965
|
-
|
|
1111
|
+
precond_type=precond_type,
|
|
966
1112
|
zero_based=zero_based,
|
|
967
1113
|
)
|
|
968
1114
|
|
|
969
1115
|
|
|
970
1116
|
def _blqmr_solve_multi_fortran(
|
|
971
|
-
Ap, Ai, Ax, B, *, tol, maxiter, droptol,
|
|
1117
|
+
Ap, Ai, Ax, B, *, tol, maxiter, droptol, precond_type, zero_based
|
|
972
1118
|
) -> BLQMRResult:
|
|
973
1119
|
"""Fortran backend for blqmr_solve_multi."""
|
|
974
1120
|
n = len(Ap) - 1
|
|
@@ -987,10 +1133,11 @@ def _blqmr_solve_multi_fortran(
|
|
|
987
1133
|
Ap = Ap + 1
|
|
988
1134
|
Ai = Ai + 1
|
|
989
1135
|
|
|
990
|
-
|
|
1136
|
+
# Convert precond_type string to Fortran integer code
|
|
1137
|
+
pcond_type = _parse_precond_type_for_fortran(precond_type)
|
|
991
1138
|
|
|
992
1139
|
X, flag, niter, relres = _blqmr.blqmr_solve_real_multi(
|
|
993
|
-
n, nnz, nrhs, Ap, Ai, Ax, B, maxiter, tol, droptol,
|
|
1140
|
+
n, nnz, nrhs, Ap, Ai, Ax, B, maxiter, tol, droptol, pcond_type
|
|
994
1141
|
)
|
|
995
1142
|
|
|
996
1143
|
return BLQMRResult(
|
|
@@ -999,7 +1146,7 @@ def _blqmr_solve_multi_fortran(
|
|
|
999
1146
|
|
|
1000
1147
|
|
|
1001
1148
|
def _blqmr_solve_multi_native(
|
|
1002
|
-
Ap, Ai, Ax, B, *, tol, maxiter,
|
|
1149
|
+
Ap, Ai, Ax, B, *, tol, maxiter, precond_type, zero_based
|
|
1003
1150
|
) -> BLQMRResult:
|
|
1004
1151
|
"""Native Python backend for blqmr_solve_multi."""
|
|
1005
1152
|
n = len(Ap) - 1
|
|
@@ -1010,15 +1157,7 @@ def _blqmr_solve_multi_native(
|
|
|
1010
1157
|
|
|
1011
1158
|
A = sparse.csc_matrix((Ax, Ai, Ap), shape=(n, n))
|
|
1012
1159
|
|
|
1013
|
-
M1 = None
|
|
1014
|
-
if use_precond:
|
|
1015
|
-
try:
|
|
1016
|
-
M1 = make_preconditioner(A, "ilu")
|
|
1017
|
-
except Exception:
|
|
1018
|
-
try:
|
|
1019
|
-
M1 = make_preconditioner(A, "diag") # FIX: Changed A_sp to A
|
|
1020
|
-
except Exception:
|
|
1021
|
-
M1 = None # Fall back to no preconditioning
|
|
1160
|
+
M1 = _get_preconditioner_for_native(A, precond_type, None)
|
|
1022
1161
|
|
|
1023
1162
|
if B.ndim == 1:
|
|
1024
1163
|
B = B.reshape(-1, 1)
|
|
@@ -1081,7 +1220,7 @@ def blqmr(
|
|
|
1081
1220
|
residual: bool = False,
|
|
1082
1221
|
workspace: Optional[BLQMRWorkspace] = None,
|
|
1083
1222
|
droptol: float = 0.001,
|
|
1084
|
-
|
|
1223
|
+
precond_type: PrecondType = "ilu",
|
|
1085
1224
|
) -> BLQMRResult:
|
|
1086
1225
|
"""
|
|
1087
1226
|
Block Quasi-Minimal-Residual (BL-QMR) solver - main interface.
|
|
@@ -1097,9 +1236,10 @@ def blqmr(
|
|
|
1097
1236
|
tol : float
|
|
1098
1237
|
Convergence tolerance (default: 1e-6)
|
|
1099
1238
|
maxiter : int, optional
|
|
1100
|
-
Maximum iterations (default: n
|
|
1239
|
+
Maximum iterations (default: n)
|
|
1101
1240
|
M1, M2 : preconditioner, optional
|
|
1102
|
-
|
|
1241
|
+
Custom preconditioners. If provided, precond_type is ignored.
|
|
1242
|
+
M = M1 @ M2 for split preconditioning (Python backend only)
|
|
1103
1243
|
x0 : ndarray, optional
|
|
1104
1244
|
Initial guess
|
|
1105
1245
|
residual : bool
|
|
@@ -1108,8 +1248,13 @@ def blqmr(
|
|
|
1108
1248
|
Pre-allocated workspace (Python backend only)
|
|
1109
1249
|
droptol : float, default 0.001
|
|
1110
1250
|
Drop tolerance for ILU preconditioner (Fortran backend only)
|
|
1111
|
-
|
|
1112
|
-
|
|
1251
|
+
precond_type : None, '', or str, default 'ilu'
|
|
1252
|
+
Preconditioner type (ignored if M1 is provided):
|
|
1253
|
+
- None or '': No preconditioning
|
|
1254
|
+
- 'ilu', 'ilu0', 'ilut': Incomplete LU
|
|
1255
|
+
- 'diag', 'jacobi': Diagonal (Jacobi)
|
|
1256
|
+
- 'lu': Full LU (expensive, for debugging)
|
|
1257
|
+
- For Fortran: integers 2 (ILU) or 3 (diagonal) also accepted
|
|
1113
1258
|
|
|
1114
1259
|
Returns
|
|
1115
1260
|
-------
|
|
@@ -1129,7 +1274,7 @@ def blqmr(
|
|
|
1129
1274
|
maxiter=maxiter,
|
|
1130
1275
|
x0=x0,
|
|
1131
1276
|
droptol=droptol,
|
|
1132
|
-
|
|
1277
|
+
precond_type=precond_type,
|
|
1133
1278
|
)
|
|
1134
1279
|
else:
|
|
1135
1280
|
return _blqmr_native(
|
|
@@ -1142,7 +1287,7 @@ def blqmr(
|
|
|
1142
1287
|
x0=x0,
|
|
1143
1288
|
residual=residual,
|
|
1144
1289
|
workspace=workspace,
|
|
1145
|
-
|
|
1290
|
+
precond_type=precond_type,
|
|
1146
1291
|
)
|
|
1147
1292
|
|
|
1148
1293
|
|
|
@@ -1154,7 +1299,7 @@ def _blqmr_fortran(
|
|
|
1154
1299
|
maxiter: Optional[int],
|
|
1155
1300
|
x0: Optional[np.ndarray],
|
|
1156
1301
|
droptol: float,
|
|
1157
|
-
|
|
1302
|
+
precond_type: PrecondType,
|
|
1158
1303
|
) -> BLQMRResult:
|
|
1159
1304
|
"""Fortran backend for blqmr()."""
|
|
1160
1305
|
A_csc = sparse.csc_matrix(A)
|
|
@@ -1176,7 +1321,7 @@ def _blqmr_fortran(
|
|
|
1176
1321
|
Ap_f = np.asfortranarray(Ap + 1, dtype=np.int32)
|
|
1177
1322
|
Ai_f = np.asfortranarray(Ai + 1, dtype=np.int32)
|
|
1178
1323
|
|
|
1179
|
-
|
|
1324
|
+
pcond_type = _parse_precond_type_for_fortran(precond_type)
|
|
1180
1325
|
|
|
1181
1326
|
# Check if complex
|
|
1182
1327
|
is_complex = np.iscomplexobj(A) or np.iscomplexobj(B)
|
|
@@ -1189,7 +1334,7 @@ def _blqmr_fortran(
|
|
|
1189
1334
|
# Single RHS
|
|
1190
1335
|
b_f = np.asfortranarray(B.ravel(), dtype=np.complex128)
|
|
1191
1336
|
x, flag, niter, relres = _blqmr.blqmr_solve_complex(
|
|
1192
|
-
n, nnz, Ap_f, Ai_f, Ax_f, b_f, maxiter, tol, droptol,
|
|
1337
|
+
n, nnz, Ap_f, Ai_f, Ax_f, b_f, maxiter, tol, droptol, pcond_type
|
|
1193
1338
|
)
|
|
1194
1339
|
return BLQMRResult(
|
|
1195
1340
|
x=x.copy(), flag=int(flag), iter=int(niter), relres=float(relres)
|
|
@@ -1199,7 +1344,7 @@ def _blqmr_fortran(
|
|
|
1199
1344
|
B_f = np.asfortranarray(B, dtype=np.complex128)
|
|
1200
1345
|
nrhs = B_f.shape[1]
|
|
1201
1346
|
X, flag, niter, relres = _blqmr.blqmr_solve_complex_multi(
|
|
1202
|
-
n, nnz, nrhs, Ap_f, Ai_f, Ax_f, B_f, maxiter, tol, droptol,
|
|
1347
|
+
n, nnz, nrhs, Ap_f, Ai_f, Ax_f, B_f, maxiter, tol, droptol, pcond_type
|
|
1203
1348
|
)
|
|
1204
1349
|
return BLQMRResult(
|
|
1205
1350
|
x=X.copy(), flag=int(flag), iter=int(niter), relres=float(relres)
|
|
@@ -1212,7 +1357,7 @@ def _blqmr_fortran(
|
|
|
1212
1357
|
# Single RHS
|
|
1213
1358
|
b_f = np.asfortranarray(B.ravel(), dtype=np.float64)
|
|
1214
1359
|
x, flag, niter, relres = _blqmr.blqmr_solve_real(
|
|
1215
|
-
n, nnz, Ap_f, Ai_f, Ax_f, b_f, maxiter, tol, droptol,
|
|
1360
|
+
n, nnz, Ap_f, Ai_f, Ax_f, b_f, maxiter, tol, droptol, pcond_type
|
|
1216
1361
|
)
|
|
1217
1362
|
return BLQMRResult(
|
|
1218
1363
|
x=x.copy(), flag=int(flag), iter=int(niter), relres=float(relres)
|
|
@@ -1222,7 +1367,7 @@ def _blqmr_fortran(
|
|
|
1222
1367
|
B_f = np.asfortranarray(B, dtype=np.float64)
|
|
1223
1368
|
nrhs = B_f.shape[1]
|
|
1224
1369
|
X, flag, niter, relres = _blqmr.blqmr_solve_real_multi(
|
|
1225
|
-
n, nnz, nrhs, Ap_f, Ai_f, Ax_f, B_f, maxiter, tol, droptol,
|
|
1370
|
+
n, nnz, nrhs, Ap_f, Ai_f, Ax_f, B_f, maxiter, tol, droptol, pcond_type
|
|
1226
1371
|
)
|
|
1227
1372
|
return BLQMRResult(
|
|
1228
1373
|
x=X.copy(), flag=int(flag), iter=int(niter), relres=float(relres)
|
|
@@ -1240,19 +1385,13 @@ def _blqmr_native(
|
|
|
1240
1385
|
x0: Optional[np.ndarray],
|
|
1241
1386
|
residual: bool,
|
|
1242
1387
|
workspace: Optional[BLQMRWorkspace],
|
|
1243
|
-
|
|
1388
|
+
precond_type: PrecondType,
|
|
1244
1389
|
) -> BLQMRResult:
|
|
1245
1390
|
"""Native Python backend for blqmr()."""
|
|
1246
|
-
#
|
|
1247
|
-
if
|
|
1391
|
+
# Get preconditioner (user-provided M1 takes precedence)
|
|
1392
|
+
if M1 is None:
|
|
1248
1393
|
A_sp = sparse.csc_matrix(A) if not sparse.issparse(A) else A
|
|
1249
|
-
|
|
1250
|
-
M1 = make_preconditioner(A_sp, "ilu")
|
|
1251
|
-
except Exception:
|
|
1252
|
-
try:
|
|
1253
|
-
M1 = make_preconditioner(A_sp, "diag")
|
|
1254
|
-
except Exception:
|
|
1255
|
-
M1 = None # Fall back to no preconditioning
|
|
1394
|
+
M1 = _get_preconditioner_for_native(A_sp, precond_type, None)
|
|
1256
1395
|
|
|
1257
1396
|
x, flag, relres, niter, resv = _blqmr_python_impl(
|
|
1258
1397
|
A,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
blocksolver-0.8.4.dist-info/METADATA,sha256=4s5MWTlfx-pnVrt5KaalrAVPmCflegDVOGiWnx5L23Y,13264
|
|
2
|
+
blocksolver-0.8.4.dist-info/WHEEL,sha256=vIXzP6jLUy4sdmrQppnovVBqmdfNCkEM0I7EHxeJ-zs,83
|
|
3
|
+
blocksolver/_blqmr.cp38-win_amd64.pyd,sha256=qgCVpkqH8GQ7JTreyXfmVEYlmzkfxBX1075cRLggRKM,34373446
|
|
4
|
+
blocksolver/_blqmr.cp38-win_amd64.dll.a,sha256=Nketjx2gg0CTuKGh2_z6lckLN0D1HR1uPudzY66SFOs,1696
|
|
5
|
+
blocksolver/__init__.py,sha256=aC1Iq40CVJ_GhNLSnjnXaOeioHZV8hHw1yy349pF3VU,1982
|
|
6
|
+
blocksolver/blqmr.py,sha256=mrJ_ze4lYxS6uV4XGF66f1NhcwhYg6p3OERWXOGaTTU,46057
|
|
7
|
+
blocksolver-0.8.4.dist-info/RECORD,,
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
blocksolver-0.8.2.dist-info/METADATA,sha256=v05b9IAmx9fbUkF3hB6OmY354Bnt-uzv4FNqJmXRhuM,13264
|
|
2
|
-
blocksolver-0.8.2.dist-info/WHEEL,sha256=vIXzP6jLUy4sdmrQppnovVBqmdfNCkEM0I7EHxeJ-zs,83
|
|
3
|
-
blocksolver/_blqmr.cp38-win_amd64.pyd,sha256=Ag1cU0yKJo_lYm4UYD5AObybJZJAOpMJyRNXaXMdB1k,34345539
|
|
4
|
-
blocksolver/_blqmr.cp38-win_amd64.dll.a,sha256=Nketjx2gg0CTuKGh2_z6lckLN0D1HR1uPudzY66SFOs,1696
|
|
5
|
-
blocksolver/__init__.py,sha256=aJUyjo4lOmok718swroWNLPFctuyfGzxdmPBJnpu_00,1982
|
|
6
|
-
blocksolver/blqmr.py,sha256=kpkl32V8bWRGKtc8UOLoAor2yFzLryYcH_6zq02KpHo,41042
|
|
7
|
-
blocksolver-0.8.2.dist-info/RECORD,,
|
|
File without changes
|