blocksolver 0.8.5__cp310-cp310-macosx_14_0_arm64.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.
@@ -0,0 +1,504 @@
1
+ Metadata-Version: 2.1
2
+ Name: blocksolver
3
+ Version: 0.8.5
4
+ Summary: Block Quasi-Minimal-Residual sparse linear solver
5
+ Keywords: sparse,linear-algebra,iterative-solver,qmr,fortran,umfpack
6
+ Author-Email: Qianqian Fang <q.fang@neu.edu>
7
+ License: BSD-3-Clause OR LGPL-3.0-or-later OR GPL-3.0-or-later
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Science/Research
10
+ Classifier: License :: OSI Approved :: BSD License
11
+ Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
12
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Operating System :: Microsoft :: Windows
17
+ Classifier: Programming Language :: Fortran
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.8
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
26
+ Project-URL: Homepage, https://blit.sourceforge.net
27
+ Project-URL: Repository, https://github.com/fangq/blocksolver
28
+ Project-URL: Documentation, https://blit.sourceforge.net
29
+ Project-URL: Bug Tracker, https://github.com/fangq/blocksolver/issues
30
+ Requires-Python: >=3.8
31
+ Requires-Dist: numpy>=1.20
32
+ Requires-Dist: scipy>=1.0
33
+ Provides-Extra: fast
34
+ Requires-Dist: numba>=0.50; extra == "fast"
35
+ Provides-Extra: test
36
+ Requires-Dist: pytest>=6.0; extra == "test"
37
+ Provides-Extra: dev
38
+ Requires-Dist: pytest>=6.0; extra == "dev"
39
+ Requires-Dist: build; extra == "dev"
40
+ Requires-Dist: twine; extra == "dev"
41
+ Description-Content-Type: text/markdown
42
+
43
+ # BlockSolver - Block Quasi-Minimal Residual (BLQMR) Sparse Linear Solver
44
+
45
+ **BlockSolver** is a Python package for solving large sparse linear systems using the Block Quasi-Minimal Residual (BLQMR) algorithm. It provides both a high-performance Fortran backend and a pure Python/NumPy implementation for maximum portability.
46
+
47
+ ## Features
48
+
49
+ - **Block QMR Algorithm**: Efficiently solves multiple right-hand sides simultaneously
50
+ - **Complex Symmetric Support**: Designed for complex symmetric matrices (A = Aᵀ, not A = A†)
51
+ - **Dual Backend**: Fortran extension for speed, Python fallback for portability
52
+ - **Flexible Preconditioning**: ILU, diagonal (Jacobi), and split preconditioners
53
+ - **SciPy Integration**: Works seamlessly with SciPy sparse matrices
54
+ - **Optional Numba Acceleration**: JIT-compiled kernels for the Python backend
55
+
56
+ ## Algorithm
57
+
58
+ ### Block Quasi-Minimal Residual (BLQMR)
59
+
60
+ The BLQMR algorithm is an iterative Krylov subspace method specifically designed for:
61
+
62
+ 1. **Complex symmetric systems**: Unlike standard methods that assume Hermitian (A = A†) or general matrices, BLQMR exploits complex symmetry (A = Aᵀ) which arises in electromagnetics, acoustics, and diffuse optical tomography.
63
+
64
+ 2. **Multiple right-hand sides**: Instead of solving each system independently, BLQMR processes all right-hand sides together in a block fashion, sharing Krylov subspace information and reducing total computation.
65
+
66
+ 3. **Quasi-minimal residual**: The algorithm minimizes a quasi-residual norm at each iteration, providing smooth convergence without the erratic behavior of some Krylov methods.
67
+
68
+ ### Key Components
69
+
70
+ - **Quasi-QR Decomposition**: A modified Gram-Schmidt process using the quasi inner product ⟨x,y⟩ = Σ xₖyₖ (without conjugation) for complex symmetric systems.
71
+
72
+ - **Three-term Lanczos Recurrence**: Builds an orthonormal basis for the Krylov subspace with short recurrences, minimizing memory usage.
73
+
74
+ - **Block Updates**: Processes m right-hand sides simultaneously, with typical block sizes of 1-64.
75
+
76
+ ### When to Use BLQMR
77
+
78
+ | Use Case | Recommendation |
79
+ |----------|----------------|
80
+ | Complex symmetric matrix (A = Aᵀ) | ✅ Ideal |
81
+ | Multiple right-hand sides | ✅ Ideal |
82
+ | Real symmetric positive definite | Consider CG first |
83
+ | General non-symmetric | Consider GMRES or BiCGSTAB |
84
+ | Very large systems (>10⁶ unknowns) | ✅ Good with preconditioning |
85
+
86
+ ## Installation
87
+
88
+ ### From PyPI
89
+
90
+ ```bash
91
+ pip install blocksolver
92
+ ```
93
+
94
+ ### From Source
95
+
96
+ Prerequisites:
97
+ - Python ≥ 3.8
98
+ - NumPy ≥ 1.20
99
+ - SciPy ≥ 1.0
100
+ - (Optional) Fortran compiler + UMFPACK for the accelerated backend
101
+ - (Optional) Numba for accelerated Python backend
102
+
103
+ ```bash
104
+ # Ubuntu/Debian
105
+ sudo apt install gfortran libsuitesparse-dev libblas-dev liblapack-dev
106
+
107
+ # macOS
108
+ brew install gcc suite-sparse openblas
109
+
110
+ # Install
111
+ cd python
112
+ pip install .
113
+ ```
114
+
115
+ ## Quick Start
116
+
117
+ ```python
118
+ import numpy as np
119
+ from scipy.sparse import csc_matrix
120
+ from blocksolver import blqmr
121
+
122
+ # Create a sparse matrix
123
+ A = csc_matrix([
124
+ [4, 1, 0, 0],
125
+ [1, 4, 1, 0],
126
+ [0, 1, 4, 1],
127
+ [0, 0, 1, 4]
128
+ ], dtype=float)
129
+
130
+ b = np.array([1., 2., 3., 4.])
131
+
132
+ # Solve Ax = b
133
+ result = blqmr(A, b, tol=1e-10)
134
+
135
+ print(f"Solution: {result.x}")
136
+ print(f"Converged: {result.converged}")
137
+ print(f"Iterations: {result.iter}")
138
+ print(f"Relative residual: {result.relres:.2e}")
139
+ ```
140
+
141
+ ## Usage
142
+
143
+ ### Main Interface: `blqmr()`
144
+
145
+ The primary function `blqmr()` automatically selects the best available backend (Fortran if available, otherwise Python).
146
+
147
+ ```python
148
+ from blocksolver import blqmr, BLQMR_EXT
149
+
150
+ # Check which backend is active
151
+ print(f"Using Fortran backend: {BLQMR_EXT}")
152
+
153
+ # Basic usage
154
+ result = blqmr(A, b)
155
+
156
+ # With options
157
+ result = blqmr(A, b,
158
+ tol=1e-8, # Convergence tolerance
159
+ maxiter=1000, # Maximum iterations
160
+ precond_type='ilu', # Preconditioner: 'ilu', 'diag', or None
161
+ )
162
+ ```
163
+
164
+ ### Multiple Right-Hand Sides
165
+
166
+ BLQMR excels when solving the same system with multiple right-hand sides:
167
+
168
+ ```python
169
+ import numpy as np
170
+ from blocksolver import blqmr
171
+
172
+ # 100 different right-hand sides
173
+ B = np.random.randn(n, 100)
174
+
175
+ # Solve all systems at once (much faster than solving individually)
176
+ result = blqmr(A, B, tol=1e-8)
177
+
178
+ # result.x has shape (n, 100)
179
+ ```
180
+
181
+ ### Complex Symmetric Systems
182
+
183
+ BLQMR is specifically designed for complex symmetric matrices (common in frequency-domain wave problems):
184
+
185
+ ```python
186
+ import numpy as np
187
+ from blocksolver import blqmr
188
+
189
+ # Complex symmetric matrix (A = A.T, NOT A.conj().T)
190
+ A = create_helmholtz_matrix(frequency=1000) # Your application
191
+ b = np.complex128(source_term)
192
+
193
+ result = blqmr(A, b, tol=1e-8, precond_type='diag')
194
+ ```
195
+
196
+ ### Preconditioning
197
+
198
+ BlockSolver supports multiple preconditioner types for both backends:
199
+
200
+ ```python
201
+ from blocksolver import blqmr, make_preconditioner
202
+
203
+ # Using precond_type parameter (works with both backends)
204
+ result = blqmr(A, b, precond_type='ilu') # Incomplete LU
205
+ result = blqmr(A, b, precond_type='diag') # Diagonal (Jacobi)
206
+ result = blqmr(A, b, precond_type=None) # No preconditioning
207
+
208
+ # Custom preconditioner (Python backend only)
209
+ M1 = make_preconditioner(A, 'ilu', drop_tol=1e-4, fill_factor=10)
210
+ result = blqmr(A, b, M1=M1, precond_type=None)
211
+
212
+ # Split preconditioning for symmetric systems (Python backend)
213
+ # Preserves symmetry: M1^{-1} A M2^{-1}
214
+ M = make_preconditioner(A, 'diag', split=True) # Returns sqrt(D)
215
+ result = blqmr(A, b, M1=M, M2=M, precond_type=None)
216
+ ```
217
+
218
+ ### SciPy-Compatible Interface
219
+
220
+ For drop-in replacement in existing code:
221
+
222
+ ```python
223
+ from blocksolver import blqmr_scipy
224
+
225
+ # Returns (x, flag) like scipy.sparse.linalg solvers
226
+ x, flag = blqmr_scipy(A, b, tol=1e-10)
227
+ ```
228
+
229
+ ### Low-Level CSC Interface
230
+
231
+ For maximum control, use the CSC component interface:
232
+
233
+ ```python
234
+ from blocksolver import blqmr_solve
235
+
236
+ # CSC format components (0-based indexing)
237
+ Ap = np.array([0, 2, 5, 9, 10, 12], dtype=np.int32) # Column pointers
238
+ Ai = np.array([0, 1, 0, 2, 4, 1, 2, 3, 4, 2, 1, 4], dtype=np.int32) # Row indices
239
+ Ax = np.array([2., 3., 3., -1., 4., 4., -3., 1., 2., 2., 6., 1.]) # Values
240
+ b = np.array([8., 45., -3., 3., 19.])
241
+
242
+ result = blqmr_solve(Ap, Ai, Ax, b,
243
+ tol=1e-8,
244
+ droptol=0.001, # ILU drop tolerance (Fortran backend only)
245
+ precond_type='ilu', # Preconditioner type
246
+ zero_based=True, # 0-based indexing (default)
247
+ )
248
+ ```
249
+
250
+ ## API Reference
251
+
252
+ ### `blqmr(A, B, **kwargs) -> BLQMRResult`
253
+
254
+ Main solver interface.
255
+
256
+ **Parameters:**
257
+ | Parameter | Type | Default | Description |
258
+ |-----------|------|---------|-------------|
259
+ | `A` | sparse matrix or ndarray | required | System matrix (n × n) |
260
+ | `B` | ndarray | required | Right-hand side (n,) or (n × m) |
261
+ | `tol` | float | 1e-6 | Convergence tolerance |
262
+ | `maxiter` | int | n | Maximum iterations |
263
+ | `M1`, `M2` | preconditioner | None | Custom preconditioners (Python backend) |
264
+ | `x0` | ndarray | None | Initial guess |
265
+ | `precond_type` | str or None | 'ilu' | Preconditioner: 'ilu', 'diag', or None |
266
+ | `droptol` | float | 0.001 | ILU drop tolerance (Fortran backend) |
267
+ | `residual` | bool | False | Use true residual for convergence (Python) |
268
+ | `workspace` | BLQMRWorkspace | None | Pre-allocated workspace (Python) |
269
+
270
+ **Returns:** `BLQMRResult` object with:
271
+ | Attribute | Type | Description |
272
+ |-----------|------|-------------|
273
+ | `x` | ndarray | Solution vector(s) |
274
+ | `flag` | int | 0=converged, 1=maxiter, 2=precond fail, 3=stagnation |
275
+ | `iter` | int | Iterations performed |
276
+ | `relres` | float | Final relative residual |
277
+ | `converged` | bool | True if flag == 0 |
278
+ | `resv` | ndarray | Residual history (Python backend only) |
279
+
280
+ ### `blqmr_solve(Ap, Ai, Ax, b, **kwargs) -> BLQMRResult`
281
+
282
+ Low-level CSC interface for single RHS.
283
+
284
+ ### `blqmr_solve_multi(Ap, Ai, Ax, B, **kwargs) -> BLQMRResult`
285
+
286
+ Low-level CSC interface for multiple right-hand sides.
287
+
288
+ ### `blqmr_scipy(A, b, **kwargs) -> Tuple[ndarray, int]`
289
+
290
+ SciPy-compatible interface returning `(x, flag)`.
291
+
292
+ ### `make_preconditioner(A, precond_type, **kwargs) -> Preconditioner`
293
+
294
+ Create a preconditioner for the Python backend.
295
+
296
+ **Parameters:**
297
+ | Parameter | Type | Default | Description |
298
+ |-----------|------|---------|-------------|
299
+ | `A` | sparse matrix | required | System matrix |
300
+ | `precond_type` | str | required | 'diag', 'jacobi', 'ilu', 'ilu0', 'ilut', 'lu', 'ssor' |
301
+ | `split` | bool | False | Return sqrt(D) for split preconditioning |
302
+ | `drop_tol` | float | 1e-4 | Drop tolerance for ILUT |
303
+ | `fill_factor` | float | 10 | Fill factor for ILUT |
304
+ | `omega` | float | 1.0 | Relaxation parameter for SSOR |
305
+
306
+ ### Utility Functions
307
+
308
+ ```python
309
+ from blocksolver import (
310
+ BLQMR_EXT, # True if Fortran backend available
311
+ HAS_NUMBA, # True if Numba acceleration available
312
+ get_backend_info, # Returns dict with backend details
313
+ test, # Run built-in tests
314
+ )
315
+ ```
316
+
317
+ ## Benchmarks
318
+
319
+ ### BLQMR vs Direct Solver (mldivide)
320
+
321
+ Complex symmetric FEM matrices, 4 right-hand sides, tolerance 10⁻⁸, split Jacobi preconditioner:
322
+
323
+ | Grid | Nodes | NNZ | mldivide | BLQMR | Speedup |
324
+ |------|-------|-----|----------|-------|---------|
325
+ | 20³ | 8,000 | 110K | 135ms | 115ms | **1.2×** |
326
+ | 30³ | 27,000 | 384K | 1.36s | 373ms | **3.6×** |
327
+ | 40³ | 64,000 | 922K | 6.40s | 947ms | **6.8×** |
328
+ | 50³ | 125,000 | 1.8M | 25.9s | 1.76s | **14.7×** |
329
+
330
+ ### Block Size Efficiency
331
+
332
+ With 64 RHS on a 8,000-node complex symmetric system:
333
+
334
+ | Block Size | Iterations | Speedup vs Single |
335
+ |------------|------------|-------------------|
336
+ | 1 (point) | 10,154 | 1.0× |
337
+ | 4 | 2,220 | 1.8× |
338
+ | 8 | 956 | 2.0× |
339
+ | 16 | 361 | 2.1× |
340
+ | 32 | 178 | 2.2× |
341
+
342
+ **Optimal block size**: 8-16 for most problems. Larger blocks have diminishing returns due to increased per-iteration cost.
343
+
344
+ ### Iteration Efficiency
345
+
346
+ With 4 RHS, BLQMR uses only ~24% of total iterations compared to 4 separate single-RHS solves — achieving **super-linear block acceleration**.
347
+
348
+ ## Performance Tips
349
+
350
+ 1. **Use the Fortran backend** when available (faster for large systems)
351
+
352
+ 2. **Enable preconditioning** for ill-conditioned systems:
353
+ ```python
354
+ result = blqmr(A, b, precond_type='ilu')
355
+ ```
356
+
357
+ 3. **Batch multiple right-hand sides** instead of solving one at a time:
358
+ ```python
359
+ # Fast: single call with all RHS
360
+ result = blqmr(A, B_matrix)
361
+
362
+ # Slow: multiple calls
363
+ for b in B_columns:
364
+ result = blqmr(A, b)
365
+ ```
366
+
367
+ 4. **Install Numba** for faster Python backend:
368
+ ```bash
369
+ pip install numba
370
+ ```
371
+
372
+ 5. **Reuse workspace** for repeated solves with the same dimensions:
373
+ ```python
374
+ from blocksolver import BLQMRWorkspace
375
+ ws = BLQMRWorkspace(n, m, dtype=np.complex128)
376
+ for b in many_rhs:
377
+ result = blqmr(A, b, workspace=ws)
378
+ ```
379
+
380
+ 6. **Use split Jacobi for complex symmetric systems**:
381
+ ```python
382
+ # Preserves symmetry of preconditioned system
383
+ M = make_preconditioner(A, 'diag', split=True)
384
+ result = blqmr(A, b, M1=M, M2=M, precond_type=None)
385
+ ```
386
+
387
+ ## Examples
388
+
389
+ ### Diffuse Optical Tomography
390
+
391
+ ```python
392
+ import numpy as np
393
+ from scipy.sparse import diags, kron, eye
394
+ from blocksolver import blqmr
395
+
396
+ def create_diffusion_matrix(nx, ny, D=1.0, mu_a=0.01, omega=1e9):
397
+ """Create 2D diffusion matrix for DOT."""
398
+ n = nx * ny
399
+ h = 1.0 / nx
400
+
401
+ # Laplacian
402
+ Lx = diags([-1, 2, -1], [-1, 0, 1], shape=(nx, nx)) / h**2
403
+ Ly = diags([-1, 2, -1], [-1, 0, 1], shape=(ny, ny)) / h**2
404
+ L = kron(eye(ny), Lx) + kron(Ly, eye(nx))
405
+
406
+ # Diffusion equation: (-D∇² + μ_a + iω/c) φ = q
407
+ c = 3e10 # speed of light in tissue (cm/s)
408
+ A = -D * L + mu_a * eye(n) + 1j * omega / c * eye(n)
409
+
410
+ return A.tocsc()
411
+
412
+ # Setup problem
413
+ A = create_diffusion_matrix(100, 100, omega=2*np.pi*100e6)
414
+ sources = np.random.randn(10000, 16) + 0j # 16 source positions
415
+
416
+ # Solve for all sources at once
417
+ result = blqmr(A, sources, tol=1e-8, precond_type='diag')
418
+ print(f"Solved {sources.shape[1]} systems in {result.iter} iterations")
419
+ ```
420
+
421
+ ### Frequency-Domain Acoustics
422
+
423
+ ```python
424
+ import numpy as np
425
+ from blocksolver import blqmr
426
+
427
+ # Helmholtz equation: (∇² + k²)p = f
428
+ # Results in complex symmetric matrix
429
+
430
+ def solve_helmholtz(K, M, f, frequencies):
431
+ """Solve Helmholtz at multiple frequencies."""
432
+ solutions = []
433
+ for omega in frequencies:
434
+ # A = K - ω²M (complex symmetric if K, M are symmetric)
435
+ A = K - omega**2 * M
436
+ result = blqmr(A, f, tol=1e-10, precond_type='diag')
437
+ solutions.append(result.x)
438
+ return np.array(solutions)
439
+ ```
440
+
441
+ ## Troubleshooting
442
+
443
+ ### "No Fortran backend available"
444
+
445
+ Install the package with Fortran support:
446
+ ```bash
447
+ # Install dependencies first
448
+ sudo apt install gfortran libsuitesparse-dev # Linux
449
+ brew install gcc suite-sparse # macOS
450
+
451
+ # Reinstall blocksolver
452
+ pip install --no-cache-dir blocksolver
453
+ ```
454
+
455
+ ### Check backend status
456
+
457
+ ```python
458
+ from blocksolver import get_backend_info
459
+ print(get_backend_info())
460
+ # {'backend': 'binary', 'has_fortran': True, 'has_numba': True}
461
+ ```
462
+
463
+ ### Slow convergence
464
+
465
+ 1. Enable preconditioning: `precond_type='ilu'` or `precond_type='diag'`
466
+ 2. Reduce ILU drop tolerance: `droptol=1e-4` (Fortran backend)
467
+ 3. Check matrix conditioning with `np.linalg.cond(A.toarray())`
468
+
469
+ ### ILU factorization fails
470
+
471
+ For indefinite or complex symmetric matrices, ILU may fail:
472
+ ```python
473
+ # Fall back to diagonal preconditioner
474
+ result = blqmr(A, b, precond_type='diag')
475
+ ```
476
+
477
+ ### Memory issues with large systems
478
+
479
+ 1. Use the Fortran backend (more memory efficient)
480
+ 2. Reduce block size for multiple RHS
481
+ 3. Use iterative refinement instead of tighter tolerance
482
+
483
+ ## License
484
+
485
+ BSD-3-Clause or GPL-3.0+ (dual-licensed)
486
+
487
+ ## Citation
488
+
489
+ If you use BlockSolver in your research, please cite:
490
+
491
+ ```bibtex
492
+ @software{blocksolver,
493
+ author = {Qianqian Fang},
494
+ title = {BlockSolver: Block Quasi-Minimal Residual Sparse Linear Solver},
495
+ url = {https://github.com/fangq/blit},
496
+ year = {2024}
497
+ }
498
+ ```
499
+
500
+ ## See Also
501
+
502
+ - [BLIT](https://github.com/fangq/blit) - The underlying Fortran library
503
+ - [SciPy sparse.linalg](https://docs.scipy.org/doc/scipy/reference/sparse.linalg.html) - Other iterative solvers
504
+ - [PyAMG](https://github.com/pyamg/pyamg) - Algebraic multigrid solvers
@@ -0,0 +1,6 @@
1
+ blocksolver-0.8.5.dist-info/METADATA,sha256=K7OUbJ-pEkHWX3Cvy9iz1_L1onkox-5T9YwOvCrCiSU,15985
2
+ blocksolver-0.8.5.dist-info/WHEEL,sha256=_cS6fEsxLUIcIdoA6Si0dddGONVIRpHlSl13K8j9MME,93
3
+ blocksolver/_blqmr.cpython-310-darwin.so,sha256=bPJxjHaR2fWALeyS-6FfIKGFePEgcJHdl8r-371iWdc,11711856
4
+ blocksolver/__init__.py,sha256=42r9UA1Cv5xbOzzQvUhvvmkI7a3_Vh6X3md3vY3qnRE,1899
5
+ blocksolver/blqmr.py,sha256=bu_JBg6oNtYPnfOAZVgyGCb_QTc-27J_ahGWrfHVPaw,45067
6
+ blocksolver-0.8.5.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: meson
3
+ Root-Is-Purelib: false
4
+ Tag: cp310-cp310-macosx_14_0_arm64