FastLSQ 0.6.0__tar.gz → 0.6.1__tar.gz

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.
Files changed (129) hide show
  1. {fastlsq-0.6.0 → fastlsq-0.6.1}/CHANGELOG.md +158 -5
  2. {fastlsq-0.6.0 → fastlsq-0.6.1}/FastLSQ.egg-info/PKG-INFO +60 -13
  3. {fastlsq-0.6.0 → fastlsq-0.6.1}/FastLSQ.egg-info/SOURCES.txt +2 -0
  4. {fastlsq-0.6.0 → fastlsq-0.6.1}/FastLSQ.egg-info/requires.txt +1 -0
  5. {fastlsq-0.6.0 → fastlsq-0.6.1}/MANIFEST.in +1 -0
  6. {fastlsq-0.6.0 → fastlsq-0.6.1}/PKG-INFO +60 -13
  7. {fastlsq-0.6.0 → fastlsq-0.6.1}/README.md +58 -12
  8. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/__init__.py +1 -1
  9. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/api.py +5 -1
  10. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/augment.py +101 -6
  11. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/basis.py +10 -1
  12. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/kernels.py +52 -5
  13. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/learnable.py +6 -2
  14. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/linalg.py +63 -23
  15. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/newton.py +29 -6
  16. fastlsq-0.6.1/fastlsq/py.typed +0 -0
  17. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/solvers.py +7 -5
  18. {fastlsq-0.6.0 → fastlsq-0.6.1}/pyproject.toml +5 -2
  19. {fastlsq-0.6.0 → fastlsq-0.6.1}/requirements.txt +1 -0
  20. fastlsq-0.6.1/tests/test_closed_forms_property.py +323 -0
  21. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_diagnostics_timing.py +4 -1
  22. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_symbol.py +7 -2
  23. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_vector_basis.py +15 -1
  24. {fastlsq-0.6.0 → fastlsq-0.6.1}/FastLSQ.egg-info/dependency_links.txt +0 -0
  25. {fastlsq-0.6.0 → fastlsq-0.6.1}/FastLSQ.egg-info/top_level.txt +0 -0
  26. {fastlsq-0.6.0 → fastlsq-0.6.1}/LICENSE +0 -0
  27. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/add_your_own_pde.py +0 -0
  28. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/benchmark_comparison.py +0 -0
  29. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/custom_features.py +0 -0
  30. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/fred_sde.py +0 -0
  31. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/fred_sde_fastlsq.py +0 -0
  32. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/gaia_potential.py +0 -0
  33. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/gaia_potential_fastlsq.py +0 -0
  34. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/horizons_ephemeris.py +0 -0
  35. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/numerai_alpha.py +0 -0
  36. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/numerai_alpha_fastlsq.py +0 -0
  37. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/run_all_fastlsq.py +0 -0
  38. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/__init__.py +0 -0
  39. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/_alsu_lattice.py +0 -0
  40. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/_common.py +0 -0
  41. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/run_all.py +0 -0
  42. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s01_beamloss_ode.py +0 -0
  43. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s01_betatron_tune.py +0 -0
  44. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s01_green_fff.py +0 -0
  45. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s01_hill_ivp.py +0 -0
  46. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s01_observe_fit_act_simulator.py +0 -0
  47. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s01_orbit_inverse.py +0 -0
  48. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s01_passive_loco.py +0 -0
  49. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s01_perturbed_hill.py +0 -0
  50. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s01_sofb_observe_fit_act.py +0 -0
  51. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s01_streaming_archive_growth.py +0 -0
  52. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s01_synchrotron_ode.py +0 -0
  53. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s01_tides_3months.py +0 -0
  54. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s01_topoff_impulse.py +0 -0
  55. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s01_visualize.py +0 -0
  56. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s02_plasma_wakefield.py +0 -0
  57. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s03_synchrobetatron.py +0 -0
  58. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s04_sunspots.py +0 -0
  59. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s05_helioseismology.py +0 -0
  60. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s06_tides.py +0 -0
  61. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s07_iers_earth_rotation.py +0 -0
  62. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s08_mauna_loa_co2.py +0 -0
  63. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s09_enso_qbo.py +0 -0
  64. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s10_pulsar_timing.py +0 -0
  65. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s11_modal_analysis.py +0 -0
  66. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s12_mems_resonator.py +0 -0
  67. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s13_variable_stars_kepler.py +0 -0
  68. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s14_eeg.py +0 -0
  69. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/scenarios/s15_circadian.py +0 -0
  70. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/extras/spectral_expansion.py +0 -0
  71. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/grad_shafranov.py +0 -0
  72. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/grid_inverse.py +0 -0
  73. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/grid_rl_control.py +0 -0
  74. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/grid_swing.py +0 -0
  75. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/gs_inverse.py +0 -0
  76. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/gs_rl_control.py +0 -0
  77. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/integral_equations.py +0 -0
  78. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/integro_differential_demo.py +0 -0
  79. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/inverse_heat_source.py +0 -0
  80. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/inverse_magnetostatics.py +0 -0
  81. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/inverse_source_position.py +0 -0
  82. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/learnable_helmholtz.py +0 -0
  83. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/memory_diffusion.py +0 -0
  84. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/orbit_hill.py +0 -0
  85. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/orbit_inverse.py +0 -0
  86. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/orbit_rl.py +0 -0
  87. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/pde_discovery.py +0 -0
  88. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/rlc_integro_differential.py +0 -0
  89. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/run_all_extensions.py +0 -0
  90. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/run_linear.py +0 -0
  91. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/run_nonlinear.py +0 -0
  92. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/sdf_domains.py +0 -0
  93. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/tutorial_basic.py +0 -0
  94. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/tutorial_nonlinear.py +0 -0
  95. {fastlsq-0.6.0 → fastlsq-0.6.1}/examples/vector_basis_stream_vorticity.py +0 -0
  96. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/benchmark.py +0 -0
  97. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/block.py +0 -0
  98. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/device.py +0 -0
  99. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/diagnostics.py +0 -0
  100. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/export.py +0 -0
  101. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/geometry.py +0 -0
  102. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/lightning.py +0 -0
  103. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/plotting.py +0 -0
  104. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/problems/__init__.py +0 -0
  105. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/problems/integral.py +0 -0
  106. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/problems/linear.py +0 -0
  107. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/problems/nonlinear.py +0 -0
  108. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/problems/regression.py +0 -0
  109. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/tuning.py +0 -0
  110. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/utils.py +0 -0
  111. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/vector.py +0 -0
  112. {fastlsq-0.6.0 → fastlsq-0.6.1}/fastlsq/viz.py +0 -0
  113. {fastlsq-0.6.0 → fastlsq-0.6.1}/setup.cfg +0 -0
  114. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_augment.py +0 -0
  115. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_basic.py +0 -0
  116. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_benchmarks_inverse.py +0 -0
  117. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_block.py +0 -0
  118. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_derivatives.py +0 -0
  119. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_device.py +0 -0
  120. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_geometry_sdf.py +0 -0
  121. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_grad_shafranov.py +0 -0
  122. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_grid_swing.py +0 -0
  123. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_integral.py +0 -0
  124. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_kernels.py +0 -0
  125. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_learnable.py +0 -0
  126. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_multi_integral.py +0 -0
  127. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_orbit_hill.py +0 -0
  128. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_problems_integral.py +0 -0
  129. {fastlsq-0.6.0 → fastlsq-0.6.1}/tests/test_projection.py +0 -0
@@ -2,6 +2,155 @@
2
2
 
3
3
  All notable changes to FastLSQ will be documented in this file.
4
4
 
5
+ ## Release tags
6
+
7
+ Every released version is tagged `vMAJOR.MINOR.PATCH`. The tags for 0.1.0 through
8
+ 0.6.0 were **reconstructed in September 2026**: the project was published to PyPI for
9
+ its first year with only one tag (`v0.2.2`) ever created by hand.
10
+
11
+ Each tag was placed by comparing the `fastlsq/*.py` sources at every commit in the
12
+ repository against the sdist actually published to PyPI, not by guessing from dates.
13
+ Fourteen of the seventeen published releases matched a commit byte for byte. The one
14
+ pre-existing tag, `v0.2.2`, was left untouched and independently resolved to the same
15
+ commit the reconstruction chose, which is the main evidence that the method is sound.
16
+
17
+ Three tags are **approximate**, and their tag messages say so:
18
+
19
+ | Version | What is wrong | Authoritative artefact |
20
+ |---|---|---|
21
+ | 0.1.2 | The published sdist contains `fastlsq/problems/apebench.py`, which exists in no commit in this repository | `fastlsq-0.1.2.tar.gz` on PyPI |
22
+ | 0.1.3 | Same file, same problem | `fastlsq-0.1.3.tar.gz` on PyPI |
23
+ | 0.1.5 | The published `fastlsq/problems/linear.py` is ahead of every committed version of that file | `fastlsq-0.1.5.tar.gz` on PyPI |
24
+
25
+ All three were released from a working tree holding source that was never committed.
26
+ For those versions the sdist on PyPI is the record, not the tag.
27
+
28
+ ### Versions documented here that were never released
29
+
30
+ - **0.2.0** (dated 2026-03-01 below) was never on PyPI and `pyproject.toml` never
31
+ declared it. The published sequence runs 0.1.4 → 0.1.5 → 0.2.1. There is no `v0.2.0`
32
+ tag. The entry is kept for the history of the work, not as a release.
33
+ - **0.4.3** and **0.5.0** are real code milestones on `main` -- `pyproject.toml`
34
+ declares each -- but neither was uploaded. The published sequence runs 0.4.2
35
+ (2026-07-20) straight to 0.6.0 (2026-07-21). Both are tagged, and both tags say
36
+ `NEVER PUBLISHED TO PyPI`.
37
+
38
+ ### Releases missing from this file
39
+
40
+ **0.1.1, 0.1.2, 0.1.3 and 0.1.4 were published to PyPI but have no entry below.**
41
+ They are tagged, and their scope can be read from the tags and the commit history, but
42
+ this file jumps 0.1.0 → 0.2.0 → 0.1.5 and does not describe them. Reconstructing four
43
+ changelog entries a year after the fact would be invention rather than record, so the
44
+ gap is documented here instead of filled in.
45
+
46
+ ## [0.6.1] - 2026-09-18
47
+
48
+ A correctness and packaging pass. No published number changes, and no API is
49
+ removed -- but three of the fixes below were silent wrong answers, not errors.
50
+
51
+ ### Fixed -- silent wrong answers
52
+
53
+ - **kernels** -- `SeparableKernelOperator.inner_products` keyed its cache on
54
+ `(id(basis), n_features)`. A freed basis' address is reused by the next one, and
55
+ `LearnableFastLSQ.basis` builds a new basis object on every access, so a learnable fit
56
+ of a Fredholm problem could train on another basis' operators (measured error of the
57
+ assembled block 0.083 on entries of about 0.05, with no warning). The key is now the
58
+ storage pointer and version counter of `W` and `b`, plus a weak reference to the basis.
59
+ - **linalg** -- `method="qr"` on a rank-deficient matrix returned garbage with no
60
+ exception and no warning (`||x|| = 7e18`, residual `6e4`, where `svd` gives `59` and
61
+ `1e-8`). The diagonal of `R` is now tested against `rcond` and warned about.
62
+ - **learnable** -- `driver="gelsd"` was hard-coded, and torch accepts the rank-revealing
63
+ LAPACK drivers on CPU only, so the learnable path could not run off CPU. Now CPU-only.
64
+
65
+ ### Fixed -- missing operators
66
+
67
+ - **augment** -- `AugmentedBasis` gained `hessian_diag`, `biharmonic`, `advection` and
68
+ `multi_integral`, with the closed forms for the polynomial columns.
69
+ `MultiIntegralOperator.apply(aug, x)` used to raise `AttributeError`, so the `Wave1D`,
70
+ `Wave2D_MS` and `Maxwell2D_TM` problems could not take an augmented basis at all.
71
+ - **basis** -- a 0-d (scalar) symbol is a constant multiplier, not an error.
72
+ - **solvers** -- `add_block(scale=...)` accepts a tuple or tensor of per-axis scales, not
73
+ just a list or ndarray.
74
+ - **newton** -- when no backtracked step satisfied Armijo, the loop restored `beta` and
75
+ then recomputed the identical step until `max_iter`, while `history` recorded the last
76
+ alpha tried as if the step had been taken. It now stops, records the rejection as
77
+ `step_size 0.0`, and tags every exit with a stop reason.
78
+ - **kernels** -- `from_inner_products` objects now raise a clear error from
79
+ `check_quadrature` and `degenerate_eigenvalues` instead of failing inside `leggauss(0)`.
80
+
81
+ ### Changed -- diagnostics and documentation
82
+
83
+ - **linalg** -- `return_info` now carries `method_used`, because `"auto"` picks its
84
+ back-end at run time; on CPU with `mu = 0` it never runs QR (Cholesky probe, then
85
+ `gelsd`), which the module docstring claimed otherwise. The docstring now describes what
86
+ the code does.
87
+ - **linalg/api** -- `rank_used` is documented as what it is: a post-hoc count of singular
88
+ values above `rcond * sigma_max`, not the rank the back-end worked with.
89
+ - **newton** -- the convergence test is relative to the first residual, as the code always
90
+ did; the docstring said absolute.
91
+ - **kernels** -- `degenerate_eigenvalues` no longer runs a full quadrature for inner
92
+ products it discards, and its docstring no longer claims the values depend on the basis.
93
+ - **basis** -- the `1/sqrt(N)` normalisation convention is documented, including the
94
+ differing defaults of `solve_linear` and `solve_nonlinear`.
95
+
96
+ ### Tests
97
+
98
+ - `test_symbol` used `np.trapz`, removed in NumPy 2, so the fractional-Laplacian check
99
+ against the singular-integral definition silently never ran on a current install. With
100
+ `np.trapezoid` the suite goes from 202 passed / 3 failed to 205 passed.
101
+ - New `tests/test_closed_forms_property.py`: 46 property tests checking every closed form
102
+ against an independent reference (autograd, Gauss-Legendre quadrature, the analytic
103
+ characteristic value) at random dimensions, feature counts, bandwidths and orders,
104
+ including the cache-invalidation regression above.
105
+ - `test_version` compared `fastlsq.__version__` against a hard-coded literal, so it had
106
+ to be edited on every release and never caught anything. It now compares
107
+ `__version__` against the version in `pyproject.toml`, which is the drift that can
108
+ actually happen.
109
+ - Full suite: 251 passed.
110
+
111
+ ### Packaging and repository
112
+
113
+ - **`scipy` is declared as a test dependency.** Eight scripts under `examples/` use it for
114
+ their reference solvers, and `tests/test_grad_shafranov.py` and `tests/test_grid_swing.py`
115
+ import those scripts -- but `scipy` appeared only in the `battery` extra, never in `dev`
116
+ or `requirements.txt`. `pip install -e ".[dev]"` into a clean environment produced six
117
+ errors; the suite passed locally only where scipy happened to be installed for other
118
+ reasons. Found by the new CI on its first run.
119
+ - `fastlsq/py.typed` is now actually shipped. `[tool.setuptools.package-data]` had
120
+ declared it since 0.4.0, but the file did not exist, so the package advertised PEP 561
121
+ inline typing that type checkers never saw.
122
+ - The build requirement is `setuptools>=77.0`. `license = "MIT"` is the PEP 639 SPDX
123
+ expression form, which setuptools only understands from 77.0 on; the pin said `>=68.0`.
124
+ - Continuous integration runs the suite on Python 3.9 through 3.12 (`.github/workflows/tests.yml`).
125
+ - The JOSS submission draft (`paper.md`, `paper.bib`) is tracked in the repository.
126
+ - Two 0.5.x changelog entries advertised example scripts under `examples/inverse/` that
127
+ were never shipped; both entries now say so.
128
+
129
+ ### Fixed -- documentation
130
+
131
+ - **The citation in `README.md` had the wrong paper title.** It read *"FastLSQ: A
132
+ Framework for One-Shot PDE Solving"*; the actual title of arXiv:2602.10541 is
133
+ *"FastLSQ: Solving PDEs in One Shot via Fourier Features with Exact Analytical
134
+ Derivatives"*. Anyone who copied that BibTeX block produced a wrong bibliography
135
+ entry. The block now matches the arXiv record and carries the DOI.
136
+ - `paper.bib` cited the same preprint without the `FastLSQ:` title prefix. Corrected to
137
+ the arXiv title, so the JOSS paper and the README agree.
138
+
139
+ ### Added -- release tags and README
140
+
141
+ - Release tags for every version, and the *Release tags* section above documenting how
142
+ they were reconstructed, which three are approximate, and which documented versions
143
+ were never released.
144
+ - `README.md`: status badges (CI, PyPI, Python versions, licence, arXiv); the
145
+ dependency and optional-extras list; a **Development** section covering the test
146
+ suite and CI; a **Releases and versioning** section; and a pointer to the JOSS paper
147
+ draft.
148
+
149
+ ### Changed -- README
150
+
151
+ - `README.md`: the BerkeleyLab ATAP talk link moved from above the project title, where
152
+ it preceded the description, into the **Paper** section with the other references.
153
+
5
154
  ## [0.6.0] - 2026-07-21
6
155
 
7
156
  Completes the operator taxonomy: multi-axis integrals, separable kernels, and
@@ -361,8 +510,10 @@ augmentation columns (§2.3).
361
510
  Gauss--Hermite quadrature of the slice integral to machine precision in d = 2, 3, 4
362
511
  (≤4e-13 observed), that autodiff of the rows wrt `c` matches finite differences, and
363
512
  that a windowed field is recovered from its projections at several directions in one
364
- LSQ. `examples/inverse/tomography_projection.py` demonstrates the full reconstruction
365
- plus the differentiable-optics gradient.
513
+ LSQ. *(Correction, 0.6.1: this entry also announced an
514
+ `examples/inverse/tomography_projection.py` demonstrating the full reconstruction plus
515
+ the differentiable-optics gradient. That example was never shipped; the reconstruction
516
+ and the gradient check live in `tests/test_projection.py` instead.)*
366
517
 
367
518
  ### Scope (honest)
368
519
 
@@ -401,9 +552,11 @@ augmentation columns (§2.3).
401
552
  linear-least-squares design matrix; coefficients stay differentiable so learnable
402
553
  integral-term coefficients train through the solve. All three exported from `fastlsq`.
403
554
  - **Examples** `examples/integro_differential_demo.py` (one-shot forward solve of
404
- `u'(x) + ∫_0^x u ds = f`, rel-L2 ~3e-11) and
405
- `examples/inverse/inverse_memory_kernel.py` (recover an unknown memory strength λ from
406
- noisy data via AdamW through the differentiable solve). New `tests/test_integral.py`
555
+ `u'(x) + ∫_0^x u ds = f`, rel-L2 ~3e-11). *(Correction, 0.6.1: this entry also
556
+ announced an `examples/inverse/inverse_memory_kernel.py` recovering an unknown memory
557
+ strength λ from noisy data via AdamW through the differentiable solve. That example was
558
+ never shipped; gradient flow through the solve is covered by `tests/test_integral.py`.)*
559
+ New `tests/test_integral.py`
407
560
  asserts ∫-then-∂ round-trips to identity, Volterra/definite match quadrature, the DC
408
561
  guard stays finite, and gradients flow to learnable coefficients.
409
562
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: FastLSQ
3
- Version: 0.6.0
3
+ Version: 0.6.1
4
4
  Summary: One-shot PDE solving via Fourier features with exact analytical derivatives; rank-revealing solvers, learnable anisotropic bandwidth, and CPU/CUDA/MPS support
5
5
  Author: Antonin Sulc
6
6
  License-Expression: MIT
@@ -33,6 +33,7 @@ Requires-Dist: scipy>=1.10; extra == "battery"
33
33
  Provides-Extra: dev
34
34
  Requires-Dist: pytest>=7.0; extra == "dev"
35
35
  Requires-Dist: pandas>=2.0; extra == "dev"
36
+ Requires-Dist: scipy>=1.10; extra == "dev"
36
37
  Requires-Dist: build; extra == "dev"
37
38
  Requires-Dist: twine; extra == "dev"
38
39
  Provides-Extra: lightning
@@ -41,8 +42,11 @@ Dynamic: license-file
41
42
 
42
43
  # FastLSQ
43
44
 
44
- [BerkeleyLab ATAP Talk](https://github.com/sulcantonin/FastLSQ/raw/main/presentations/ATAP_Sulc_20260324.pptx)
45
-
45
+ [![tests](https://github.com/sulcantonin/FastLSQ/actions/workflows/tests.yml/badge.svg)](https://github.com/sulcantonin/FastLSQ/actions/workflows/tests.yml)
46
+ [![PyPI](https://img.shields.io/pypi/v/FastLSQ.svg)](https://pypi.org/project/FastLSQ/)
47
+ [![Python](https://img.shields.io/pypi/pyversions/FastLSQ.svg)](https://pypi.org/project/FastLSQ/)
48
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
49
+ [![arXiv](https://img.shields.io/badge/arXiv-2602.10541-b31b1b.svg)](https://arxiv.org/abs/2602.10541)
46
50
 
47
51
  <p align="center">
48
52
  <img src="https://raw.githubusercontent.com/sulcantonin/FastLSQ/main/misc/fastlsq_teaser.png" alt="FastLSQ method overview" width="400"/>
@@ -68,14 +72,23 @@ regularisation, 1/sqrt(N) feature normalisation, and continuation/homotopy.
68
72
  pip install fastlsq
69
73
  ```
70
74
 
71
- For development (includes testing and build tools):
75
+ Requires Python 3.9+, PyTorch 2.0+, NumPy 1.24+ and Matplotlib 3.7+. There is no
76
+ compiled extension and no mesh library: everything runs on the PyTorch tensor stack,
77
+ on CPU, CUDA or Apple-MPS.
78
+
79
+ For development -- test runner, the SciPy reference solvers the example scripts
80
+ compare against, and the build tools:
72
81
 
73
82
  ```bash
74
83
  git clone https://github.com/sulcantonin/FastLSQ.git
75
84
  cd FastLSQ
76
85
  pip install -e ".[dev]"
86
+ pytest tests/ # 251 tests, about 30 s on a laptop CPU
77
87
  ```
78
88
 
89
+ Optional extras: `.[battery]` for the battery-degradation examples (`progpy`),
90
+ `.[lightning]` for the PyTorch Lightning training loop.
91
+
79
92
  ## Quick start
80
93
 
81
94
  ### Solve a linear PDE in one line
@@ -516,23 +529,57 @@ See `examples/add_your_own_pde.py` for the complete tutorial.
516
529
  - **PyTorch Lightning**: Integration for training loops
517
530
  - **20+ benchmark problems**: Linear, nonlinear, and regression-mode PDEs
518
531
 
532
+ ## Development
533
+
534
+ ```bash
535
+ pip install -e ".[dev]"
536
+ pytest tests/
537
+ ```
538
+
539
+ The suite is 251 tests and runs in about 30 seconds on a laptop CPU. Every closed
540
+ form -- derivative, integral, Fourier symbol, projection -- is checked against an
541
+ independent reference (autograd, Gauss-Legendre quadrature, or the analytic value) in
542
+ `tests/test_closed_forms_property.py`, so a wrong closed form fails the suite rather
543
+ than silently returning a plausible number.
544
+
545
+ [Continuous integration](https://github.com/sulcantonin/FastLSQ/actions/workflows/tests.yml)
546
+ runs the suite on Python 3.9, 3.10, 3.11 and 3.12, and separately builds the sdist and
547
+ wheel and checks their metadata.
548
+
549
+ ## Releases and versioning
550
+
551
+ Released versions are on [PyPI](https://pypi.org/project/FastLSQ/) and tagged in this
552
+ repository as `vMAJOR.MINOR.PATCH`. `CHANGELOG.md` documents every release.
553
+
554
+ The tags for 0.1.0 through 0.6.0 were reconstructed after the fact, since the project
555
+ was published to PyPI for its first year without tagging. Each tag was matched to its
556
+ commit by comparing the commit's `fastlsq/*.py` sources against the sdist actually
557
+ published, so most are byte-for-byte exact; the three that are not say so in the tag
558
+ message. See the *Release tags* note at the top of `CHANGELOG.md`.
559
+
519
560
  ## Paper
520
561
 
521
- The full preprint is available on [arXiv](https://arxiv.org/abs/2602.10541)
562
+ The preprint is on [arXiv](https://arxiv.org/abs/2602.10541). A software paper for the
563
+ [Journal of Open Source Software](https://joss.theoj.org/) is drafted in
564
+ [`paper.md`](paper.md).
565
+
566
+ There is also a [BerkeleyLab ATAP talk](https://github.com/sulcantonin/FastLSQ/raw/main/presentations/ATAP_Sulc_20260324.pptx)
567
+ covering the method and the accelerator-physics applications.
522
568
 
523
569
  ## Citing this work
524
570
 
525
571
  If you use FastLSQ in your research, please cite:
526
572
 
527
573
  ```bibtex
528
- @misc{sulc2026fastlsqframeworkoneshotpde,
529
- title={FastLSQ: A Framework for One-Shot PDE Solving},
530
- author={Antonin Sulc},
531
- year={2026},
532
- eprint={2602.10541},
533
- archivePrefix={arXiv},
534
- primaryClass={math.NA},
535
- url={https://arxiv.org/abs/2602.10541},
574
+ @misc{sulc2026fastlsq,
575
+ author = {Sulc, Antonin},
576
+ title = {{FastLSQ}: Solving {PDEs} in One Shot via {Fourier} Features with Exact Analytical Derivatives},
577
+ year = {2026},
578
+ eprint = {2602.10541},
579
+ archivePrefix = {arXiv},
580
+ primaryClass = {math.NA},
581
+ doi = {10.48550/arXiv.2602.10541},
582
+ url = {https://arxiv.org/abs/2602.10541}
536
583
  }
537
584
  ```
538
585
 
@@ -94,6 +94,7 @@ fastlsq/lightning.py
94
94
  fastlsq/linalg.py
95
95
  fastlsq/newton.py
96
96
  fastlsq/plotting.py
97
+ fastlsq/py.typed
97
98
  fastlsq/solvers.py
98
99
  fastlsq/tuning.py
99
100
  fastlsq/utils.py
@@ -108,6 +109,7 @@ tests/test_augment.py
108
109
  tests/test_basic.py
109
110
  tests/test_benchmarks_inverse.py
110
111
  tests/test_block.py
112
+ tests/test_closed_forms_property.py
111
113
  tests/test_derivatives.py
112
114
  tests/test_device.py
113
115
  tests/test_diagnostics_timing.py
@@ -10,6 +10,7 @@ scipy>=1.10
10
10
  [dev]
11
11
  pytest>=7.0
12
12
  pandas>=2.0
13
+ scipy>=1.10
13
14
  build
14
15
  twine
15
16
 
@@ -2,6 +2,7 @@ include LICENSE
2
2
  include README.md
3
3
  include CHANGELOG.md
4
4
  include requirements.txt
5
+ include fastlsq/py.typed
5
6
  recursive-include examples *.py
6
7
  recursive-include tests *.py
7
8
  recursive-exclude * __pycache__
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: FastLSQ
3
- Version: 0.6.0
3
+ Version: 0.6.1
4
4
  Summary: One-shot PDE solving via Fourier features with exact analytical derivatives; rank-revealing solvers, learnable anisotropic bandwidth, and CPU/CUDA/MPS support
5
5
  Author: Antonin Sulc
6
6
  License-Expression: MIT
@@ -33,6 +33,7 @@ Requires-Dist: scipy>=1.10; extra == "battery"
33
33
  Provides-Extra: dev
34
34
  Requires-Dist: pytest>=7.0; extra == "dev"
35
35
  Requires-Dist: pandas>=2.0; extra == "dev"
36
+ Requires-Dist: scipy>=1.10; extra == "dev"
36
37
  Requires-Dist: build; extra == "dev"
37
38
  Requires-Dist: twine; extra == "dev"
38
39
  Provides-Extra: lightning
@@ -41,8 +42,11 @@ Dynamic: license-file
41
42
 
42
43
  # FastLSQ
43
44
 
44
- [BerkeleyLab ATAP Talk](https://github.com/sulcantonin/FastLSQ/raw/main/presentations/ATAP_Sulc_20260324.pptx)
45
-
45
+ [![tests](https://github.com/sulcantonin/FastLSQ/actions/workflows/tests.yml/badge.svg)](https://github.com/sulcantonin/FastLSQ/actions/workflows/tests.yml)
46
+ [![PyPI](https://img.shields.io/pypi/v/FastLSQ.svg)](https://pypi.org/project/FastLSQ/)
47
+ [![Python](https://img.shields.io/pypi/pyversions/FastLSQ.svg)](https://pypi.org/project/FastLSQ/)
48
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
49
+ [![arXiv](https://img.shields.io/badge/arXiv-2602.10541-b31b1b.svg)](https://arxiv.org/abs/2602.10541)
46
50
 
47
51
  <p align="center">
48
52
  <img src="https://raw.githubusercontent.com/sulcantonin/FastLSQ/main/misc/fastlsq_teaser.png" alt="FastLSQ method overview" width="400"/>
@@ -68,14 +72,23 @@ regularisation, 1/sqrt(N) feature normalisation, and continuation/homotopy.
68
72
  pip install fastlsq
69
73
  ```
70
74
 
71
- For development (includes testing and build tools):
75
+ Requires Python 3.9+, PyTorch 2.0+, NumPy 1.24+ and Matplotlib 3.7+. There is no
76
+ compiled extension and no mesh library: everything runs on the PyTorch tensor stack,
77
+ on CPU, CUDA or Apple-MPS.
78
+
79
+ For development -- test runner, the SciPy reference solvers the example scripts
80
+ compare against, and the build tools:
72
81
 
73
82
  ```bash
74
83
  git clone https://github.com/sulcantonin/FastLSQ.git
75
84
  cd FastLSQ
76
85
  pip install -e ".[dev]"
86
+ pytest tests/ # 251 tests, about 30 s on a laptop CPU
77
87
  ```
78
88
 
89
+ Optional extras: `.[battery]` for the battery-degradation examples (`progpy`),
90
+ `.[lightning]` for the PyTorch Lightning training loop.
91
+
79
92
  ## Quick start
80
93
 
81
94
  ### Solve a linear PDE in one line
@@ -516,23 +529,57 @@ See `examples/add_your_own_pde.py` for the complete tutorial.
516
529
  - **PyTorch Lightning**: Integration for training loops
517
530
  - **20+ benchmark problems**: Linear, nonlinear, and regression-mode PDEs
518
531
 
532
+ ## Development
533
+
534
+ ```bash
535
+ pip install -e ".[dev]"
536
+ pytest tests/
537
+ ```
538
+
539
+ The suite is 251 tests and runs in about 30 seconds on a laptop CPU. Every closed
540
+ form -- derivative, integral, Fourier symbol, projection -- is checked against an
541
+ independent reference (autograd, Gauss-Legendre quadrature, or the analytic value) in
542
+ `tests/test_closed_forms_property.py`, so a wrong closed form fails the suite rather
543
+ than silently returning a plausible number.
544
+
545
+ [Continuous integration](https://github.com/sulcantonin/FastLSQ/actions/workflows/tests.yml)
546
+ runs the suite on Python 3.9, 3.10, 3.11 and 3.12, and separately builds the sdist and
547
+ wheel and checks their metadata.
548
+
549
+ ## Releases and versioning
550
+
551
+ Released versions are on [PyPI](https://pypi.org/project/FastLSQ/) and tagged in this
552
+ repository as `vMAJOR.MINOR.PATCH`. `CHANGELOG.md` documents every release.
553
+
554
+ The tags for 0.1.0 through 0.6.0 were reconstructed after the fact, since the project
555
+ was published to PyPI for its first year without tagging. Each tag was matched to its
556
+ commit by comparing the commit's `fastlsq/*.py` sources against the sdist actually
557
+ published, so most are byte-for-byte exact; the three that are not say so in the tag
558
+ message. See the *Release tags* note at the top of `CHANGELOG.md`.
559
+
519
560
  ## Paper
520
561
 
521
- The full preprint is available on [arXiv](https://arxiv.org/abs/2602.10541)
562
+ The preprint is on [arXiv](https://arxiv.org/abs/2602.10541). A software paper for the
563
+ [Journal of Open Source Software](https://joss.theoj.org/) is drafted in
564
+ [`paper.md`](paper.md).
565
+
566
+ There is also a [BerkeleyLab ATAP talk](https://github.com/sulcantonin/FastLSQ/raw/main/presentations/ATAP_Sulc_20260324.pptx)
567
+ covering the method and the accelerator-physics applications.
522
568
 
523
569
  ## Citing this work
524
570
 
525
571
  If you use FastLSQ in your research, please cite:
526
572
 
527
573
  ```bibtex
528
- @misc{sulc2026fastlsqframeworkoneshotpde,
529
- title={FastLSQ: A Framework for One-Shot PDE Solving},
530
- author={Antonin Sulc},
531
- year={2026},
532
- eprint={2602.10541},
533
- archivePrefix={arXiv},
534
- primaryClass={math.NA},
535
- url={https://arxiv.org/abs/2602.10541},
574
+ @misc{sulc2026fastlsq,
575
+ author = {Sulc, Antonin},
576
+ title = {{FastLSQ}: Solving {PDEs} in One Shot via {Fourier} Features with Exact Analytical Derivatives},
577
+ year = {2026},
578
+ eprint = {2602.10541},
579
+ archivePrefix = {arXiv},
580
+ primaryClass = {math.NA},
581
+ doi = {10.48550/arXiv.2602.10541},
582
+ url = {https://arxiv.org/abs/2602.10541}
536
583
  }
537
584
  ```
538
585
 
@@ -1,7 +1,10 @@
1
1
  # FastLSQ
2
2
 
3
- [BerkeleyLab ATAP Talk](https://github.com/sulcantonin/FastLSQ/raw/main/presentations/ATAP_Sulc_20260324.pptx)
4
-
3
+ [![tests](https://github.com/sulcantonin/FastLSQ/actions/workflows/tests.yml/badge.svg)](https://github.com/sulcantonin/FastLSQ/actions/workflows/tests.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/FastLSQ.svg)](https://pypi.org/project/FastLSQ/)
5
+ [![Python](https://img.shields.io/pypi/pyversions/FastLSQ.svg)](https://pypi.org/project/FastLSQ/)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
+ [![arXiv](https://img.shields.io/badge/arXiv-2602.10541-b31b1b.svg)](https://arxiv.org/abs/2602.10541)
5
8
 
6
9
  <p align="center">
7
10
  <img src="https://raw.githubusercontent.com/sulcantonin/FastLSQ/main/misc/fastlsq_teaser.png" alt="FastLSQ method overview" width="400"/>
@@ -27,14 +30,23 @@ regularisation, 1/sqrt(N) feature normalisation, and continuation/homotopy.
27
30
  pip install fastlsq
28
31
  ```
29
32
 
30
- For development (includes testing and build tools):
33
+ Requires Python 3.9+, PyTorch 2.0+, NumPy 1.24+ and Matplotlib 3.7+. There is no
34
+ compiled extension and no mesh library: everything runs on the PyTorch tensor stack,
35
+ on CPU, CUDA or Apple-MPS.
36
+
37
+ For development -- test runner, the SciPy reference solvers the example scripts
38
+ compare against, and the build tools:
31
39
 
32
40
  ```bash
33
41
  git clone https://github.com/sulcantonin/FastLSQ.git
34
42
  cd FastLSQ
35
43
  pip install -e ".[dev]"
44
+ pytest tests/ # 251 tests, about 30 s on a laptop CPU
36
45
  ```
37
46
 
47
+ Optional extras: `.[battery]` for the battery-degradation examples (`progpy`),
48
+ `.[lightning]` for the PyTorch Lightning training loop.
49
+
38
50
  ## Quick start
39
51
 
40
52
  ### Solve a linear PDE in one line
@@ -475,23 +487,57 @@ See `examples/add_your_own_pde.py` for the complete tutorial.
475
487
  - **PyTorch Lightning**: Integration for training loops
476
488
  - **20+ benchmark problems**: Linear, nonlinear, and regression-mode PDEs
477
489
 
490
+ ## Development
491
+
492
+ ```bash
493
+ pip install -e ".[dev]"
494
+ pytest tests/
495
+ ```
496
+
497
+ The suite is 251 tests and runs in about 30 seconds on a laptop CPU. Every closed
498
+ form -- derivative, integral, Fourier symbol, projection -- is checked against an
499
+ independent reference (autograd, Gauss-Legendre quadrature, or the analytic value) in
500
+ `tests/test_closed_forms_property.py`, so a wrong closed form fails the suite rather
501
+ than silently returning a plausible number.
502
+
503
+ [Continuous integration](https://github.com/sulcantonin/FastLSQ/actions/workflows/tests.yml)
504
+ runs the suite on Python 3.9, 3.10, 3.11 and 3.12, and separately builds the sdist and
505
+ wheel and checks their metadata.
506
+
507
+ ## Releases and versioning
508
+
509
+ Released versions are on [PyPI](https://pypi.org/project/FastLSQ/) and tagged in this
510
+ repository as `vMAJOR.MINOR.PATCH`. `CHANGELOG.md` documents every release.
511
+
512
+ The tags for 0.1.0 through 0.6.0 were reconstructed after the fact, since the project
513
+ was published to PyPI for its first year without tagging. Each tag was matched to its
514
+ commit by comparing the commit's `fastlsq/*.py` sources against the sdist actually
515
+ published, so most are byte-for-byte exact; the three that are not say so in the tag
516
+ message. See the *Release tags* note at the top of `CHANGELOG.md`.
517
+
478
518
  ## Paper
479
519
 
480
- The full preprint is available on [arXiv](https://arxiv.org/abs/2602.10541)
520
+ The preprint is on [arXiv](https://arxiv.org/abs/2602.10541). A software paper for the
521
+ [Journal of Open Source Software](https://joss.theoj.org/) is drafted in
522
+ [`paper.md`](paper.md).
523
+
524
+ There is also a [BerkeleyLab ATAP talk](https://github.com/sulcantonin/FastLSQ/raw/main/presentations/ATAP_Sulc_20260324.pptx)
525
+ covering the method and the accelerator-physics applications.
481
526
 
482
527
  ## Citing this work
483
528
 
484
529
  If you use FastLSQ in your research, please cite:
485
530
 
486
531
  ```bibtex
487
- @misc{sulc2026fastlsqframeworkoneshotpde,
488
- title={FastLSQ: A Framework for One-Shot PDE Solving},
489
- author={Antonin Sulc},
490
- year={2026},
491
- eprint={2602.10541},
492
- archivePrefix={arXiv},
493
- primaryClass={math.NA},
494
- url={https://arxiv.org/abs/2602.10541},
532
+ @misc{sulc2026fastlsq,
533
+ author = {Sulc, Antonin},
534
+ title = {{FastLSQ}: Solving {PDEs} in One Shot via {Fourier} Features with Exact Analytical Derivatives},
535
+ year = {2026},
536
+ eprint = {2602.10541},
537
+ archivePrefix = {arXiv},
538
+ primaryClass = {math.NA},
539
+ doi = {10.48550/arXiv.2602.10541},
540
+ url = {https://arxiv.org/abs/2602.10541}
495
541
  }
496
542
  ```
497
543
 
@@ -81,7 +81,7 @@ from fastlsq.export import (
81
81
  from fastlsq import viz
82
82
  from fastlsq import benchmark
83
83
 
84
- __version__ = "0.6.0"
84
+ __version__ = "0.6.1"
85
85
  __all__ = [
86
86
  # Device selection (CPU / CUDA / Apple-MPS, dtype-aware)
87
87
  "resolve_device",
@@ -193,10 +193,14 @@ def solve_linear(
193
193
  "scale_search_s": scale_search_s,
194
194
  "assemble_s": assemble_s,
195
195
  "solve_s": solve_s,
196
- # Rank-revealing solve diagnostics (the paper's central mechanism).
196
+ # Conditioning diagnostics of the assembled system. "rank_used" is the
197
+ # number of singular values above rcond * sigma_max -- a property of A, NOT
198
+ # the rank the back-end worked with (QR and Cholesky use every column).
199
+ # "method_used" says which back-end actually ran.
197
200
  "rank_used": solve_info["rank_used"],
198
201
  "residual": solve_info["residual"],
199
202
  "cond_estimate": solve_info["cond_estimate"],
203
+ "method_used": solve_info.get("method_used"),
200
204
  }
201
205
  if verbose:
202
206
  print(f"Value error: {val_err:.2e}, Gradient error: {grad_err:.2e}")