FastLSQ 0.4.2__tar.gz → 0.6.0__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 (128) hide show
  1. {fastlsq-0.4.2 → fastlsq-0.6.0}/CHANGELOG.md +256 -0
  2. {fastlsq-0.4.2 → fastlsq-0.6.0}/FastLSQ.egg-info/PKG-INFO +132 -1
  3. {fastlsq-0.4.2 → fastlsq-0.6.0}/FastLSQ.egg-info/SOURCES.txt +11 -0
  4. {fastlsq-0.4.2 → fastlsq-0.6.0}/PKG-INFO +132 -1
  5. {fastlsq-0.4.2 → fastlsq-0.6.0}/README.md +131 -0
  6. fastlsq-0.6.0/examples/integral_equations.py +93 -0
  7. fastlsq-0.6.0/examples/sdf_domains.py +126 -0
  8. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/__init__.py +54 -1
  9. fastlsq-0.6.0/fastlsq/augment.py +463 -0
  10. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/basis.py +465 -6
  11. fastlsq-0.6.0/fastlsq/geometry.py +1029 -0
  12. fastlsq-0.6.0/fastlsq/kernels.py +283 -0
  13. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/learnable.py +43 -17
  14. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/problems/__init__.py +7 -0
  15. fastlsq-0.6.0/fastlsq/problems/integral.py +284 -0
  16. {fastlsq-0.4.2 → fastlsq-0.6.0}/pyproject.toml +1 -1
  17. fastlsq-0.6.0/tests/test_augment.py +299 -0
  18. fastlsq-0.6.0/tests/test_geometry_sdf.py +314 -0
  19. fastlsq-0.6.0/tests/test_kernels.py +208 -0
  20. fastlsq-0.6.0/tests/test_multi_integral.py +146 -0
  21. fastlsq-0.6.0/tests/test_problems_integral.py +184 -0
  22. fastlsq-0.6.0/tests/test_symbol.py +273 -0
  23. {fastlsq-0.4.2 → fastlsq-0.6.0}/tests/test_vector_basis.py +1 -1
  24. fastlsq-0.4.2/fastlsq/geometry.py +0 -243
  25. {fastlsq-0.4.2 → fastlsq-0.6.0}/FastLSQ.egg-info/dependency_links.txt +0 -0
  26. {fastlsq-0.4.2 → fastlsq-0.6.0}/FastLSQ.egg-info/requires.txt +0 -0
  27. {fastlsq-0.4.2 → fastlsq-0.6.0}/FastLSQ.egg-info/top_level.txt +0 -0
  28. {fastlsq-0.4.2 → fastlsq-0.6.0}/LICENSE +0 -0
  29. {fastlsq-0.4.2 → fastlsq-0.6.0}/MANIFEST.in +0 -0
  30. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/add_your_own_pde.py +0 -0
  31. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/benchmark_comparison.py +0 -0
  32. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/custom_features.py +0 -0
  33. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/fred_sde.py +0 -0
  34. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/fred_sde_fastlsq.py +0 -0
  35. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/gaia_potential.py +0 -0
  36. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/gaia_potential_fastlsq.py +0 -0
  37. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/horizons_ephemeris.py +0 -0
  38. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/numerai_alpha.py +0 -0
  39. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/numerai_alpha_fastlsq.py +0 -0
  40. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/run_all_fastlsq.py +0 -0
  41. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/__init__.py +0 -0
  42. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/_alsu_lattice.py +0 -0
  43. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/_common.py +0 -0
  44. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/run_all.py +0 -0
  45. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s01_beamloss_ode.py +0 -0
  46. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s01_betatron_tune.py +0 -0
  47. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s01_green_fff.py +0 -0
  48. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s01_hill_ivp.py +0 -0
  49. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s01_observe_fit_act_simulator.py +0 -0
  50. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s01_orbit_inverse.py +0 -0
  51. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s01_passive_loco.py +0 -0
  52. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s01_perturbed_hill.py +0 -0
  53. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s01_sofb_observe_fit_act.py +0 -0
  54. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s01_streaming_archive_growth.py +0 -0
  55. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s01_synchrotron_ode.py +0 -0
  56. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s01_tides_3months.py +0 -0
  57. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s01_topoff_impulse.py +0 -0
  58. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s01_visualize.py +0 -0
  59. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s02_plasma_wakefield.py +0 -0
  60. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s03_synchrobetatron.py +0 -0
  61. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s04_sunspots.py +0 -0
  62. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s05_helioseismology.py +0 -0
  63. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s06_tides.py +0 -0
  64. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s07_iers_earth_rotation.py +0 -0
  65. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s08_mauna_loa_co2.py +0 -0
  66. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s09_enso_qbo.py +0 -0
  67. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s10_pulsar_timing.py +0 -0
  68. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s11_modal_analysis.py +0 -0
  69. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s12_mems_resonator.py +0 -0
  70. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s13_variable_stars_kepler.py +0 -0
  71. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s14_eeg.py +0 -0
  72. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/scenarios/s15_circadian.py +0 -0
  73. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/extras/spectral_expansion.py +0 -0
  74. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/grad_shafranov.py +0 -0
  75. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/grid_inverse.py +0 -0
  76. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/grid_rl_control.py +0 -0
  77. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/grid_swing.py +0 -0
  78. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/gs_inverse.py +0 -0
  79. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/gs_rl_control.py +0 -0
  80. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/integro_differential_demo.py +0 -0
  81. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/inverse_heat_source.py +0 -0
  82. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/inverse_magnetostatics.py +0 -0
  83. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/inverse_source_position.py +0 -0
  84. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/learnable_helmholtz.py +0 -0
  85. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/memory_diffusion.py +0 -0
  86. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/orbit_hill.py +0 -0
  87. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/orbit_inverse.py +0 -0
  88. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/orbit_rl.py +0 -0
  89. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/pde_discovery.py +0 -0
  90. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/rlc_integro_differential.py +0 -0
  91. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/run_all_extensions.py +0 -0
  92. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/run_linear.py +0 -0
  93. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/run_nonlinear.py +0 -0
  94. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/tutorial_basic.py +0 -0
  95. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/tutorial_nonlinear.py +0 -0
  96. {fastlsq-0.4.2 → fastlsq-0.6.0}/examples/vector_basis_stream_vorticity.py +0 -0
  97. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/api.py +0 -0
  98. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/benchmark.py +0 -0
  99. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/block.py +0 -0
  100. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/device.py +0 -0
  101. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/diagnostics.py +0 -0
  102. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/export.py +0 -0
  103. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/lightning.py +0 -0
  104. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/linalg.py +0 -0
  105. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/newton.py +0 -0
  106. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/plotting.py +0 -0
  107. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/problems/linear.py +0 -0
  108. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/problems/nonlinear.py +0 -0
  109. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/problems/regression.py +0 -0
  110. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/solvers.py +0 -0
  111. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/tuning.py +0 -0
  112. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/utils.py +0 -0
  113. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/vector.py +0 -0
  114. {fastlsq-0.4.2 → fastlsq-0.6.0}/fastlsq/viz.py +0 -0
  115. {fastlsq-0.4.2 → fastlsq-0.6.0}/requirements.txt +0 -0
  116. {fastlsq-0.4.2 → fastlsq-0.6.0}/setup.cfg +0 -0
  117. {fastlsq-0.4.2 → fastlsq-0.6.0}/tests/test_basic.py +0 -0
  118. {fastlsq-0.4.2 → fastlsq-0.6.0}/tests/test_benchmarks_inverse.py +0 -0
  119. {fastlsq-0.4.2 → fastlsq-0.6.0}/tests/test_block.py +0 -0
  120. {fastlsq-0.4.2 → fastlsq-0.6.0}/tests/test_derivatives.py +0 -0
  121. {fastlsq-0.4.2 → fastlsq-0.6.0}/tests/test_device.py +0 -0
  122. {fastlsq-0.4.2 → fastlsq-0.6.0}/tests/test_diagnostics_timing.py +0 -0
  123. {fastlsq-0.4.2 → fastlsq-0.6.0}/tests/test_grad_shafranov.py +0 -0
  124. {fastlsq-0.4.2 → fastlsq-0.6.0}/tests/test_grid_swing.py +0 -0
  125. {fastlsq-0.4.2 → fastlsq-0.6.0}/tests/test_integral.py +0 -0
  126. {fastlsq-0.4.2 → fastlsq-0.6.0}/tests/test_learnable.py +0 -0
  127. {fastlsq-0.4.2 → fastlsq-0.6.0}/tests/test_orbit_hill.py +0 -0
  128. {fastlsq-0.4.2 → fastlsq-0.6.0}/tests/test_projection.py +0 -0
@@ -2,6 +2,262 @@
2
2
 
3
3
  All notable changes to FastLSQ will be documented in this file.
4
4
 
5
+ ## [0.6.0] - 2026-07-21
6
+
7
+ Completes the operator taxonomy: multi-axis integrals, separable kernels, and
8
+ Fredholm equations, with the integral-equation problems promoted into the same
9
+ `solve_linear` harness as the PDEs so they report in one table.
10
+
11
+ ### New features
12
+
13
+ | Feature | API | What it buys |
14
+ |---|---|---|
15
+ | 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
+ | 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 |
17
+ | Fredholm, second kind | `fredholm_second_kind` | `u − λ∫K u = f` as `I − λK`, solved in the same single least squares as everything else |
18
+ | 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 |
19
+ | Integral Problem classes | `fastlsq.problems.integral` | Four integral / integro-differential problems with **closed-form** solutions, running through `solve_linear` |
20
+
21
+ ### Added
22
+
23
+ - **`MultiIntegralOperator` / `SinusoidalBasis.multi_integral`.** The plane wave
24
+ factorises over axes, `e^{iW·x} = ∏_k e^{iW_k x_k}`, so a multi-axis integral
25
+ is a *product* of the same numerically stable one-axis factors already used by
26
+ `definite_integral`, and the phase collapses to the value at the per-axis
27
+ midpoint:
28
+
29
+ ∫∫ φ_j ∏_{k∈S} dx_k = [∏_{k∈S} Δ_k · sinc(W_k Δ_k / 2π)] · sin(Z_mid)
30
+
31
+ Exact, quadrature-free, and finite for near-DC features on every axis. With
32
+ `|S| = 1` it reproduces `IntegralOperator.definite` bit-for-bit (verified to
33
+ 1e-14); against tensor-product quadrature in 2-D and 3-D it agrees to the
34
+ quadrature's own error. Order is 1 per axis — a repeated integral along one
35
+ axis remains `IntegralOperator`, whose Cauchy/Taylor branches handle the
36
+ small-`W` cancellation a naive repeated product would suffer.
37
+
38
+ - **`SeparableKernelOperator` (`fastlsq/kernels.py`).** A degenerate kernel
39
+ collapses the integral operator to `Σ_m g_m(x) ∫ h_m u`, so acting on the
40
+ basis needs only the `R × N` matrix `C_{mj} = ∫ h_m φ_j`. Assembly is
41
+ `G(x) @ C` — a rank-`R` factorisation instead of an `M × M` kernel evaluation,
42
+ and the assembled block's rank is exactly `R` (verified).
43
+
44
+ Quadrature enters in exactly one place, computing `C`, and it is a
45
+ precomputation rather than a per-row cost: tensor-product Gauss-Legendre,
46
+ spectrally convergent for smooth integrands. Because it must still resolve the
47
+ feature oscillation, `check_quadrature` reports the achieved convergence
48
+ (2e-14 for a typical basis; the test suite also pins that it *flags* an
49
+ under-resolved one), and `from_inner_products` accepts analytic `C` to skip
50
+ quadrature entirely.
51
+
52
+ - **`fredholm_second_kind` and `degenerate_eigenvalues`.** Fredholm equations of
53
+ the second kind assemble as `I − λK`. For a rank-`R` degenerate kernel there
54
+ are at most `R` characteristic values, at which the equation is singular;
55
+ `degenerate_eigenvalues` computes them so a near-singular `λ` is detectable
56
+ instead of silently producing a garbage fit. For `K(x,y)=xy` on `[0,1]` it
57
+ recovers the analytic `λ = 3` as `3.0000000000000093`.
58
+
59
+ Validated against degenerate-kernel theory rather than a reference quadrature:
60
+ the exact solution of `u − λ∫₀¹xy·u = f` is `u = f + λcx` with
61
+ `c = ∫yf/(1−λ/3)`, matched to ~1e-8 for `λ` swept from 0.25 to 2.9 (i.e. right
62
+ up to the singular value). `λ` may be an `nn.Parameter`.
63
+
64
+ - **`fastlsq/problems/integral.py`.** `FredholmProductKernel`,
65
+ `FredholmRank2Kernel`, `VolterraSecondKind` and `IntegroDifferentialODE`, each
66
+ with a closed-form solution, running through `solve_linear` exactly as the PDE
67
+ problems do — so integral equations now produce a results table alongside the
68
+ PDEs rather than living only in example scripts. The module docstring writes
69
+ down the (duck-typed, no base class) Problem contract, which was previously
70
+ implicit and inconsistent.
71
+
72
+ Note the second-kind problems return an **empty** `bcs` list: the identity term
73
+ makes them well posed with no boundary rows at all. Only the
74
+ integro-differential ODE, which has a genuine constant of integration, needs
75
+ one.
76
+
77
+ - **Accuracy regressions (`tests/test_problems_integral.py`).** Each problem is
78
+ solved through the real harness and checked against its analytic solution, and
79
+ separately each closed form is verified to satisfy its own equation by
80
+ independent quadrature — so a closed form and an operator cannot be wrong in
81
+ the same direction without being caught.
82
+
83
+ ### Measured
84
+
85
+ All five integral-equation problems, through `solve_linear` (300 features, 2000
86
+ collocation points), against their closed-form solutions:
87
+
88
+ | Problem | rel L2 | grad rel L2 | boundary rows |
89
+ |---|---|---|---|
90
+ | Fredholm `K=xy`, λ=0.5 | 5.5e-14 | 5.7e-12 | 0 |
91
+ | Fredholm `K=xy`, λ=2.0 | 3.8e-13 | 3.9e-11 | 0 |
92
+ | Fredholm rank-2, λ=0.4 | 9.1e-14 | 9.5e-12 | 0 |
93
+ | Volterra 2nd kind, λ=1.5 | 8.8e-13 | 1.0e-10 | 0 |
94
+ | Integro-differential ODE, λ=4.0 | 6.2e-16 | 1.7e-14 | 1 |
95
+
96
+ Approaching the kernel's singular value `λ = 3` costs only ~2 orders of
97
+ magnitude: 5.5e-14 at `λ = 0.5`, 6.8e-13 at `λ = 2.9`, 3.9e-12 at `λ = 2.99`.
98
+
99
+ ### Documentation
100
+
101
+ - The README now tables the SDF primitives (`sdf_ball`, `sdf_disk`, `sdf_box`,
102
+ `sdf_annulus`, `sdf_lshape`, `sdf_flower`, `sdf_polygon`, `sdf_tokamak`) and
103
+ the CSG combinators (`sdf_union`, `sdf_intersection`, `sdf_difference`,
104
+ `sdf_complement`) by name. They were exported in `__all__` from 0.5.0 but only
105
+ described in prose, so the composable `ψ`-level API was effectively
106
+ undiscoverable — you could find `SDFDomain.disk()` but not `sdf_disk`, and the
107
+ CSG functions not at all.
108
+ - `PolynomialColumns` is likewise named explicitly alongside `AugmentedBasis`.
109
+
110
+ ### Changed
111
+
112
+ - **Operator composition is now duck-typed.** `IntegroDifferentialOperator._as_terms`
113
+ and `DiffOperator.__add__` dispatch on the presence of `.apply(basis, x, cache)`
114
+ rather than a fixed `isinstance` tuple, so operators defined in other modules
115
+ (`SeparableKernelOperator`, and any user-defined operator) compose with the
116
+ built-ins without `basis.py` importing them. No change to existing behaviour.
117
+
118
+ ## [0.5.0] - 2026-07-21
119
+
120
+ Closes three gaps between what the paper describes and what the package shipped:
121
+ SDF geometry (§2.7), Fourier-symbol operators (§3.4), and explicit polynomial
122
+ augmentation columns (§2.3).
123
+
124
+ ### Added
125
+
126
+ - **SDF / membership-oracle geometry (`fastlsq.geometry`).** A domain can now be
127
+ given as any callable `ψ(x)` that is negative inside — no mesh, no analytic
128
+ sampler. `sample_sdf` rejection-samples the interior (unbiased, so the radial
129
+ CDF of a disk is exactly `r²`), `project_to_boundary` lands points on `ψ = 0`,
130
+ and `outward_normal` returns `∇ψ/‖∇ψ‖` via autograd, which is what a Neumann or
131
+ Robin condition needs. `SDFDomain` bundles these with a bounding box and CSG
132
+ composition (`|`, `&`, `-`), plus `neumann_rows` / `robin_rows` that contract
133
+ the analytic basis gradient against the normal into an `(M, N)` block.
134
+
135
+ Shipped domains: `disk`, `ball`, `box`, `annulus` (multiply-connected),
136
+ `lshape` (reentrant corner), `flower` (smooth non-convex), `polygon`, and
137
+ `tokamak` — the D-shaped Miller poloidal cross-section, at the same MAST-U
138
+ scale (`R ∈ [0.6, 1.4]`) as `examples/grad_shafranov.py`, which until now used
139
+ a plain rectangle.
140
+
141
+ Projection is **damped** Newton, `x ← x − ψ∇ψ/‖∇ψ‖²`, with per-point
142
+ backtracking and monotone acceptance. The `1/‖∇ψ‖²` normalisation matters:
143
+ the textbook `x − ψ∇ψ` is valid only for a true distance function, and
144
+ `sdf_flower` has `‖∇ψ‖` spanning 1 to ~10. Undamped, it left
145
+ `|ψ| ≈ 1.5e-1` on the boundary; damped and monotone, 99.9% of interior seeds
146
+ converge to `1e-10`. The residual failures are seeds within `r < 0.2` of the
147
+ centre, where a non-distance `ψ` has no unique nearest boundary point;
148
+ `sample_boundary_sdf` filters them rather than hiding them.
149
+
150
+ - **`SymbolOperator` — Fourier-multiplier (nonlocal) operators.** Every feature
151
+ is a plane wave, so a multiplier `L e^{iξ·x} = m(ξ) e^{iξ·x}` acts *diagonally*
152
+ on the basis: assembling `L` is a per-column rescale, exact to machine
153
+ precision, with no quadrature and no discretisation of the kernel. Operators
154
+ that are hard for mesh methods — the fractional Laplacian is nonlocal with a
155
+ singular kernel, giving a dense ill-conditioned FEM/FD matrix — cost exactly
156
+ what the Laplacian costs here.
157
+
158
+ Factories: `fractional_laplacian(s)`, `riesz_potential(s)`,
159
+ `riesz_transform(k)`, `convolution(k̂)`. The order `s` may be an
160
+ `nn.Parameter`: the symbol is `exp(s·log‖ξ‖²)`, so gradient descent recovers a
161
+ planted order (test recovers `s = 0.65` to `<1e-3`). Composes with `Op` and
162
+ `IntegralOperator` through the usual arithmetic.
163
+
164
+ Verified against independent references, not just self-consistency: `s=1`
165
+ reproduces `−Δ` and `s=2` reproduces `Δ²` bit-exactly; the symbol is checked
166
+ against the **singular-integral definition** including its normalising constant
167
+ `C(1,s)`; and Gaussian convolution matches direct quadrature to `<1e-8`.
168
+
169
+ `ConvolutionOperator` is `SymbolOperator.convolution(k̂)` — this is what
170
+ `examples/memory_diffusion.py:80` was doing by hand with a `(1, N)` broadcast
171
+ coefficient. That hand-rolled version uses `basis.W[0:1, :]`, correct only
172
+ because `d=2` has exactly one spatial axis; a symbol sees all of `W` and
173
+ generalises to any dimension.
174
+
175
+ - **Polynomial / DC augmentation columns (`fastlsq.augment`).** `AugmentedBasis`
176
+ widens a basis with explicit `1, x, x², …` columns carrying **exact** operator
177
+ images — analytic monomial derivatives, antiderivatives (a negative multi-index
178
+ integrates: `x^p → x^{p+1}/(p+1)`), and Cauchy iterated integrals — not a
179
+ zero-derivative stub. It implements the same duck-typed protocol as
180
+ `SinusoidalBasis`, so `Op`, `IntegralOperator` and `SymbolOperator` all work
181
+ unchanged and return `(M, N + n_cols)`.
182
+
183
+ This is what §2.3 means by an integration constant "pinned by an explicit
184
+ polynomial column"; previously the only such column in the repo was hand-rolled
185
+ in `examples/extras/scenarios/s01_beamloss_ode.py`, which had to maintain its
186
+ derivative block by hand.
187
+
188
+ Scope, honestly: a sinusoidal bank with random phases can already approximate a
189
+ constant from near-DC features, so this is an improvement, not a rescue — on
190
+ `u'' = f` with a DC offset it is neutral at `C = 0` and ~12× better at
191
+ `C = 1000`. A non-constant monomial has no function-valued Fourier-multiplier
192
+ image, so `SymbolOperator` on `degree > 0` columns raises rather than inventing
193
+ a value.
194
+
195
+ ### Fixed
196
+
197
+ - **`ProjectionOperator`'s docstring claimed the Fourier-symbol operators were
198
+ "already in the package".** They were not, until this release. It now points at
199
+ `SymbolOperator` and states the actual distinction (a symbol acts diagonally;
200
+ a projection row mixes features).
201
+
202
+ ### Notes
203
+
204
+ - **Which fractional Laplacian.** The symbol calculus applies the multiplier to
205
+ the global plane-wave extension of the trial function, so
206
+ `fractional_laplacian` is the **whole-space (restricted)** `(−Δ)^s` on `R^d`,
207
+ *not* the spectral or regional variant defined by an eigenbasis of a bounded
208
+ domain. These coincide on `R^d` and differ on a bounded domain. Results should
209
+ not be read as the spectral fractional Laplacian.
210
+
211
+ - Boundary points from `sample_boundary_sdf` are **not** uniform with respect to
212
+ surface measure — they are the pushforward of the uniform box measure under the
213
+ projection, which over-weights convex bulges. Fine for collocation; do not use
214
+ them as quadrature nodes for a surface integral without reweighting.
215
+
216
+ ## [0.4.3] - 2026-07-20
217
+
218
+ ### Fixed
219
+
220
+ - **The Σ-learner trained uphill: `train_bandwidth` now uses the exact envelope
221
+ gradient.** The outer loss was backpropagated *through* `torch.linalg.lstsq`,
222
+ whose backward carries a `(AᵀA)⁻¹` and therefore squares `cond(A)`. For a
223
+ routine collocation system (`cond(A) ≈ 2.2e11` on the `AnisoPoisson` benchmark)
224
+ that needs ~5e22 of dynamic range, far past float64. Measured against central
225
+ finite differences at the isotropic init, the resulting "gradient" was **4.3e6×
226
+ too large with `cos(angle) = −0.707`** — an *ascent* direction, which `AdamW`
227
+ duly followed (`clip_grad` rescaled the magnitude but preserved the direction).
228
+ `fit()` consequently returned a solution ~3.6× *worse* than the isotropic
229
+ `solve_linear` baseline it is meant to beat.
230
+
231
+ The inner solve is now performed under `no_grad` and only the assembled `A(L)`
232
+ is differentiated. This is **exact, not an approximation**: for
233
+ `J(L) = ‖A(L)β* − b‖²` the chain-rule contribution through `β*` is
234
+ `2 (Aᵀr)ᵀ dβ*/dL`, and `Aᵀr = 0` at the least-squares optimum (the residual is
235
+ orthogonal to `range(A)`), so the term vanishes identically and
236
+ `dJ/dL = 2 rᵀ (dA/dL) β*` — the envelope (Danskin) theorem. The detached
237
+ gradient matches finite differences to **1.2e-3 relative, `cos(angle) = 1.0000`**,
238
+ and skips the ill-conditioned backward entirely.
239
+
240
+ On the `AnisoPoisson` benchmark the training loss now descends monotonically
241
+ (`1.9e-3` → `9.8e-11`) instead of climbing (`1.9e-3` → `1.2e+05`), an
242
+ independent held-out collocation draw tracks it the whole way (no overfitting),
243
+ and the learned Σ reaches `4.4e-8` against the isotropic `solve_linear`
244
+ baseline's `1.9e-4` — **~4350× better**, where it was previously ~3.6× *worse*.
245
+ Not backpropagating through the solve also makes each step ~6× cheaper
246
+ (`tests/test_learnable.py`: 389s → 57s).
247
+
248
+ Note this is the *third* attempt at this bug (see 0.2.1 and 0.2.2): both earlier
249
+ fixes swapped the inner solve's driver (`svd` → rank-revealing `gelsd`) to stop
250
+ the gradient being `NaN`. That made it **finite but never correct** — the loop
251
+ still differentiated through the solve. Differentiating through an
252
+ ill-conditioned least-squares solve is not stabilisable by choice of driver;
253
+ the envelope identity removes the need to do it at all.
254
+ - **`train_bandwidth` restored the wrong "best" iterate.** `best_params` was
255
+ snapshotted *after* `optimizer.step()`, so it stored `θ_{t+1}` while recording
256
+ `loss(θ_t)` — the best-iterate restore returned the successor of the best
257
+ iterate. The snapshot (and the `history` entry's `sigma` / `cov_diag`, which had
258
+ the same off-by-one) now happens before the step, so all three describe the same
259
+ point.
260
+
5
261
  ## [0.4.2] - 2026-07-20
6
262
 
7
263
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: FastLSQ
3
- Version: 0.4.2
3
+ Version: 0.6.0
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
@@ -159,6 +159,128 @@ A_pde = helmholtz.apply(basis, x) # (5000, 1500)
159
159
  wave = Op.partial(dim=2, order=2, d=3) - c**2 * Op.laplacian(d=3, dims=[0, 1])
160
160
  ```
161
161
 
162
+ ### Nonlocal operators (fractional Laplacian, convolution)
163
+
164
+ Every feature is a plane wave, so a Fourier multiplier `m(ξ)` acts **diagonally**
165
+ on the basis -- assembling it is a per-column rescale, exact, with no quadrature
166
+ and no discretisation of the (singular, nonlocal) kernel:
167
+
168
+ ```python
169
+ from fastlsq.basis import SinusoidalBasis, SymbolOperator, Op
170
+
171
+ basis = SinusoidalBasis.random(input_dim=2, n_features=1500, sigma=5.0)
172
+
173
+ frac = SymbolOperator.fractional_laplacian(s=0.75) # (−Δ)^0.75
174
+ A = frac.apply(basis, x) # (M, 1500), one rescale
175
+
176
+ # Mixes freely with differential terms
177
+ L = SymbolOperator.fractional_laplacian(0.5) + 3.0 * Op.identity(d=2)
178
+
179
+ # Convolution from the kernel's transform; s may be an nn.Parameter, so the
180
+ # fractional order itself can be recovered by gradient descent.
181
+ K = SymbolOperator.convolution(lambda W: torch.exp(-(W**2).sum(0, keepdim=True) / 12))
182
+ ```
183
+
184
+ `s=1` reproduces `−Δ` bit-exactly. Note this is the **whole-space (restricted)**
185
+ `(−Δ)^s`, not the spectral variant defined on a bounded domain -- the two differ
186
+ once the domain is bounded.
187
+
188
+ ### Integral equations (Fredholm, Volterra, separable kernels)
189
+
190
+ A separable kernel `K(x,y) = Σ g_m(x) h_m(y)` collapses the integral operator to
191
+ `Σ_m g_m(x) ∫ h_m u`, so acting on the basis needs only an `R × N` matrix of
192
+ inner products, computed once. A Fredholm equation of the second kind is then
193
+ one linear least squares like everything else:
194
+
195
+ ```python
196
+ from fastlsq import SeparableKernelOperator, fredholm_second_kind, degenerate_eigenvalues
197
+
198
+ # u(x) − λ ∫₀¹ x y u(y) dy = f(x)
199
+ K = SeparableKernelOperator([lambda x: x[:, 0]], # g_m
200
+ [lambda y: y[:, 0]], # h_m
201
+ lower=0.0, upper=1.0, d=1)
202
+
203
+ print(degenerate_eigenvalues(K, basis)) # λ where the equation is singular → 3.0
204
+ print(K.check_quadrature(basis)) # are the inner products resolved?
205
+
206
+ L = fredholm_second_kind(K, lam=0.5, d=1)
207
+ beta = solve_lstsq(L.apply(basis, x), f(x))
208
+ ```
209
+
210
+ Second-kind equations need **no boundary rows** — the identity term makes them
211
+ well posed on its own. Integration over several axes at once (definite, running,
212
+ or mixed) is `MultiIntegralOperator`:
213
+
214
+ ```python
215
+ from fastlsq import MultiIntegralOperator
216
+
217
+ area = MultiIntegralOperator.definite([0, 1], [0, 0], [1, 1], d=2) # ∫∫ over a box
218
+ memory = MultiIntegralOperator([0, 1], [0.0, 0.0], d=2, uppers=[1.0, None]) # definite × running
219
+ ```
220
+
221
+ Ready-made problems with closed-form solutions live in `fastlsq.problems` and run
222
+ through `solve_linear` like the PDEs (`PYTHONPATH=. python3
223
+ examples/integral_equations.py`, 300 features, 2000 collocation points):
224
+
225
+ | Problem | rel L2 | grad rel L2 | boundary rows |
226
+ |---|---|---|---|
227
+ | `FredholmProductKernel(lam=0.5)` | 5.5e-14 | 5.7e-12 | 0 |
228
+ | `FredholmProductKernel(lam=2.0)` | 3.8e-13 | 3.9e-11 | 0 |
229
+ | `FredholmRank2Kernel(lam=0.4)` | 9.1e-14 | 9.5e-12 | 0 |
230
+ | `VolterraSecondKind(lam=1.5)` | 8.8e-13 | 1.0e-10 | 0 |
231
+ | `IntegroDifferentialODE(lam=4.0)` | 6.2e-16 | 1.7e-14 | 1 |
232
+
233
+ Errors are against the **closed-form** solutions (degenerate-kernel theory for
234
+ the Fredholm cases, the equivalent ODE for the Volterra ones), not a reference
235
+ quadrature. Accuracy degrades gracefully toward the kernel's singular value --
236
+ for `K = xy`, whose only characteristic value is `λ = 3`, the error moves from
237
+ 5.5e-14 at `λ = 0.5` to 3.9e-12 at `λ = 2.99`.
238
+
239
+ ### Complex geometry without a mesh
240
+
241
+ A domain is any callable that is negative inside. Interior points come from
242
+ rejection sampling, boundary points from projection onto `ψ = 0`, and outward
243
+ normals from `∇ψ/‖∇ψ‖` -- which is exactly what Neumann and Robin conditions need:
244
+
245
+ ```python
246
+ from fastlsq.geometry import SDFDomain
247
+
248
+ dom = SDFDomain.annulus(0.3, 1.0) # or .disk() .lshape() .flower() .tokamak()
249
+ x = dom.sample(4000) # interior collocation
250
+ xb = dom.sample_boundary(600) # boundary collocation
251
+ B = dom.neumann_rows(basis, xb) # (M, N) block for ∂u/∂n = g
252
+
253
+ # Non-convex and multiply-connected domains are built, not meshed
254
+ plate = SDFDomain.disk(1.0) - SDFDomain.disk(0.2, center=(0.4, 0.0))
255
+ ```
256
+
257
+ Built-in domains, as `SDFDomain` constructors or as bare `ψ` callables:
258
+
259
+ | Domain | `SDFDomain` | Bare `ψ` | Why it's there |
260
+ |---|---|---|---|
261
+ | Ball / disk | `.ball()`, `.disk()` | `sdf_ball`, `sdf_disk` | Exact SDF, any dimension; the §2.7 unit disk |
262
+ | Axis-aligned box | `.box(lo, hi)` | `sdf_box` | Exact inside and out; the CSG building block |
263
+ | Annulus / shell | `.annulus(r_in, r_out)` | `sdf_annulus` | **Multiply-connected** — an interior boundary whose outward normal points toward the centre |
264
+ | L-shape | `.lshape(size, cut)` | `sdf_lshape` | **Reentrant corner**, the standard non-convex stress case (`r^{2/3}` solution singularity) |
265
+ | Flower | `.flower(R, a, k)` | `sdf_flower` | Smooth non-convex, and deliberately **not** a distance function (`‖∇ψ‖` spans 1–10) — the case that separates a correct projection from a naive one |
266
+ | Polygon | — | `sdf_polygon(verts)` | Exact for any simple polygon; the escape hatch for a cross-section known only as a curve (measured, CAD, traced) |
267
+ | Tokamak | `.tokamak()` | `sdf_tokamak` | D-shaped Miller poloidal cross-section, via `sdf_polygon` |
268
+
269
+ Any `ψ` of your own works too — it only has to be negative inside. Combine them
270
+ with the CSG helpers, which are also available as plain functions:
271
+
272
+ | Set operation | Operator | Function |
273
+ |---|---|---|
274
+ | Union `A ∪ B` | `A \| B` | `sdf_union(a, b)` |
275
+ | Intersection `A ∩ B` | `A & B` | `sdf_intersection(a, b)` |
276
+ | Difference `A \ B` | `A - B` | `sdf_difference(a, b)` |
277
+ | Complement | — | `sdf_complement(a)` |
278
+
279
+ CSG results are valid implicit functions (correct sign everywhere) but not
280
+ generally exact distance functions — `min`/`max` of two exact SDFs over- or
281
+ under-estimates distance near the seam. Nothing here depends on exactness:
282
+ sampling uses only the sign, and `project_to_boundary` normalises by `‖∇ψ‖²`.
283
+
162
284
  ### Vector-valued solutions
163
285
 
164
286
  `solve_linear` / `solve_nonlinear` support vector-valued **u**: ℝᵈ → ℝᵏ for
@@ -279,7 +401,12 @@ derivative engine:
279
401
  | `BasisCache` | Pre-computes sin(Z)/cos(Z) once, reuses across multiple derivative evaluations |
280
402
  | `DiffOperator` / `Op` | Symbolic linear differential operators that compose via +, -, scalar *; coefficients can be `nn.Parameter` for learnable PDEs |
281
403
  | `IntegralOperator` / `IntegroDifferentialOperator` | Closed-form **single-axis** definite / running (Volterra) integrals, including `order=n` **iterated** integrals `∫_lo^x (x−t)^{n−1}/(n−1)! φ dt`; compose with `Op` into one integro-differential design matrix |
404
+ | `MultiIntegralOperator` | Closed-form integration over **several axes at once**, each independently definite or Volterra -- area/volume functionals and mixed "definite in space, running in time" memory terms. The plane wave factorises over axes, so it is a product of the same stable one-axis factors |
405
+ | `SeparableKernelOperator` | Separable (degenerate) kernels `K(x,y) = Σ g_m(x) h_m(y)`, assembled as a rank-`R` product `G @ C` with the inner products `C` precomputed once. With `fredholm_second_kind` this makes `u − λ∫K u = f` one linear least squares |
406
+ | `SymbolOperator` | **Fourier-multiplier (nonlocal)** operators `L e^{iξ·x} = m(ξ) e^{iξ·x}`. Features *are* plane waves, so the symbol acts diagonally -- a per-column rescale, exact, no quadrature. Ships `fractional_laplacian(s)` (with learnable `s`), `riesz_potential`, `riesz_transform`, `convolution(k̂)` |
282
407
  | `GaussianWindowedBasis` / `ProjectionOperator` | Windowed-Fourier (Gabor) basis + closed-form **projection (Radon)** operator `∫ f δ(c·z−u) dz` for tomographic / line-integral inverse problems; quadrature-free and differentiable in the optics `c` |
408
+ | `AugmentedBasis` / `PolynomialColumns` | Widens a basis with explicit `1, x, x², …` columns carrying **exact** operator images, to pin integration constants and DC modes that leave the sinusoidal family. Transparent to every operator |
409
+ | `SDFDomain` + `sample_sdf` / `project_to_boundary` / `outward_normal` | **Membership-oracle geometry**: give any `ψ(x)` negative inside and get interior points, boundary points and outward normals -- no mesh. CSG composition via `\|`, `&`, `-`; built-ins include disk, annulus, L-shape, flower, polygon and a tokamak cross-section |
283
410
  | `FeatureBasis` | Adapter for non-sinusoidal solvers (e.g. PIELM with tanh) |
284
411
  | `FastLSQSolver` | Manages feature blocks; exposes `.basis` for all derivative computations |
285
412
  | `LearnableFastLSQ` | Differentiable solver with learnable bandwidth via reparameterisation trick |
@@ -370,7 +497,10 @@ See `examples/add_your_own_pde.py` for the complete tutorial.
370
497
  - **Analytical derivative engine**: `SinusoidalBasis` computes arbitrary-order derivatives exactly in O(1) -- the foundation of the entire framework
371
498
  - **Symbolic PDE operators**: Compose differential operators with `Op` (Laplacian, wave, Helmholtz, biharmonic, custom) via intuitive arithmetic; coefficients can be `nn.Parameter` for AdamW optimisation
372
499
  - **Closed-form integral operators**: `IntegralOperator` (single-axis definite / Volterra integrals) composes with `Op` into one integro-differential least-squares block. The integral class now also includes the **projection (Radon) operator** (`ProjectionOperator` on a `GaussianWindowedBasis`) -- quadrature-free `∫ f δ(c·z−u) dz` line/hyperplane integrals for tomographic inverse problems, differentiable in the optics `c` for experiment design
500
+ - **Integral equations**: Separable (degenerate) kernels `K = Σ g_m(x) h_m(y)` assemble as a rank-`R` product with inner products precomputed once, so a Fredholm equation of the second kind `u − λ∫K u = f` is a single linear least squares needing **no boundary rows**. `degenerate_eigenvalues` reports the `λ` at which the equation is singular and `check_quadrature` whether the inner products are resolved -- both otherwise-silent failure modes. `MultiIntegralOperator` integrates over several axes at once, each independently definite or Volterra
501
+ - **Nonlocal / Fourier-symbol operators**: `SymbolOperator` assembles any multiplier `m(ξ)` as a per-column rescale -- exact, quadrature-free, and the same cost as the Laplacian. Covers the **fractional Laplacian** `(−Δ)^s` (with a *learnable* order `s`), Riesz potentials and transforms, and **convolution** `k * u` from the kernel transform `k̂`. Operators whose kernels are singular and nonlocal -- dense, ill-conditioned matrices for FEM/FD -- are diagonal here
373
502
  - **Vector-valued solutions**: First-class support for **u**: ℝᵈ → ℝᵏ (elasticity, Stokes, Maxwell). Problems declare `n_outputs = k`; `block_concat` assembles coupled block systems; `solver.predict(x)` returns shape `(M, k)`. Scalar problems are the `k=1` case
503
+ - **Augmentation columns**: `AugmentedBasis` + `PolynomialColumns` widen the basis with exact `1, x, x², …` columns to pin integration constants and DC modes that leave the sinusoidal family -- transparent to every operator
374
504
  - **High-level API**: Solve PDEs in one line with `solve_linear()` and `solve_nonlinear()`
375
505
  - **Robust linear solver**: Pluggable least-squares back-ends; the default `auto` routes Cholesky -> QR -> SVD, and backward-stable QR delivers SVD-grade accuracy at QR cost on the rank-deficient random-feature system
376
506
  - **Learnable bandwidth**: `LearnableFastLSQ` optimises the bandwidth (scalar or anisotropic) via reparameterisation
@@ -380,6 +510,7 @@ See `examples/add_your_own_pde.py` for the complete tutorial.
380
510
  - **Adaptive collocation**: `n_pde` / `n_bc` default to feature-count-scaled values, overridable per solve
381
511
  - **Built-in plotting**: Solution visualization, convergence plots, spectral sensitivity
382
512
  - **Geometry samplers**: Box, ball, sphere, interval, custom samplers
513
+ - **Meshless complex geometry**: `SDFDomain` takes any membership oracle `ψ(x)` (negative inside) and supplies interior points, boundary points and outward normals `∇ψ/‖∇ψ‖` for Neumann/Robin conditions. CSG composition (`|`, `&`, `-`) builds non-convex and multiply-connected domains; built-ins include disk, annulus, L-shape, flower, arbitrary polygon, and a D-shaped tokamak poloidal cross-section
383
514
  - **Diagnostics**: Problem validation, conditioning checks, error detection
384
515
  - **Export utilities**: NumPy conversion, checkpoint saving/loading
385
516
  - **PyTorch Lightning**: Integration for training loops
@@ -18,6 +18,7 @@ examples/grid_rl_control.py
18
18
  examples/grid_swing.py
19
19
  examples/gs_inverse.py
20
20
  examples/gs_rl_control.py
21
+ examples/integral_equations.py
21
22
  examples/integro_differential_demo.py
22
23
  examples/inverse_heat_source.py
23
24
  examples/inverse_magnetostatics.py
@@ -32,6 +33,7 @@ examples/rlc_integro_differential.py
32
33
  examples/run_all_extensions.py
33
34
  examples/run_linear.py
34
35
  examples/run_nonlinear.py
36
+ examples/sdf_domains.py
35
37
  examples/tutorial_basic.py
36
38
  examples/tutorial_nonlinear.py
37
39
  examples/vector_basis_stream_vorticity.py
@@ -78,6 +80,7 @@ examples/extras/scenarios/s14_eeg.py
78
80
  examples/extras/scenarios/s15_circadian.py
79
81
  fastlsq/__init__.py
80
82
  fastlsq/api.py
83
+ fastlsq/augment.py
81
84
  fastlsq/basis.py
82
85
  fastlsq/benchmark.py
83
86
  fastlsq/block.py
@@ -85,6 +88,7 @@ fastlsq/device.py
85
88
  fastlsq/diagnostics.py
86
89
  fastlsq/export.py
87
90
  fastlsq/geometry.py
91
+ fastlsq/kernels.py
88
92
  fastlsq/learnable.py
89
93
  fastlsq/lightning.py
90
94
  fastlsq/linalg.py
@@ -96,19 +100,26 @@ fastlsq/utils.py
96
100
  fastlsq/vector.py
97
101
  fastlsq/viz.py
98
102
  fastlsq/problems/__init__.py
103
+ fastlsq/problems/integral.py
99
104
  fastlsq/problems/linear.py
100
105
  fastlsq/problems/nonlinear.py
101
106
  fastlsq/problems/regression.py
107
+ tests/test_augment.py
102
108
  tests/test_basic.py
103
109
  tests/test_benchmarks_inverse.py
104
110
  tests/test_block.py
105
111
  tests/test_derivatives.py
106
112
  tests/test_device.py
107
113
  tests/test_diagnostics_timing.py
114
+ tests/test_geometry_sdf.py
108
115
  tests/test_grad_shafranov.py
109
116
  tests/test_grid_swing.py
110
117
  tests/test_integral.py
118
+ tests/test_kernels.py
111
119
  tests/test_learnable.py
120
+ tests/test_multi_integral.py
112
121
  tests/test_orbit_hill.py
122
+ tests/test_problems_integral.py
113
123
  tests/test_projection.py
124
+ tests/test_symbol.py
114
125
  tests/test_vector_basis.py