Commit aa2b906
Make the three examples runnable on the modernized stack
End-to-end verification of `examples/` revealed three issues, none of
them regressions in the library itself but all of them rough edges
that left the examples broken on a clean v1.0.0 install:
1. `examples/wlsqm_example.py` and `examples/lapackdrivers_example.py`
both used `plt.grid(b=True, which='both')`, where `b=` is the old
matplotlib 2.x kwarg. Matplotlib 3.x renamed it to `visible=` and
now errors out with a confusing "keyword grid_b is not recognized".
Changed to `plt.grid(visible=True, which='both')` at all three sites.
2. `examples/wlsqm_example.py` imports sympy to symbolically
differentiate a manufactured solution and check the wlsqm fit
against the analytical derivatives. sympy was not in the dev deps,
so the example failed at import time. Added sympy to the dev list
in pyproject.toml. Also restored the comments PDM stripped from
the dev list during the previous `pdm add --dev` invocation
(build, pyyaml, meson-python all had explanatory comments that
went missing).
3. `examples/lapackdrivers_example.py` had a pre-existing test-logic
flaw: it asserted `|x_wlsqm - x_numpy| < 1e-10` against a
`numpy.linalg.solve` reference, treating NumPy as ground truth.
On this particular run, wlsqm and `scipy.linalg.solve` agree
bit-identically (they call the same LAPACK DGESV/DSYSV) but both
differ from NumPy by ~1.5e-6. Investigation: residuals
`‖A x − b‖ ≈ 1e-13` for all three. The matrices are `(U + U.T)/2`
for U ~ uniform[0,1], moderately ill-conditioned (κ ~ 1e4 at n=117),
indefinite, and the three LAPACK paths legitimately pick slightly
different equally-valid solutions — that is a property of finite-
precision arithmetic on a borderline-conditioned matrix, not a bug.
The example never tripped consistently in 2017 because it used
unseeded `np.random.sample(...)` and depended on lucky draws.
Replaced the per-element `|x_wlsqm - x_numpy|` assertion with a
per-solver relative residual check: `‖A x − b‖ / ‖b‖ < 1e-8`,
computed per problem instance with a vectorized
`np.einsum('ijk,jk->ik', A, x) - b`. This is the right sanity check
for a linear solver — it does not depend on having a "ground truth"
solution. The 1e-8 threshold accommodates DSYSV (Bunch-Kaufman)
producing noticeably larger residuals than DGESV on indefinite
matrices, which is normal and expected. It is still 8 orders of
magnitude above machine epsilon and tight enough to catch any
realistic regression in the Cython wrappers.
Also seeded `np.random.seed(42)` in both examples' `main()` so the
per-run residuals (and the printed "max error" stats in
wlsqm_example.py) are reproducible across runs and machines.
Without seeding, the lapackdrivers residuals swung from ~1e-11 to
~1e-9 between runs purely as a function of which random matrix the
global RNG drew.
After this commit, all three examples run cleanly to exit code 0 with
matplotlib in headless mode (`MPLBACKEND=Agg`):
- examples/expertsolver_example.py: ~1 s
- examples/lapackdrivers_example.py: ~30 s (lots of timed solver runs)
- examples/wlsqm_example.py: ~1 min (test3d, test2d, test1d, testmany2d)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>1 parent f040575 commit aa2b906
3 files changed
Lines changed: 54 additions & 12 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
68 | 75 | | |
69 | 76 | | |
70 | 77 | | |
| |||
246 | 253 | | |
247 | 254 | | |
248 | 255 | | |
249 | | - | |
250 | | - | |
251 | | - | |
252 | | - | |
253 | | - | |
254 | | - | |
255 | | - | |
256 | | - | |
257 | | - | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
258 | 288 | | |
259 | 289 | | |
260 | 290 | | |
| |||
304 | 334 | | |
305 | 335 | | |
306 | 336 | | |
307 | | - | |
| 337 | + | |
308 | 338 | | |
309 | 339 | | |
310 | 340 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
899 | 899 | | |
900 | 900 | | |
901 | 901 | | |
902 | | - | |
| 902 | + | |
903 | 903 | | |
904 | 904 | | |
905 | 905 | | |
| |||
1256 | 1256 | | |
1257 | 1257 | | |
1258 | 1258 | | |
1259 | | - | |
| 1259 | + | |
1260 | 1260 | | |
1261 | 1261 | | |
1262 | 1262 | | |
1263 | 1263 | | |
1264 | 1264 | | |
| 1265 | + | |
| 1266 | + | |
| 1267 | + | |
| 1268 | + | |
| 1269 | + | |
| 1270 | + | |
| 1271 | + | |
| 1272 | + | |
1265 | 1273 | | |
1266 | 1274 | | |
1267 | 1275 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
157 | 157 | | |
158 | 158 | | |
159 | 159 | | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
160 | 164 | | |
161 | 165 | | |
162 | 166 | | |
| |||
0 commit comments