FastLSQ 0.6.0__tar.gz → 0.6.2__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 (132) hide show
  1. {fastlsq-0.6.0 → fastlsq-0.6.2}/CHANGELOG.md +235 -6
  2. fastlsq-0.6.2/CITATION.cff +53 -0
  3. {fastlsq-0.6.0 → fastlsq-0.6.2}/FastLSQ.egg-info/PKG-INFO +65 -14
  4. {fastlsq-0.6.0 → fastlsq-0.6.2}/FastLSQ.egg-info/SOURCES.txt +5 -0
  5. {fastlsq-0.6.0 → fastlsq-0.6.2}/FastLSQ.egg-info/requires.txt +1 -0
  6. {fastlsq-0.6.0 → fastlsq-0.6.2}/MANIFEST.in +2 -0
  7. {fastlsq-0.6.0 → fastlsq-0.6.2}/PKG-INFO +65 -14
  8. {fastlsq-0.6.0 → fastlsq-0.6.2}/README.md +62 -12
  9. fastlsq-0.6.2/examples/stealth_navigation.py +332 -0
  10. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/__init__.py +1 -1
  11. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/api.py +5 -1
  12. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/augment.py +101 -6
  13. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/basis.py +10 -1
  14. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/kernels.py +52 -5
  15. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/learnable.py +6 -2
  16. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/linalg.py +63 -23
  17. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/newton.py +29 -6
  18. fastlsq-0.6.2/fastlsq/py.typed +0 -0
  19. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/solvers.py +7 -5
  20. {fastlsq-0.6.0 → fastlsq-0.6.2}/pyproject.toml +6 -3
  21. {fastlsq-0.6.0 → fastlsq-0.6.2}/requirements.txt +1 -0
  22. fastlsq-0.6.2/tests/test_closed_forms_property.py +323 -0
  23. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_diagnostics_timing.py +4 -1
  24. fastlsq-0.6.2/tests/test_stealth_navigation.py +51 -0
  25. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_symbol.py +7 -2
  26. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_vector_basis.py +15 -1
  27. {fastlsq-0.6.0 → fastlsq-0.6.2}/FastLSQ.egg-info/dependency_links.txt +0 -0
  28. {fastlsq-0.6.0 → fastlsq-0.6.2}/FastLSQ.egg-info/top_level.txt +0 -0
  29. {fastlsq-0.6.0 → fastlsq-0.6.2}/LICENSE +0 -0
  30. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/add_your_own_pde.py +0 -0
  31. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/benchmark_comparison.py +0 -0
  32. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/custom_features.py +0 -0
  33. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/fred_sde.py +0 -0
  34. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/fred_sde_fastlsq.py +0 -0
  35. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/gaia_potential.py +0 -0
  36. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/gaia_potential_fastlsq.py +0 -0
  37. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/horizons_ephemeris.py +0 -0
  38. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/numerai_alpha.py +0 -0
  39. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/numerai_alpha_fastlsq.py +0 -0
  40. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/run_all_fastlsq.py +0 -0
  41. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/__init__.py +0 -0
  42. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/_alsu_lattice.py +0 -0
  43. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/_common.py +0 -0
  44. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/run_all.py +0 -0
  45. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s01_beamloss_ode.py +0 -0
  46. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s01_betatron_tune.py +0 -0
  47. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s01_green_fff.py +0 -0
  48. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s01_hill_ivp.py +0 -0
  49. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s01_observe_fit_act_simulator.py +0 -0
  50. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s01_orbit_inverse.py +0 -0
  51. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s01_passive_loco.py +0 -0
  52. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s01_perturbed_hill.py +0 -0
  53. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s01_sofb_observe_fit_act.py +0 -0
  54. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s01_streaming_archive_growth.py +0 -0
  55. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s01_synchrotron_ode.py +0 -0
  56. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s01_tides_3months.py +0 -0
  57. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s01_topoff_impulse.py +0 -0
  58. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s01_visualize.py +0 -0
  59. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s02_plasma_wakefield.py +0 -0
  60. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s03_synchrobetatron.py +0 -0
  61. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s04_sunspots.py +0 -0
  62. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s05_helioseismology.py +0 -0
  63. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s06_tides.py +0 -0
  64. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s07_iers_earth_rotation.py +0 -0
  65. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s08_mauna_loa_co2.py +0 -0
  66. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s09_enso_qbo.py +0 -0
  67. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s10_pulsar_timing.py +0 -0
  68. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s11_modal_analysis.py +0 -0
  69. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s12_mems_resonator.py +0 -0
  70. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s13_variable_stars_kepler.py +0 -0
  71. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s14_eeg.py +0 -0
  72. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/scenarios/s15_circadian.py +0 -0
  73. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/extras/spectral_expansion.py +0 -0
  74. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/grad_shafranov.py +0 -0
  75. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/grid_inverse.py +0 -0
  76. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/grid_rl_control.py +0 -0
  77. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/grid_swing.py +0 -0
  78. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/gs_inverse.py +0 -0
  79. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/gs_rl_control.py +0 -0
  80. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/integral_equations.py +0 -0
  81. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/integro_differential_demo.py +0 -0
  82. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/inverse_heat_source.py +0 -0
  83. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/inverse_magnetostatics.py +0 -0
  84. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/inverse_source_position.py +0 -0
  85. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/learnable_helmholtz.py +0 -0
  86. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/memory_diffusion.py +0 -0
  87. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/orbit_hill.py +0 -0
  88. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/orbit_inverse.py +0 -0
  89. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/orbit_rl.py +0 -0
  90. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/pde_discovery.py +0 -0
  91. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/rlc_integro_differential.py +0 -0
  92. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/run_all_extensions.py +0 -0
  93. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/run_linear.py +0 -0
  94. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/run_nonlinear.py +0 -0
  95. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/sdf_domains.py +0 -0
  96. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/tutorial_basic.py +0 -0
  97. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/tutorial_nonlinear.py +0 -0
  98. {fastlsq-0.6.0 → fastlsq-0.6.2}/examples/vector_basis_stream_vorticity.py +0 -0
  99. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/benchmark.py +0 -0
  100. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/block.py +0 -0
  101. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/device.py +0 -0
  102. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/diagnostics.py +0 -0
  103. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/export.py +0 -0
  104. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/geometry.py +0 -0
  105. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/lightning.py +0 -0
  106. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/plotting.py +0 -0
  107. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/problems/__init__.py +0 -0
  108. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/problems/integral.py +0 -0
  109. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/problems/linear.py +0 -0
  110. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/problems/nonlinear.py +0 -0
  111. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/problems/regression.py +0 -0
  112. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/tuning.py +0 -0
  113. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/utils.py +0 -0
  114. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/vector.py +0 -0
  115. {fastlsq-0.6.0 → fastlsq-0.6.2}/fastlsq/viz.py +0 -0
  116. {fastlsq-0.6.0 → fastlsq-0.6.2}/setup.cfg +0 -0
  117. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_augment.py +0 -0
  118. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_basic.py +0 -0
  119. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_benchmarks_inverse.py +0 -0
  120. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_block.py +0 -0
  121. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_derivatives.py +0 -0
  122. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_device.py +0 -0
  123. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_geometry_sdf.py +0 -0
  124. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_grad_shafranov.py +0 -0
  125. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_grid_swing.py +0 -0
  126. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_integral.py +0 -0
  127. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_kernels.py +0 -0
  128. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_learnable.py +0 -0
  129. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_multi_integral.py +0 -0
  130. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_orbit_hill.py +0 -0
  131. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_problems_integral.py +0 -0
  132. {fastlsq-0.6.0 → fastlsq-0.6.2}/tests/test_projection.py +0 -0
@@ -2,6 +2,231 @@
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
+ ### Zenodo archives
29
+
30
+ Every tag is archived on Zenodo under concept DOI
31
+ [10.5281/zenodo.22830737](https://doi.org/10.5281/zenodo.22830737).
32
+
33
+ Two things about that archive are worth knowing, because neither is obvious:
34
+
35
+ - **Zenodo reads `.zenodo.json` from the tagged commit's tree, not from `main`.**
36
+ No tag before 0.6.2 contained the file, so every archived version up to and
37
+ including 0.6.1 carries GitHub's auto-generated metadata -- the title
38
+ `sulcantonin/FastLSQ: FastLSQ <version>`, no ORCID, no affiliation, no keywords
39
+ and no link to the preprint.
40
+ - **Zenodo's "latest version" is the one published last, not the highest version
41
+ number.** The twenty releases were archived in parallel and finished out of
42
+ order, so the concept DOI resolved to 0.2.6 for a while. Cutting a new release is
43
+ what corrects it.
44
+
45
+ ### Versions documented here that were never released
46
+
47
+ - **0.2.0** (dated 2026-03-01 below) was never on PyPI and `pyproject.toml` never
48
+ declared it. The published sequence runs 0.1.4 → 0.1.5 → 0.2.1. There is no `v0.2.0`
49
+ tag. The entry is kept for the history of the work, not as a release.
50
+ - **0.4.3** and **0.5.0** are real code milestones on `main` -- `pyproject.toml`
51
+ declares each -- but neither was uploaded. The published sequence runs 0.4.2
52
+ (2026-07-20) straight to 0.6.0 (2026-07-21). Both are tagged, and both tags say
53
+ `NEVER PUBLISHED TO PyPI`.
54
+
55
+ ### Releases missing from this file
56
+
57
+ **0.1.1, 0.1.2, 0.1.3 and 0.1.4 were published to PyPI but have no entry below.**
58
+ They are tagged, and their scope can be read from the tags and the commit history, but
59
+ this file jumps 0.1.0 → 0.2.0 → 0.1.5 and does not describe them. Reconstructing four
60
+ changelog entries a year after the fact would be invention rather than record, so the
61
+ gap is documented here instead of filled in.
62
+
63
+ ## [0.6.2] - 2026-09-18
64
+
65
+ Packaging, citation and repository metadata. **No change to `fastlsq/`** -- the
66
+ importable library is byte-identical to 0.6.1, and the wheel is unchanged. The
67
+ sdist gains one example and its test.
68
+
69
+ ### Added
70
+
71
+ - **`examples/stealth_navigation.py`** -- a FastLSQ world model refit inside a
72
+ control loop. A drone crosses a radar interference field sensing only a
73
+ five-point cross at its own position; every three steps it refits a surrogate
74
+ over everything sensed so far (one Tikhonov least-squares solve, 6.1 ms median)
75
+ and steers on that surrogate's *analytic* gradient. The ablation is the
76
+ argument: with the gradient term off, driving straight at the goal trips the
77
+ detector at step 133 of an otherwise 275-step crossing.
78
+
79
+ `tests/test_stealth_navigation.py` checks the analytic gradient against finite
80
+ differences and asserts the ablation in both directions.
81
+
82
+ The project website quoted figures for this demo, but no script in the
83
+ repository produced them. Now the numbers are output rather than assertion.
84
+
85
+ - **Citation and archive metadata.** `CITATION.cff` drives GitHub's *Cite this
86
+ repository* widget, with `preferred-citation` pointing at the paper.
87
+ `.zenodo.json` describes the Zenodo deposit -- title, abstract, ORCID,
88
+ affiliation, keywords, and related identifiers for the preprint, the repository
89
+ and PyPI.
90
+
91
+ Zenodo reads `.zenodo.json` from the **tagged commit's tree**, and no earlier tag
92
+ contained it, so every archived version up to 0.6.1 carries GitHub's
93
+ auto-generated metadata instead. This is the first tag that fixes that.
94
+
95
+ - **`scripts/add_zenodo_doi.py`** writes a Zenodo concept DOI into `README.md`,
96
+ `CITATION.cff` and `.zenodo.json` consistently; idempotent, with a `--check` mode.
97
+ - **`scripts/sync_release_notes.py`** flattens Markdown tables in GitHub Release
98
+ bodies. Zenodo turns a release body into the archived record's description and
99
+ its converter does not understand tables, so a table arrives as one unreadable
100
+ run of pipes -- the first thing anyone following the DOI sees. `CHANGELOG.md`
101
+ keeps its tables, which GitHub renders correctly.
102
+ - **`docs/RELEASING.md`** -- the release procedure, the Zenodo behaviour worth
103
+ knowing in advance, and when JOSS actually requires a DOI (at acceptance, not at
104
+ submission).
105
+
106
+ ### Changed
107
+
108
+ - The Zenodo concept DOI [10.5281/zenodo.22830737](https://doi.org/10.5281/zenodo.22830737)
109
+ is recorded in the README badge and citation section, and in `CITATION.cff`.
110
+ - `pyproject.toml` `Homepage` is now <https://fastlsq.com>; `Repository` still
111
+ points at GitHub, so PyPI shows both. The 0.6.1 upload predated this and shows
112
+ the repository as its homepage.
113
+ - The 0.6.0 feature table's third column is headed "Benefit".
114
+
115
+ ### Removed
116
+
117
+ - **The website.** `site/` and its GitHub Pages workflow are gone: this repository
118
+ is the Python package, and the site is maintained separately at
119
+ <https://fastlsq.com>. The old copy remains recoverable from history
120
+ (`git checkout 8392f6e -- site/`).
121
+
122
+ ## [0.6.1] - 2026-09-18
123
+
124
+ A correctness and packaging pass. No published number changes, and no API is
125
+ removed -- but three of the fixes below were silent wrong answers, not errors.
126
+
127
+ ### Fixed -- silent wrong answers
128
+
129
+ - **kernels** -- `SeparableKernelOperator.inner_products` keyed its cache on
130
+ `(id(basis), n_features)`. A freed basis' address is reused by the next one, and
131
+ `LearnableFastLSQ.basis` builds a new basis object on every access, so a learnable fit
132
+ of a Fredholm problem could train on another basis' operators (measured error of the
133
+ assembled block 0.083 on entries of about 0.05, with no warning). The key is now the
134
+ storage pointer and version counter of `W` and `b`, plus a weak reference to the basis.
135
+ - **linalg** -- `method="qr"` on a rank-deficient matrix returned garbage with no
136
+ exception and no warning (`||x|| = 7e18`, residual `6e4`, where `svd` gives `59` and
137
+ `1e-8`). The diagonal of `R` is now tested against `rcond` and warned about.
138
+ - **learnable** -- `driver="gelsd"` was hard-coded, and torch accepts the rank-revealing
139
+ LAPACK drivers on CPU only, so the learnable path could not run off CPU. Now CPU-only.
140
+
141
+ ### Fixed -- missing operators
142
+
143
+ - **augment** -- `AugmentedBasis` gained `hessian_diag`, `biharmonic`, `advection` and
144
+ `multi_integral`, with the closed forms for the polynomial columns.
145
+ `MultiIntegralOperator.apply(aug, x)` used to raise `AttributeError`, so the `Wave1D`,
146
+ `Wave2D_MS` and `Maxwell2D_TM` problems could not take an augmented basis at all.
147
+ - **basis** -- a 0-d (scalar) symbol is a constant multiplier, not an error.
148
+ - **solvers** -- `add_block(scale=...)` accepts a tuple or tensor of per-axis scales, not
149
+ just a list or ndarray.
150
+ - **newton** -- when no backtracked step satisfied Armijo, the loop restored `beta` and
151
+ then recomputed the identical step until `max_iter`, while `history` recorded the last
152
+ alpha tried as if the step had been taken. It now stops, records the rejection as
153
+ `step_size 0.0`, and tags every exit with a stop reason.
154
+ - **kernels** -- `from_inner_products` objects now raise a clear error from
155
+ `check_quadrature` and `degenerate_eigenvalues` instead of failing inside `leggauss(0)`.
156
+
157
+ ### Changed -- diagnostics and documentation
158
+
159
+ - **linalg** -- `return_info` now carries `method_used`, because `"auto"` picks its
160
+ back-end at run time; on CPU with `mu = 0` it never runs QR (Cholesky probe, then
161
+ `gelsd`), which the module docstring claimed otherwise. The docstring now describes what
162
+ the code does.
163
+ - **linalg/api** -- `rank_used` is documented as what it is: a post-hoc count of singular
164
+ values above `rcond * sigma_max`, not the rank the back-end worked with.
165
+ - **newton** -- the convergence test is relative to the first residual, as the code always
166
+ did; the docstring said absolute.
167
+ - **kernels** -- `degenerate_eigenvalues` no longer runs a full quadrature for inner
168
+ products it discards, and its docstring no longer claims the values depend on the basis.
169
+ - **basis** -- the `1/sqrt(N)` normalisation convention is documented, including the
170
+ differing defaults of `solve_linear` and `solve_nonlinear`.
171
+
172
+ ### Tests
173
+
174
+ - `test_symbol` used `np.trapz`, removed in NumPy 2, so the fractional-Laplacian check
175
+ against the singular-integral definition silently never ran on a current install. With
176
+ `np.trapezoid` the suite goes from 202 passed / 3 failed to 205 passed.
177
+ - New `tests/test_closed_forms_property.py`: 46 property tests checking every closed form
178
+ against an independent reference (autograd, Gauss-Legendre quadrature, the analytic
179
+ characteristic value) at random dimensions, feature counts, bandwidths and orders,
180
+ including the cache-invalidation regression above.
181
+ - `test_version` compared `fastlsq.__version__` against a hard-coded literal, so it had
182
+ to be edited on every release and never caught anything. It now compares
183
+ `__version__` against the version in `pyproject.toml`, which is the drift that can
184
+ actually happen.
185
+ - Full suite: 251 passed.
186
+
187
+ ### Packaging and repository
188
+
189
+ - **`scipy` is declared as a test dependency.** Eight scripts under `examples/` use it for
190
+ their reference solvers, and `tests/test_grad_shafranov.py` and `tests/test_grid_swing.py`
191
+ import those scripts -- but `scipy` appeared only in the `battery` extra, never in `dev`
192
+ or `requirements.txt`. `pip install -e ".[dev]"` into a clean environment produced six
193
+ errors; the suite passed locally only where scipy happened to be installed for other
194
+ reasons. Found by the new CI on its first run.
195
+ - `fastlsq/py.typed` is now actually shipped. `[tool.setuptools.package-data]` had
196
+ declared it since 0.4.0, but the file did not exist, so the package advertised PEP 561
197
+ inline typing that type checkers never saw.
198
+ - The build requirement is `setuptools>=77.0`. `license = "MIT"` is the PEP 639 SPDX
199
+ expression form, which setuptools only understands from 77.0 on; the pin said `>=68.0`.
200
+ - Continuous integration runs the suite on Python 3.9 through 3.12 (`.github/workflows/tests.yml`).
201
+ - The JOSS submission draft (`paper.md`, `paper.bib`) is tracked in the repository.
202
+ - Two 0.5.x changelog entries advertised example scripts under `examples/inverse/` that
203
+ were never shipped; both entries now say so.
204
+
205
+ ### Fixed -- documentation
206
+
207
+ - **The citation in `README.md` had the wrong paper title.** It read *"FastLSQ: A
208
+ Framework for One-Shot PDE Solving"*; the actual title of arXiv:2602.10541 is
209
+ *"FastLSQ: Solving PDEs in One Shot via Fourier Features with Exact Analytical
210
+ Derivatives"*. Anyone who copied that BibTeX block produced a wrong bibliography
211
+ entry. The block now matches the arXiv record and carries the DOI.
212
+ - `paper.bib` cited the same preprint without the `FastLSQ:` title prefix. Corrected to
213
+ the arXiv title, so the JOSS paper and the README agree.
214
+
215
+ ### Added -- release tags and README
216
+
217
+ - Release tags for every version, and the *Release tags* section above documenting how
218
+ they were reconstructed, which three are approximate, and which documented versions
219
+ were never released.
220
+ - `README.md`: status badges (CI, PyPI, Python versions, licence, arXiv); the
221
+ dependency and optional-extras list; a **Development** section covering the test
222
+ suite and CI; a **Releases and versioning** section; and a pointer to the JOSS paper
223
+ draft.
224
+
225
+ ### Changed -- README
226
+
227
+ - `README.md`: the BerkeleyLab ATAP talk link moved from above the project title, where
228
+ it preceded the description, into the **Paper** section with the other references.
229
+
5
230
  ## [0.6.0] - 2026-07-21
6
231
 
7
232
  Completes the operator taxonomy: multi-axis integrals, separable kernels, and
@@ -10,7 +235,7 @@ Fredholm equations, with the integral-equation problems promoted into the same
10
235
 
11
236
  ### New features
12
237
 
13
- | Feature | API | What it buys |
238
+ | Feature | API | Benefit |
14
239
  |---|---|---|
15
240
  | Multi-axis integrals | `MultiIntegralOperator`, `SinusoidalBasis.multi_integral` | Integrate over any subset of axes at once, each independently definite or Volterra — area/volume functionals and mixed "definite in space, running in time" memory terms that a single-axis operator cannot express |
16
241
  | Separable (degenerate) kernels | `SeparableKernelOperator` | `K(x,y) = Σ g_m(x) h_m(y)` assembles as a rank-`R` product `G @ C`; the inner products `C` are computed once, independent of the collocation points |
@@ -361,8 +586,10 @@ augmentation columns (§2.3).
361
586
  Gauss--Hermite quadrature of the slice integral to machine precision in d = 2, 3, 4
362
587
  (≤4e-13 observed), that autodiff of the rows wrt `c` matches finite differences, and
363
588
  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.
589
+ LSQ. *(Correction, 0.6.1: this entry also announced an
590
+ `examples/inverse/tomography_projection.py` demonstrating the full reconstruction plus
591
+ the differentiable-optics gradient. That example was never shipped; the reconstruction
592
+ and the gradient check live in `tests/test_projection.py` instead.)*
366
593
 
367
594
  ### Scope (honest)
368
595
 
@@ -401,9 +628,11 @@ augmentation columns (§2.3).
401
628
  linear-least-squares design matrix; coefficients stay differentiable so learnable
402
629
  integral-term coefficients train through the solve. All three exported from `fastlsq`.
403
630
  - **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`
631
+ `u'(x) + ∫_0^x u ds = f`, rel-L2 ~3e-11). *(Correction, 0.6.1: this entry also
632
+ announced an `examples/inverse/inverse_memory_kernel.py` recovering an unknown memory
633
+ strength λ from noisy data via AdamW through the differentiable solve. That example was
634
+ never shipped; gradient flow through the solve is covered by `tests/test_integral.py`.)*
635
+ New `tests/test_integral.py`
407
636
  asserts ∫-then-∂ round-trips to identity, Volterra/definite match quadrature, the DC
408
637
  guard stays finite, and gradients flow to learnable coefficients.
409
638
 
@@ -0,0 +1,53 @@
1
+ cff-version: 1.2.0
2
+ title: "FastLSQ: One-shot PDE solving via Fourier features with exact analytical derivatives"
3
+ message: >-
4
+ If you use this software, please cite the paper below. If you need to cite a
5
+ specific archived version of the code, use the Zenodo DOI for that version.
6
+ type: software
7
+ authors:
8
+ - family-names: Sulc
9
+ given-names: Antonin
10
+ orcid: "https://orcid.org/0000-0001-7767-778X"
11
+ affiliation: "Lawrence Berkeley National Laboratory, Berkeley, CA, United States"
12
+ repository-code: "https://github.com/sulcantonin/FastLSQ"
13
+ url: "https://fastlsq.com"
14
+ repository-artifact: "https://pypi.org/project/FastLSQ/"
15
+ abstract: >-
16
+ FastLSQ solves linear partial differential, integral and integro-differential
17
+ equations in a single least-squares call, by expanding the unknown in a basis of
18
+ random Fourier features whose derivatives and integrals are available in closed
19
+ form. Because every feature is a plane wave, a linear operator maps the basis to
20
+ another explicit matrix: no mesh, no automatic differentiation, no iterative
21
+ training. The closed-form derivatives make the fitted field a differentiable
22
+ surrogate whose operator factors once, so inverse problems and control loops can
23
+ afford thousands of forward solves.
24
+ keywords:
25
+ - partial differential equations
26
+ - integral equations
27
+ - inverse problems
28
+ - Fourier features
29
+ - least squares
30
+ - closed-form derivatives
31
+ - differentiable surrogate
32
+ - scientific machine learning
33
+ - PyTorch
34
+ license: MIT
35
+ version: 0.6.2
36
+ date-released: "2026-09-18"
37
+
38
+ identifiers:
39
+ - type: doi
40
+ value: 10.5281/zenodo.22830737
41
+ description: Concept DOI — resolves to the latest archived version
42
+
43
+ preferred-citation:
44
+ type: article
45
+ title: "FastLSQ: Solving PDEs in One Shot via Fourier Features with Exact Analytical Derivatives"
46
+ authors:
47
+ - family-names: Sulc
48
+ given-names: Antonin
49
+ orcid: "https://orcid.org/0000-0001-7767-778X"
50
+ year: 2026
51
+ doi: "10.48550/arXiv.2602.10541"
52
+ url: "https://arxiv.org/abs/2602.10541"
53
+ journal: "arXiv preprint arXiv:2602.10541"
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: FastLSQ
3
- Version: 0.6.0
3
+ Version: 0.6.2
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
7
- Project-URL: Homepage, https://github.com/sulcantonin/FastLSQ
7
+ Project-URL: Homepage, https://fastlsq.com
8
8
  Project-URL: Repository, https://github.com/sulcantonin/FastLSQ
9
9
  Project-URL: Paper, https://arxiv.org/abs/2602.10541
10
10
  Project-URL: Bug Tracker, https://github.com/sulcantonin/FastLSQ/issues
@@ -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,13 @@ 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)
50
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.22830737.svg)](https://doi.org/10.5281/zenodo.22830737)
51
+ [![site](https://img.shields.io/badge/site-fastlsq.com-1f3df5.svg)](https://fastlsq.com)
46
52
 
47
53
  <p align="center">
48
54
  <img src="https://raw.githubusercontent.com/sulcantonin/FastLSQ/main/misc/fastlsq_teaser.png" alt="FastLSQ method overview" width="400"/>
@@ -68,14 +74,23 @@ regularisation, 1/sqrt(N) feature normalisation, and continuation/homotopy.
68
74
  pip install fastlsq
69
75
  ```
70
76
 
71
- For development (includes testing and build tools):
77
+ Requires Python 3.9+, PyTorch 2.0+, NumPy 1.24+ and Matplotlib 3.7+. There is no
78
+ compiled extension and no mesh library: everything runs on the PyTorch tensor stack,
79
+ on CPU, CUDA or Apple-MPS.
80
+
81
+ For development -- test runner, the SciPy reference solvers the example scripts
82
+ compare against, and the build tools:
72
83
 
73
84
  ```bash
74
85
  git clone https://github.com/sulcantonin/FastLSQ.git
75
86
  cd FastLSQ
76
87
  pip install -e ".[dev]"
88
+ pytest tests/ # 251 tests, about 30 s on a laptop CPU
77
89
  ```
78
90
 
91
+ Optional extras: `.[battery]` for the battery-degradation examples (`progpy`),
92
+ `.[lightning]` for the PyTorch Lightning training loop.
93
+
79
94
  ## Quick start
80
95
 
81
96
  ### Solve a linear PDE in one line
@@ -516,26 +531,62 @@ See `examples/add_your_own_pde.py` for the complete tutorial.
516
531
  - **PyTorch Lightning**: Integration for training loops
517
532
  - **20+ benchmark problems**: Linear, nonlinear, and regression-mode PDEs
518
533
 
534
+ ## Development
535
+
536
+ ```bash
537
+ pip install -e ".[dev]"
538
+ pytest tests/
539
+ ```
540
+
541
+ The suite is 251 tests and runs in about 30 seconds on a laptop CPU. Every closed
542
+ form -- derivative, integral, Fourier symbol, projection -- is checked against an
543
+ independent reference (autograd, Gauss-Legendre quadrature, or the analytic value) in
544
+ `tests/test_closed_forms_property.py`, so a wrong closed form fails the suite rather
545
+ than silently returning a plausible number.
546
+
547
+ [Continuous integration](https://github.com/sulcantonin/FastLSQ/actions/workflows/tests.yml)
548
+ runs the suite on Python 3.9, 3.10, 3.11 and 3.12, and separately builds the sdist and
549
+ wheel and checks their metadata.
550
+
551
+ ## Releases and versioning
552
+
553
+ Released versions are on [PyPI](https://pypi.org/project/FastLSQ/) and tagged in this
554
+ repository as `vMAJOR.MINOR.PATCH`. `CHANGELOG.md` documents every release.
555
+
556
+ The tags for 0.1.0 through 0.6.0 were reconstructed after the fact, since the project
557
+ was published to PyPI for its first year without tagging. Each tag was matched to its
558
+ commit by comparing the commit's `fastlsq/*.py` sources against the sdist actually
559
+ published, so most are byte-for-byte exact; the three that are not say so in the tag
560
+ message. See the *Release tags* note at the top of `CHANGELOG.md`.
561
+
519
562
  ## Paper
520
563
 
521
- The full preprint is available on [arXiv](https://arxiv.org/abs/2602.10541)
564
+ The project site, with interactive demos, is at [fastlsq.com](https://fastlsq.com). The preprint is on [arXiv](https://arxiv.org/abs/2602.10541). A software paper for the
565
+ [Journal of Open Source Software](https://joss.theoj.org/) is drafted in
566
+ [`paper.md`](paper.md).
567
+
568
+ There is also a [BerkeleyLab ATAP talk](https://github.com/sulcantonin/FastLSQ/raw/main/presentations/ATAP_Sulc_20260324.pptx)
569
+ covering the method and the accelerator-physics applications.
522
570
 
523
571
  ## Citing this work
524
572
 
525
573
  If you use FastLSQ in your research, please cite:
526
574
 
527
575
  ```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},
576
+ @misc{sulc2026fastlsq,
577
+ author = {Sulc, Antonin},
578
+ title = {{FastLSQ}: Solving {PDEs} in One Shot via {Fourier} Features with Exact Analytical Derivatives},
579
+ year = {2026},
580
+ eprint = {2602.10541},
581
+ archivePrefix = {arXiv},
582
+ primaryClass = {math.NA},
583
+ doi = {10.48550/arXiv.2602.10541},
584
+ url = {https://arxiv.org/abs/2602.10541}
536
585
  }
537
586
  ```
538
587
 
588
+ To cite a specific archived version of the code rather than the paper, use the Zenodo concept DOI [10.5281/zenodo.22830737](https://doi.org/10.5281/zenodo.22830737), which always resolves to the most recent release.
589
+
539
590
  ## License
540
591
 
541
592
  This project is licensed under the MIT License -- see [LICENSE](LICENSE) for details.
@@ -1,4 +1,5 @@
1
1
  CHANGELOG.md
2
+ CITATION.cff
2
3
  LICENSE
3
4
  MANIFEST.in
4
5
  README.md
@@ -34,6 +35,7 @@ examples/run_all_extensions.py
34
35
  examples/run_linear.py
35
36
  examples/run_nonlinear.py
36
37
  examples/sdf_domains.py
38
+ examples/stealth_navigation.py
37
39
  examples/tutorial_basic.py
38
40
  examples/tutorial_nonlinear.py
39
41
  examples/vector_basis_stream_vorticity.py
@@ -94,6 +96,7 @@ fastlsq/lightning.py
94
96
  fastlsq/linalg.py
95
97
  fastlsq/newton.py
96
98
  fastlsq/plotting.py
99
+ fastlsq/py.typed
97
100
  fastlsq/solvers.py
98
101
  fastlsq/tuning.py
99
102
  fastlsq/utils.py
@@ -108,6 +111,7 @@ tests/test_augment.py
108
111
  tests/test_basic.py
109
112
  tests/test_benchmarks_inverse.py
110
113
  tests/test_block.py
114
+ tests/test_closed_forms_property.py
111
115
  tests/test_derivatives.py
112
116
  tests/test_device.py
113
117
  tests/test_diagnostics_timing.py
@@ -121,5 +125,6 @@ tests/test_multi_integral.py
121
125
  tests/test_orbit_hill.py
122
126
  tests/test_problems_integral.py
123
127
  tests/test_projection.py
128
+ tests/test_stealth_navigation.py
124
129
  tests/test_symbol.py
125
130
  tests/test_vector_basis.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
 
@@ -1,7 +1,9 @@
1
1
  include LICENSE
2
2
  include README.md
3
3
  include CHANGELOG.md
4
+ include CITATION.cff
4
5
  include requirements.txt
6
+ include fastlsq/py.typed
5
7
  recursive-include examples *.py
6
8
  recursive-include tests *.py
7
9
  recursive-exclude * __pycache__
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: FastLSQ
3
- Version: 0.6.0
3
+ Version: 0.6.2
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
7
- Project-URL: Homepage, https://github.com/sulcantonin/FastLSQ
7
+ Project-URL: Homepage, https://fastlsq.com
8
8
  Project-URL: Repository, https://github.com/sulcantonin/FastLSQ
9
9
  Project-URL: Paper, https://arxiv.org/abs/2602.10541
10
10
  Project-URL: Bug Tracker, https://github.com/sulcantonin/FastLSQ/issues
@@ -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,13 @@ 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)
50
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.22830737.svg)](https://doi.org/10.5281/zenodo.22830737)
51
+ [![site](https://img.shields.io/badge/site-fastlsq.com-1f3df5.svg)](https://fastlsq.com)
46
52
 
47
53
  <p align="center">
48
54
  <img src="https://raw.githubusercontent.com/sulcantonin/FastLSQ/main/misc/fastlsq_teaser.png" alt="FastLSQ method overview" width="400"/>
@@ -68,14 +74,23 @@ regularisation, 1/sqrt(N) feature normalisation, and continuation/homotopy.
68
74
  pip install fastlsq
69
75
  ```
70
76
 
71
- For development (includes testing and build tools):
77
+ Requires Python 3.9+, PyTorch 2.0+, NumPy 1.24+ and Matplotlib 3.7+. There is no
78
+ compiled extension and no mesh library: everything runs on the PyTorch tensor stack,
79
+ on CPU, CUDA or Apple-MPS.
80
+
81
+ For development -- test runner, the SciPy reference solvers the example scripts
82
+ compare against, and the build tools:
72
83
 
73
84
  ```bash
74
85
  git clone https://github.com/sulcantonin/FastLSQ.git
75
86
  cd FastLSQ
76
87
  pip install -e ".[dev]"
88
+ pytest tests/ # 251 tests, about 30 s on a laptop CPU
77
89
  ```
78
90
 
91
+ Optional extras: `.[battery]` for the battery-degradation examples (`progpy`),
92
+ `.[lightning]` for the PyTorch Lightning training loop.
93
+
79
94
  ## Quick start
80
95
 
81
96
  ### Solve a linear PDE in one line
@@ -516,26 +531,62 @@ See `examples/add_your_own_pde.py` for the complete tutorial.
516
531
  - **PyTorch Lightning**: Integration for training loops
517
532
  - **20+ benchmark problems**: Linear, nonlinear, and regression-mode PDEs
518
533
 
534
+ ## Development
535
+
536
+ ```bash
537
+ pip install -e ".[dev]"
538
+ pytest tests/
539
+ ```
540
+
541
+ The suite is 251 tests and runs in about 30 seconds on a laptop CPU. Every closed
542
+ form -- derivative, integral, Fourier symbol, projection -- is checked against an
543
+ independent reference (autograd, Gauss-Legendre quadrature, or the analytic value) in
544
+ `tests/test_closed_forms_property.py`, so a wrong closed form fails the suite rather
545
+ than silently returning a plausible number.
546
+
547
+ [Continuous integration](https://github.com/sulcantonin/FastLSQ/actions/workflows/tests.yml)
548
+ runs the suite on Python 3.9, 3.10, 3.11 and 3.12, and separately builds the sdist and
549
+ wheel and checks their metadata.
550
+
551
+ ## Releases and versioning
552
+
553
+ Released versions are on [PyPI](https://pypi.org/project/FastLSQ/) and tagged in this
554
+ repository as `vMAJOR.MINOR.PATCH`. `CHANGELOG.md` documents every release.
555
+
556
+ The tags for 0.1.0 through 0.6.0 were reconstructed after the fact, since the project
557
+ was published to PyPI for its first year without tagging. Each tag was matched to its
558
+ commit by comparing the commit's `fastlsq/*.py` sources against the sdist actually
559
+ published, so most are byte-for-byte exact; the three that are not say so in the tag
560
+ message. See the *Release tags* note at the top of `CHANGELOG.md`.
561
+
519
562
  ## Paper
520
563
 
521
- The full preprint is available on [arXiv](https://arxiv.org/abs/2602.10541)
564
+ The project site, with interactive demos, is at [fastlsq.com](https://fastlsq.com). The preprint is on [arXiv](https://arxiv.org/abs/2602.10541). A software paper for the
565
+ [Journal of Open Source Software](https://joss.theoj.org/) is drafted in
566
+ [`paper.md`](paper.md).
567
+
568
+ There is also a [BerkeleyLab ATAP talk](https://github.com/sulcantonin/FastLSQ/raw/main/presentations/ATAP_Sulc_20260324.pptx)
569
+ covering the method and the accelerator-physics applications.
522
570
 
523
571
  ## Citing this work
524
572
 
525
573
  If you use FastLSQ in your research, please cite:
526
574
 
527
575
  ```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},
576
+ @misc{sulc2026fastlsq,
577
+ author = {Sulc, Antonin},
578
+ title = {{FastLSQ}: Solving {PDEs} in One Shot via {Fourier} Features with Exact Analytical Derivatives},
579
+ year = {2026},
580
+ eprint = {2602.10541},
581
+ archivePrefix = {arXiv},
582
+ primaryClass = {math.NA},
583
+ doi = {10.48550/arXiv.2602.10541},
584
+ url = {https://arxiv.org/abs/2602.10541}
536
585
  }
537
586
  ```
538
587
 
588
+ To cite a specific archived version of the code rather than the paper, use the Zenodo concept DOI [10.5281/zenodo.22830737](https://doi.org/10.5281/zenodo.22830737), which always resolves to the most recent release.
589
+
539
590
  ## License
540
591
 
541
592
  This project is licensed under the MIT License -- see [LICENSE](LICENSE) for details.