FastLSQ 0.4.2__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.
- {fastlsq-0.4.2 → fastlsq-0.6.1}/CHANGELOG.md +414 -5
- {fastlsq-0.4.2 → fastlsq-0.6.1}/FastLSQ.egg-info/PKG-INFO +191 -13
- {fastlsq-0.4.2 → fastlsq-0.6.1}/FastLSQ.egg-info/SOURCES.txt +13 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/FastLSQ.egg-info/requires.txt +1 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/MANIFEST.in +1 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/PKG-INFO +191 -13
- {fastlsq-0.4.2 → fastlsq-0.6.1}/README.md +189 -12
- fastlsq-0.6.1/examples/integral_equations.py +93 -0
- fastlsq-0.6.1/examples/sdf_domains.py +126 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/__init__.py +54 -1
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/api.py +5 -1
- fastlsq-0.6.1/fastlsq/augment.py +558 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/basis.py +475 -7
- fastlsq-0.6.1/fastlsq/geometry.py +1029 -0
- fastlsq-0.6.1/fastlsq/kernels.py +330 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/learnable.py +49 -19
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/linalg.py +63 -23
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/newton.py +29 -6
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/problems/__init__.py +7 -0
- fastlsq-0.6.1/fastlsq/problems/integral.py +284 -0
- fastlsq-0.6.1/fastlsq/py.typed +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/solvers.py +7 -5
- {fastlsq-0.4.2 → fastlsq-0.6.1}/pyproject.toml +5 -2
- {fastlsq-0.4.2 → fastlsq-0.6.1}/requirements.txt +1 -0
- fastlsq-0.6.1/tests/test_augment.py +299 -0
- fastlsq-0.6.1/tests/test_closed_forms_property.py +323 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/tests/test_diagnostics_timing.py +4 -1
- fastlsq-0.6.1/tests/test_geometry_sdf.py +314 -0
- fastlsq-0.6.1/tests/test_kernels.py +208 -0
- fastlsq-0.6.1/tests/test_multi_integral.py +146 -0
- fastlsq-0.6.1/tests/test_problems_integral.py +184 -0
- fastlsq-0.6.1/tests/test_symbol.py +278 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/tests/test_vector_basis.py +15 -1
- fastlsq-0.4.2/fastlsq/geometry.py +0 -243
- {fastlsq-0.4.2 → fastlsq-0.6.1}/FastLSQ.egg-info/dependency_links.txt +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/FastLSQ.egg-info/top_level.txt +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/LICENSE +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/add_your_own_pde.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/benchmark_comparison.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/custom_features.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/fred_sde.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/fred_sde_fastlsq.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/gaia_potential.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/gaia_potential_fastlsq.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/horizons_ephemeris.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/numerai_alpha.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/numerai_alpha_fastlsq.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/run_all_fastlsq.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/__init__.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/_alsu_lattice.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/_common.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/run_all.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s01_beamloss_ode.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s01_betatron_tune.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s01_green_fff.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s01_hill_ivp.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s01_observe_fit_act_simulator.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s01_orbit_inverse.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s01_passive_loco.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s01_perturbed_hill.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s01_sofb_observe_fit_act.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s01_streaming_archive_growth.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s01_synchrotron_ode.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s01_tides_3months.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s01_topoff_impulse.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s01_visualize.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s02_plasma_wakefield.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s03_synchrobetatron.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s04_sunspots.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s05_helioseismology.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s06_tides.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s07_iers_earth_rotation.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s08_mauna_loa_co2.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s09_enso_qbo.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s10_pulsar_timing.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s11_modal_analysis.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s12_mems_resonator.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s13_variable_stars_kepler.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s14_eeg.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/scenarios/s15_circadian.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/extras/spectral_expansion.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/grad_shafranov.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/grid_inverse.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/grid_rl_control.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/grid_swing.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/gs_inverse.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/gs_rl_control.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/integro_differential_demo.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/inverse_heat_source.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/inverse_magnetostatics.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/inverse_source_position.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/learnable_helmholtz.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/memory_diffusion.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/orbit_hill.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/orbit_inverse.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/orbit_rl.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/pde_discovery.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/rlc_integro_differential.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/run_all_extensions.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/run_linear.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/run_nonlinear.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/tutorial_basic.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/tutorial_nonlinear.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/examples/vector_basis_stream_vorticity.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/benchmark.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/block.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/device.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/diagnostics.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/export.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/lightning.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/plotting.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/problems/linear.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/problems/nonlinear.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/problems/regression.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/tuning.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/utils.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/vector.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/fastlsq/viz.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/setup.cfg +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/tests/test_basic.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/tests/test_benchmarks_inverse.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/tests/test_block.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/tests/test_derivatives.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/tests/test_device.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/tests/test_grad_shafranov.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/tests/test_grid_swing.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/tests/test_integral.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/tests/test_learnable.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/tests/test_orbit_hill.py +0 -0
- {fastlsq-0.4.2 → fastlsq-0.6.1}/tests/test_projection.py +0 -0
|
@@ -2,6 +2,411 @@
|
|
|
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
|
+
|
|
154
|
+
## [0.6.0] - 2026-07-21
|
|
155
|
+
|
|
156
|
+
Completes the operator taxonomy: multi-axis integrals, separable kernels, and
|
|
157
|
+
Fredholm equations, with the integral-equation problems promoted into the same
|
|
158
|
+
`solve_linear` harness as the PDEs so they report in one table.
|
|
159
|
+
|
|
160
|
+
### New features
|
|
161
|
+
|
|
162
|
+
| Feature | API | What it buys |
|
|
163
|
+
|---|---|---|
|
|
164
|
+
| 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 |
|
|
165
|
+
| 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 |
|
|
166
|
+
| Fredholm, second kind | `fredholm_second_kind` | `u − λ∫K u = f` as `I − λK`, solved in the same single least squares as everything else |
|
|
167
|
+
| Kernel diagnostics | `degenerate_eigenvalues`, `check_quadrature` | The `λ` at which the equation is singular, and whether the inner-product quadrature actually resolves the basis — both failure modes that otherwise show up only as a bad fit |
|
|
168
|
+
| Integral Problem classes | `fastlsq.problems.integral` | Four integral / integro-differential problems with **closed-form** solutions, running through `solve_linear` |
|
|
169
|
+
|
|
170
|
+
### Added
|
|
171
|
+
|
|
172
|
+
- **`MultiIntegralOperator` / `SinusoidalBasis.multi_integral`.** The plane wave
|
|
173
|
+
factorises over axes, `e^{iW·x} = ∏_k e^{iW_k x_k}`, so a multi-axis integral
|
|
174
|
+
is a *product* of the same numerically stable one-axis factors already used by
|
|
175
|
+
`definite_integral`, and the phase collapses to the value at the per-axis
|
|
176
|
+
midpoint:
|
|
177
|
+
|
|
178
|
+
∫∫ φ_j ∏_{k∈S} dx_k = [∏_{k∈S} Δ_k · sinc(W_k Δ_k / 2π)] · sin(Z_mid)
|
|
179
|
+
|
|
180
|
+
Exact, quadrature-free, and finite for near-DC features on every axis. With
|
|
181
|
+
`|S| = 1` it reproduces `IntegralOperator.definite` bit-for-bit (verified to
|
|
182
|
+
1e-14); against tensor-product quadrature in 2-D and 3-D it agrees to the
|
|
183
|
+
quadrature's own error. Order is 1 per axis — a repeated integral along one
|
|
184
|
+
axis remains `IntegralOperator`, whose Cauchy/Taylor branches handle the
|
|
185
|
+
small-`W` cancellation a naive repeated product would suffer.
|
|
186
|
+
|
|
187
|
+
- **`SeparableKernelOperator` (`fastlsq/kernels.py`).** A degenerate kernel
|
|
188
|
+
collapses the integral operator to `Σ_m g_m(x) ∫ h_m u`, so acting on the
|
|
189
|
+
basis needs only the `R × N` matrix `C_{mj} = ∫ h_m φ_j`. Assembly is
|
|
190
|
+
`G(x) @ C` — a rank-`R` factorisation instead of an `M × M` kernel evaluation,
|
|
191
|
+
and the assembled block's rank is exactly `R` (verified).
|
|
192
|
+
|
|
193
|
+
Quadrature enters in exactly one place, computing `C`, and it is a
|
|
194
|
+
precomputation rather than a per-row cost: tensor-product Gauss-Legendre,
|
|
195
|
+
spectrally convergent for smooth integrands. Because it must still resolve the
|
|
196
|
+
feature oscillation, `check_quadrature` reports the achieved convergence
|
|
197
|
+
(2e-14 for a typical basis; the test suite also pins that it *flags* an
|
|
198
|
+
under-resolved one), and `from_inner_products` accepts analytic `C` to skip
|
|
199
|
+
quadrature entirely.
|
|
200
|
+
|
|
201
|
+
- **`fredholm_second_kind` and `degenerate_eigenvalues`.** Fredholm equations of
|
|
202
|
+
the second kind assemble as `I − λK`. For a rank-`R` degenerate kernel there
|
|
203
|
+
are at most `R` characteristic values, at which the equation is singular;
|
|
204
|
+
`degenerate_eigenvalues` computes them so a near-singular `λ` is detectable
|
|
205
|
+
instead of silently producing a garbage fit. For `K(x,y)=xy` on `[0,1]` it
|
|
206
|
+
recovers the analytic `λ = 3` as `3.0000000000000093`.
|
|
207
|
+
|
|
208
|
+
Validated against degenerate-kernel theory rather than a reference quadrature:
|
|
209
|
+
the exact solution of `u − λ∫₀¹xy·u = f` is `u = f + λcx` with
|
|
210
|
+
`c = ∫yf/(1−λ/3)`, matched to ~1e-8 for `λ` swept from 0.25 to 2.9 (i.e. right
|
|
211
|
+
up to the singular value). `λ` may be an `nn.Parameter`.
|
|
212
|
+
|
|
213
|
+
- **`fastlsq/problems/integral.py`.** `FredholmProductKernel`,
|
|
214
|
+
`FredholmRank2Kernel`, `VolterraSecondKind` and `IntegroDifferentialODE`, each
|
|
215
|
+
with a closed-form solution, running through `solve_linear` exactly as the PDE
|
|
216
|
+
problems do — so integral equations now produce a results table alongside the
|
|
217
|
+
PDEs rather than living only in example scripts. The module docstring writes
|
|
218
|
+
down the (duck-typed, no base class) Problem contract, which was previously
|
|
219
|
+
implicit and inconsistent.
|
|
220
|
+
|
|
221
|
+
Note the second-kind problems return an **empty** `bcs` list: the identity term
|
|
222
|
+
makes them well posed with no boundary rows at all. Only the
|
|
223
|
+
integro-differential ODE, which has a genuine constant of integration, needs
|
|
224
|
+
one.
|
|
225
|
+
|
|
226
|
+
- **Accuracy regressions (`tests/test_problems_integral.py`).** Each problem is
|
|
227
|
+
solved through the real harness and checked against its analytic solution, and
|
|
228
|
+
separately each closed form is verified to satisfy its own equation by
|
|
229
|
+
independent quadrature — so a closed form and an operator cannot be wrong in
|
|
230
|
+
the same direction without being caught.
|
|
231
|
+
|
|
232
|
+
### Measured
|
|
233
|
+
|
|
234
|
+
All five integral-equation problems, through `solve_linear` (300 features, 2000
|
|
235
|
+
collocation points), against their closed-form solutions:
|
|
236
|
+
|
|
237
|
+
| Problem | rel L2 | grad rel L2 | boundary rows |
|
|
238
|
+
|---|---|---|---|
|
|
239
|
+
| Fredholm `K=xy`, λ=0.5 | 5.5e-14 | 5.7e-12 | 0 |
|
|
240
|
+
| Fredholm `K=xy`, λ=2.0 | 3.8e-13 | 3.9e-11 | 0 |
|
|
241
|
+
| Fredholm rank-2, λ=0.4 | 9.1e-14 | 9.5e-12 | 0 |
|
|
242
|
+
| Volterra 2nd kind, λ=1.5 | 8.8e-13 | 1.0e-10 | 0 |
|
|
243
|
+
| Integro-differential ODE, λ=4.0 | 6.2e-16 | 1.7e-14 | 1 |
|
|
244
|
+
|
|
245
|
+
Approaching the kernel's singular value `λ = 3` costs only ~2 orders of
|
|
246
|
+
magnitude: 5.5e-14 at `λ = 0.5`, 6.8e-13 at `λ = 2.9`, 3.9e-12 at `λ = 2.99`.
|
|
247
|
+
|
|
248
|
+
### Documentation
|
|
249
|
+
|
|
250
|
+
- The README now tables the SDF primitives (`sdf_ball`, `sdf_disk`, `sdf_box`,
|
|
251
|
+
`sdf_annulus`, `sdf_lshape`, `sdf_flower`, `sdf_polygon`, `sdf_tokamak`) and
|
|
252
|
+
the CSG combinators (`sdf_union`, `sdf_intersection`, `sdf_difference`,
|
|
253
|
+
`sdf_complement`) by name. They were exported in `__all__` from 0.5.0 but only
|
|
254
|
+
described in prose, so the composable `ψ`-level API was effectively
|
|
255
|
+
undiscoverable — you could find `SDFDomain.disk()` but not `sdf_disk`, and the
|
|
256
|
+
CSG functions not at all.
|
|
257
|
+
- `PolynomialColumns` is likewise named explicitly alongside `AugmentedBasis`.
|
|
258
|
+
|
|
259
|
+
### Changed
|
|
260
|
+
|
|
261
|
+
- **Operator composition is now duck-typed.** `IntegroDifferentialOperator._as_terms`
|
|
262
|
+
and `DiffOperator.__add__` dispatch on the presence of `.apply(basis, x, cache)`
|
|
263
|
+
rather than a fixed `isinstance` tuple, so operators defined in other modules
|
|
264
|
+
(`SeparableKernelOperator`, and any user-defined operator) compose with the
|
|
265
|
+
built-ins without `basis.py` importing them. No change to existing behaviour.
|
|
266
|
+
|
|
267
|
+
## [0.5.0] - 2026-07-21
|
|
268
|
+
|
|
269
|
+
Closes three gaps between what the paper describes and what the package shipped:
|
|
270
|
+
SDF geometry (§2.7), Fourier-symbol operators (§3.4), and explicit polynomial
|
|
271
|
+
augmentation columns (§2.3).
|
|
272
|
+
|
|
273
|
+
### Added
|
|
274
|
+
|
|
275
|
+
- **SDF / membership-oracle geometry (`fastlsq.geometry`).** A domain can now be
|
|
276
|
+
given as any callable `ψ(x)` that is negative inside — no mesh, no analytic
|
|
277
|
+
sampler. `sample_sdf` rejection-samples the interior (unbiased, so the radial
|
|
278
|
+
CDF of a disk is exactly `r²`), `project_to_boundary` lands points on `ψ = 0`,
|
|
279
|
+
and `outward_normal` returns `∇ψ/‖∇ψ‖` via autograd, which is what a Neumann or
|
|
280
|
+
Robin condition needs. `SDFDomain` bundles these with a bounding box and CSG
|
|
281
|
+
composition (`|`, `&`, `-`), plus `neumann_rows` / `robin_rows` that contract
|
|
282
|
+
the analytic basis gradient against the normal into an `(M, N)` block.
|
|
283
|
+
|
|
284
|
+
Shipped domains: `disk`, `ball`, `box`, `annulus` (multiply-connected),
|
|
285
|
+
`lshape` (reentrant corner), `flower` (smooth non-convex), `polygon`, and
|
|
286
|
+
`tokamak` — the D-shaped Miller poloidal cross-section, at the same MAST-U
|
|
287
|
+
scale (`R ∈ [0.6, 1.4]`) as `examples/grad_shafranov.py`, which until now used
|
|
288
|
+
a plain rectangle.
|
|
289
|
+
|
|
290
|
+
Projection is **damped** Newton, `x ← x − ψ∇ψ/‖∇ψ‖²`, with per-point
|
|
291
|
+
backtracking and monotone acceptance. The `1/‖∇ψ‖²` normalisation matters:
|
|
292
|
+
the textbook `x − ψ∇ψ` is valid only for a true distance function, and
|
|
293
|
+
`sdf_flower` has `‖∇ψ‖` spanning 1 to ~10. Undamped, it left
|
|
294
|
+
`|ψ| ≈ 1.5e-1` on the boundary; damped and monotone, 99.9% of interior seeds
|
|
295
|
+
converge to `1e-10`. The residual failures are seeds within `r < 0.2` of the
|
|
296
|
+
centre, where a non-distance `ψ` has no unique nearest boundary point;
|
|
297
|
+
`sample_boundary_sdf` filters them rather than hiding them.
|
|
298
|
+
|
|
299
|
+
- **`SymbolOperator` — Fourier-multiplier (nonlocal) operators.** Every feature
|
|
300
|
+
is a plane wave, so a multiplier `L e^{iξ·x} = m(ξ) e^{iξ·x}` acts *diagonally*
|
|
301
|
+
on the basis: assembling `L` is a per-column rescale, exact to machine
|
|
302
|
+
precision, with no quadrature and no discretisation of the kernel. Operators
|
|
303
|
+
that are hard for mesh methods — the fractional Laplacian is nonlocal with a
|
|
304
|
+
singular kernel, giving a dense ill-conditioned FEM/FD matrix — cost exactly
|
|
305
|
+
what the Laplacian costs here.
|
|
306
|
+
|
|
307
|
+
Factories: `fractional_laplacian(s)`, `riesz_potential(s)`,
|
|
308
|
+
`riesz_transform(k)`, `convolution(k̂)`. The order `s` may be an
|
|
309
|
+
`nn.Parameter`: the symbol is `exp(s·log‖ξ‖²)`, so gradient descent recovers a
|
|
310
|
+
planted order (test recovers `s = 0.65` to `<1e-3`). Composes with `Op` and
|
|
311
|
+
`IntegralOperator` through the usual arithmetic.
|
|
312
|
+
|
|
313
|
+
Verified against independent references, not just self-consistency: `s=1`
|
|
314
|
+
reproduces `−Δ` and `s=2` reproduces `Δ²` bit-exactly; the symbol is checked
|
|
315
|
+
against the **singular-integral definition** including its normalising constant
|
|
316
|
+
`C(1,s)`; and Gaussian convolution matches direct quadrature to `<1e-8`.
|
|
317
|
+
|
|
318
|
+
`ConvolutionOperator` is `SymbolOperator.convolution(k̂)` — this is what
|
|
319
|
+
`examples/memory_diffusion.py:80` was doing by hand with a `(1, N)` broadcast
|
|
320
|
+
coefficient. That hand-rolled version uses `basis.W[0:1, :]`, correct only
|
|
321
|
+
because `d=2` has exactly one spatial axis; a symbol sees all of `W` and
|
|
322
|
+
generalises to any dimension.
|
|
323
|
+
|
|
324
|
+
- **Polynomial / DC augmentation columns (`fastlsq.augment`).** `AugmentedBasis`
|
|
325
|
+
widens a basis with explicit `1, x, x², …` columns carrying **exact** operator
|
|
326
|
+
images — analytic monomial derivatives, antiderivatives (a negative multi-index
|
|
327
|
+
integrates: `x^p → x^{p+1}/(p+1)`), and Cauchy iterated integrals — not a
|
|
328
|
+
zero-derivative stub. It implements the same duck-typed protocol as
|
|
329
|
+
`SinusoidalBasis`, so `Op`, `IntegralOperator` and `SymbolOperator` all work
|
|
330
|
+
unchanged and return `(M, N + n_cols)`.
|
|
331
|
+
|
|
332
|
+
This is what §2.3 means by an integration constant "pinned by an explicit
|
|
333
|
+
polynomial column"; previously the only such column in the repo was hand-rolled
|
|
334
|
+
in `examples/extras/scenarios/s01_beamloss_ode.py`, which had to maintain its
|
|
335
|
+
derivative block by hand.
|
|
336
|
+
|
|
337
|
+
Scope, honestly: a sinusoidal bank with random phases can already approximate a
|
|
338
|
+
constant from near-DC features, so this is an improvement, not a rescue — on
|
|
339
|
+
`u'' = f` with a DC offset it is neutral at `C = 0` and ~12× better at
|
|
340
|
+
`C = 1000`. A non-constant monomial has no function-valued Fourier-multiplier
|
|
341
|
+
image, so `SymbolOperator` on `degree > 0` columns raises rather than inventing
|
|
342
|
+
a value.
|
|
343
|
+
|
|
344
|
+
### Fixed
|
|
345
|
+
|
|
346
|
+
- **`ProjectionOperator`'s docstring claimed the Fourier-symbol operators were
|
|
347
|
+
"already in the package".** They were not, until this release. It now points at
|
|
348
|
+
`SymbolOperator` and states the actual distinction (a symbol acts diagonally;
|
|
349
|
+
a projection row mixes features).
|
|
350
|
+
|
|
351
|
+
### Notes
|
|
352
|
+
|
|
353
|
+
- **Which fractional Laplacian.** The symbol calculus applies the multiplier to
|
|
354
|
+
the global plane-wave extension of the trial function, so
|
|
355
|
+
`fractional_laplacian` is the **whole-space (restricted)** `(−Δ)^s` on `R^d`,
|
|
356
|
+
*not* the spectral or regional variant defined by an eigenbasis of a bounded
|
|
357
|
+
domain. These coincide on `R^d` and differ on a bounded domain. Results should
|
|
358
|
+
not be read as the spectral fractional Laplacian.
|
|
359
|
+
|
|
360
|
+
- Boundary points from `sample_boundary_sdf` are **not** uniform with respect to
|
|
361
|
+
surface measure — they are the pushforward of the uniform box measure under the
|
|
362
|
+
projection, which over-weights convex bulges. Fine for collocation; do not use
|
|
363
|
+
them as quadrature nodes for a surface integral without reweighting.
|
|
364
|
+
|
|
365
|
+
## [0.4.3] - 2026-07-20
|
|
366
|
+
|
|
367
|
+
### Fixed
|
|
368
|
+
|
|
369
|
+
- **The Σ-learner trained uphill: `train_bandwidth` now uses the exact envelope
|
|
370
|
+
gradient.** The outer loss was backpropagated *through* `torch.linalg.lstsq`,
|
|
371
|
+
whose backward carries a `(AᵀA)⁻¹` and therefore squares `cond(A)`. For a
|
|
372
|
+
routine collocation system (`cond(A) ≈ 2.2e11` on the `AnisoPoisson` benchmark)
|
|
373
|
+
that needs ~5e22 of dynamic range, far past float64. Measured against central
|
|
374
|
+
finite differences at the isotropic init, the resulting "gradient" was **4.3e6×
|
|
375
|
+
too large with `cos(angle) = −0.707`** — an *ascent* direction, which `AdamW`
|
|
376
|
+
duly followed (`clip_grad` rescaled the magnitude but preserved the direction).
|
|
377
|
+
`fit()` consequently returned a solution ~3.6× *worse* than the isotropic
|
|
378
|
+
`solve_linear` baseline it is meant to beat.
|
|
379
|
+
|
|
380
|
+
The inner solve is now performed under `no_grad` and only the assembled `A(L)`
|
|
381
|
+
is differentiated. This is **exact, not an approximation**: for
|
|
382
|
+
`J(L) = ‖A(L)β* − b‖²` the chain-rule contribution through `β*` is
|
|
383
|
+
`2 (Aᵀr)ᵀ dβ*/dL`, and `Aᵀr = 0` at the least-squares optimum (the residual is
|
|
384
|
+
orthogonal to `range(A)`), so the term vanishes identically and
|
|
385
|
+
`dJ/dL = 2 rᵀ (dA/dL) β*` — the envelope (Danskin) theorem. The detached
|
|
386
|
+
gradient matches finite differences to **1.2e-3 relative, `cos(angle) = 1.0000`**,
|
|
387
|
+
and skips the ill-conditioned backward entirely.
|
|
388
|
+
|
|
389
|
+
On the `AnisoPoisson` benchmark the training loss now descends monotonically
|
|
390
|
+
(`1.9e-3` → `9.8e-11`) instead of climbing (`1.9e-3` → `1.2e+05`), an
|
|
391
|
+
independent held-out collocation draw tracks it the whole way (no overfitting),
|
|
392
|
+
and the learned Σ reaches `4.4e-8` against the isotropic `solve_linear`
|
|
393
|
+
baseline's `1.9e-4` — **~4350× better**, where it was previously ~3.6× *worse*.
|
|
394
|
+
Not backpropagating through the solve also makes each step ~6× cheaper
|
|
395
|
+
(`tests/test_learnable.py`: 389s → 57s).
|
|
396
|
+
|
|
397
|
+
Note this is the *third* attempt at this bug (see 0.2.1 and 0.2.2): both earlier
|
|
398
|
+
fixes swapped the inner solve's driver (`svd` → rank-revealing `gelsd`) to stop
|
|
399
|
+
the gradient being `NaN`. That made it **finite but never correct** — the loop
|
|
400
|
+
still differentiated through the solve. Differentiating through an
|
|
401
|
+
ill-conditioned least-squares solve is not stabilisable by choice of driver;
|
|
402
|
+
the envelope identity removes the need to do it at all.
|
|
403
|
+
- **`train_bandwidth` restored the wrong "best" iterate.** `best_params` was
|
|
404
|
+
snapshotted *after* `optimizer.step()`, so it stored `θ_{t+1}` while recording
|
|
405
|
+
`loss(θ_t)` — the best-iterate restore returned the successor of the best
|
|
406
|
+
iterate. The snapshot (and the `history` entry's `sigma` / `cov_diag`, which had
|
|
407
|
+
the same off-by-one) now happens before the step, so all three describe the same
|
|
408
|
+
point.
|
|
409
|
+
|
|
5
410
|
## [0.4.2] - 2026-07-20
|
|
6
411
|
|
|
7
412
|
### Fixed
|
|
@@ -105,8 +510,10 @@ All notable changes to FastLSQ will be documented in this file.
|
|
|
105
510
|
Gauss--Hermite quadrature of the slice integral to machine precision in d = 2, 3, 4
|
|
106
511
|
(≤4e-13 observed), that autodiff of the rows wrt `c` matches finite differences, and
|
|
107
512
|
that a windowed field is recovered from its projections at several directions in one
|
|
108
|
-
LSQ.
|
|
109
|
-
|
|
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.)*
|
|
110
517
|
|
|
111
518
|
### Scope (honest)
|
|
112
519
|
|
|
@@ -145,9 +552,11 @@ All notable changes to FastLSQ will be documented in this file.
|
|
|
145
552
|
linear-least-squares design matrix; coefficients stay differentiable so learnable
|
|
146
553
|
integral-term coefficients train through the solve. All three exported from `fastlsq`.
|
|
147
554
|
- **Examples** `examples/integro_differential_demo.py` (one-shot forward solve of
|
|
148
|
-
`u'(x) + ∫_0^x u ds = f`, rel-L2 ~3e-11)
|
|
149
|
-
`examples/inverse/inverse_memory_kernel.py`
|
|
150
|
-
noisy data via AdamW through the differentiable solve
|
|
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`
|
|
151
560
|
asserts ∫-then-∂ round-trips to identity, Volterra/definite match quadrature, the DC
|
|
152
561
|
guard stays finite, and gradients flow to learnable coefficients.
|
|
153
562
|
|