# Preconditioner Changes

## Origin

Based on [GNP](https://github.com/jiechenjiechen/GNP) (Paper: arXiv 2406.00809v3).
Original source: `GNP/precond/` and `GNP/utils.py`.

## What we changed

- Extracted shared torch-scipy conversion into `sparse_convert.py` (was duplicated across ILU, AMG, BlockJacobi)
- ILU: Store L factor as CSR instead of CSC for efficient `spsolve_triangular`
- AMG: Use `solver_configuration()` before `solver()` to match reference behavior
- AMG_AIR: Added as variant for unsymmetric matrices (uses `air_solver` from pyamg)
- Jacobi: Fail fast on zero diagonals instead of silent replacement; use `scatter_add_` + `coalesce()` for correct diagonal extraction
- Removed file-based caching from ILU and BlockJacobi — not needed for our evaluation pipeline
- All preconditioners follow the `Preconditioner` Protocol from `solver/fgmres.py`
- Added `NoPreconditioner` as explicit identity baseline
