FastLSQ 0.4.1__tar.gz → 0.4.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 (153) hide show
  1. {fastlsq-0.4.1 → fastlsq-0.4.2}/CHANGELOG.md +36 -0
  2. {fastlsq-0.4.1 → fastlsq-0.4.2}/FastLSQ.egg-info/PKG-INFO +2 -2
  3. {fastlsq-0.4.1 → fastlsq-0.4.2}/FastLSQ.egg-info/SOURCES.txt +0 -37
  4. {fastlsq-0.4.1 → fastlsq-0.4.2}/PKG-INFO +2 -2
  5. {fastlsq-0.4.1 → fastlsq-0.4.2}/README.md +1 -1
  6. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/__init__.py +1 -1
  7. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/basis.py +124 -15
  8. {fastlsq-0.4.1 → fastlsq-0.4.2}/pyproject.toml +1 -1
  9. {fastlsq-0.4.1 → fastlsq-0.4.2}/tests/test_integral.py +133 -0
  10. fastlsq-0.4.1/examples/digital_twins/darcy_heat.py +0 -583
  11. fastlsq-0.4.1/examples/digital_twins/pendulum.py +0 -242
  12. fastlsq-0.4.1/examples/digital_twins/pendulum_benchmark.py +0 -281
  13. fastlsq-0.4.1/examples/digital_twins/plasma_wakefield.py +0 -339
  14. fastlsq-0.4.1/examples/digital_twins/plasma_wakefield_2D_1.py +0 -863
  15. fastlsq-0.4.1/examples/digital_twins/plasma_wakefield_2D_2.py +0 -769
  16. fastlsq-0.4.1/examples/digital_twins/plasma_wakefield_2d_3.py +0 -818
  17. fastlsq-0.4.1/examples/digital_twins/plasma_wakefield_parameteric.py +0 -475
  18. fastlsq-0.4.1/examples/digital_twins/plot_utils.py +0 -42
  19. fastlsq-0.4.1/examples/digital_twins/structural_health_simple.py +0 -343
  20. fastlsq-0.4.1/examples/digital_twins/turbulence_gravity_cooling.py +0 -387
  21. fastlsq-0.4.1/examples/inverse/aero_.py +0 -362
  22. fastlsq-0.4.1/examples/inverse/denoising_parameter_estimation.py +0 -297
  23. fastlsq-0.4.1/examples/inverse/elastic_wave_animation.py +0 -300
  24. fastlsq-0.4.1/examples/inverse/heat_from_video.py +0 -394
  25. fastlsq-0.4.1/examples/inverse/inverse_memory_kernel.py +0 -151
  26. fastlsq-0.4.1/examples/inverse/inverse_source_integral_sensors.py +0 -130
  27. fastlsq-0.4.1/examples/inverse/inverse_turbulence.py +0 -811
  28. fastlsq-0.4.1/examples/inverse/rlc_system_id.py +0 -159
  29. fastlsq-0.4.1/examples/inverse/shape_ns.py +0 -1426
  30. fastlsq-0.4.1/examples/inverse/subsurface_imaging.py +0 -547
  31. fastlsq-0.4.1/examples/inverse/tomography_projection.py +0 -145
  32. fastlsq-0.4.1/examples/inverse/wing_optimize_simple.py +0 -283
  33. fastlsq-0.4.1/examples/sindy/compare_sindy_methods.py +0 -760
  34. fastlsq-0.4.1/examples/sindy/sindy_benchmarks.py +0 -351
  35. fastlsq-0.4.1/examples/sindy/sindy_differentiable.py +0 -418
  36. fastlsq-0.4.1/examples/sindy/sindy_minimal_diff.py +0 -716
  37. fastlsq-0.4.1/misc/fastlsq_teaser.png +0 -0
  38. fastlsq-0.4.1/misc/ideal_quadrupole.png +0 -0
  39. fastlsq-0.4.1/misc/inverse_heat_source.gif +0 -0
  40. fastlsq-0.4.1/misc/inverse_heat_source.png +0 -0
  41. fastlsq-0.4.1/misc/inverse_magnetostatics.png +0 -0
  42. fastlsq-0.4.1/misc/inverse_magnetostatics_convergence.png +0 -0
  43. fastlsq-0.4.1/misc/quadrupole_convergence.png +0 -0
  44. fastlsq-0.4.1/misc/quadrupole_optimization.png +0 -0
  45. fastlsq-0.4.1/misc/tutorial_nlpoisson_convergence.png +0 -0
  46. fastlsq-0.4.1/misc/tutorial_nlpoisson_solution.png +0 -0
  47. {fastlsq-0.4.1 → fastlsq-0.4.2}/FastLSQ.egg-info/dependency_links.txt +0 -0
  48. {fastlsq-0.4.1 → fastlsq-0.4.2}/FastLSQ.egg-info/requires.txt +0 -0
  49. {fastlsq-0.4.1 → fastlsq-0.4.2}/FastLSQ.egg-info/top_level.txt +0 -0
  50. {fastlsq-0.4.1 → fastlsq-0.4.2}/LICENSE +0 -0
  51. {fastlsq-0.4.1 → fastlsq-0.4.2}/MANIFEST.in +0 -0
  52. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/add_your_own_pde.py +0 -0
  53. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/benchmark_comparison.py +0 -0
  54. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/custom_features.py +0 -0
  55. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/fred_sde.py +0 -0
  56. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/fred_sde_fastlsq.py +0 -0
  57. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/gaia_potential.py +0 -0
  58. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/gaia_potential_fastlsq.py +0 -0
  59. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/horizons_ephemeris.py +0 -0
  60. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/numerai_alpha.py +0 -0
  61. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/numerai_alpha_fastlsq.py +0 -0
  62. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/run_all_fastlsq.py +0 -0
  63. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/__init__.py +0 -0
  64. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/_alsu_lattice.py +0 -0
  65. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/_common.py +0 -0
  66. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/run_all.py +0 -0
  67. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s01_beamloss_ode.py +0 -0
  68. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s01_betatron_tune.py +0 -0
  69. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s01_green_fff.py +0 -0
  70. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s01_hill_ivp.py +0 -0
  71. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s01_observe_fit_act_simulator.py +0 -0
  72. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s01_orbit_inverse.py +0 -0
  73. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s01_passive_loco.py +0 -0
  74. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s01_perturbed_hill.py +0 -0
  75. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s01_sofb_observe_fit_act.py +0 -0
  76. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s01_streaming_archive_growth.py +0 -0
  77. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s01_synchrotron_ode.py +0 -0
  78. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s01_tides_3months.py +0 -0
  79. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s01_topoff_impulse.py +0 -0
  80. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s01_visualize.py +0 -0
  81. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s02_plasma_wakefield.py +0 -0
  82. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s03_synchrobetatron.py +0 -0
  83. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s04_sunspots.py +0 -0
  84. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s05_helioseismology.py +0 -0
  85. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s06_tides.py +0 -0
  86. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s07_iers_earth_rotation.py +0 -0
  87. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s08_mauna_loa_co2.py +0 -0
  88. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s09_enso_qbo.py +0 -0
  89. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s10_pulsar_timing.py +0 -0
  90. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s11_modal_analysis.py +0 -0
  91. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s12_mems_resonator.py +0 -0
  92. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s13_variable_stars_kepler.py +0 -0
  93. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s14_eeg.py +0 -0
  94. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/scenarios/s15_circadian.py +0 -0
  95. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/extras/spectral_expansion.py +0 -0
  96. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/grad_shafranov.py +0 -0
  97. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/grid_inverse.py +0 -0
  98. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/grid_rl_control.py +0 -0
  99. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/grid_swing.py +0 -0
  100. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/gs_inverse.py +0 -0
  101. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/gs_rl_control.py +0 -0
  102. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/integro_differential_demo.py +0 -0
  103. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/inverse_heat_source.py +0 -0
  104. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/inverse_magnetostatics.py +0 -0
  105. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/inverse_source_position.py +0 -0
  106. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/learnable_helmholtz.py +0 -0
  107. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/memory_diffusion.py +0 -0
  108. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/orbit_hill.py +0 -0
  109. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/orbit_inverse.py +0 -0
  110. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/orbit_rl.py +0 -0
  111. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/pde_discovery.py +0 -0
  112. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/rlc_integro_differential.py +0 -0
  113. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/run_all_extensions.py +0 -0
  114. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/run_linear.py +0 -0
  115. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/run_nonlinear.py +0 -0
  116. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/tutorial_basic.py +0 -0
  117. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/tutorial_nonlinear.py +0 -0
  118. {fastlsq-0.4.1 → fastlsq-0.4.2}/examples/vector_basis_stream_vorticity.py +0 -0
  119. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/api.py +0 -0
  120. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/benchmark.py +0 -0
  121. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/block.py +0 -0
  122. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/device.py +0 -0
  123. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/diagnostics.py +0 -0
  124. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/export.py +0 -0
  125. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/geometry.py +0 -0
  126. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/learnable.py +0 -0
  127. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/lightning.py +0 -0
  128. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/linalg.py +0 -0
  129. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/newton.py +0 -0
  130. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/plotting.py +0 -0
  131. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/problems/__init__.py +0 -0
  132. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/problems/linear.py +0 -0
  133. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/problems/nonlinear.py +0 -0
  134. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/problems/regression.py +0 -0
  135. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/solvers.py +0 -0
  136. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/tuning.py +0 -0
  137. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/utils.py +0 -0
  138. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/vector.py +0 -0
  139. {fastlsq-0.4.1 → fastlsq-0.4.2}/fastlsq/viz.py +0 -0
  140. {fastlsq-0.4.1 → fastlsq-0.4.2}/requirements.txt +0 -0
  141. {fastlsq-0.4.1 → fastlsq-0.4.2}/setup.cfg +0 -0
  142. {fastlsq-0.4.1 → fastlsq-0.4.2}/tests/test_basic.py +0 -0
  143. {fastlsq-0.4.1 → fastlsq-0.4.2}/tests/test_benchmarks_inverse.py +0 -0
  144. {fastlsq-0.4.1 → fastlsq-0.4.2}/tests/test_block.py +0 -0
  145. {fastlsq-0.4.1 → fastlsq-0.4.2}/tests/test_derivatives.py +0 -0
  146. {fastlsq-0.4.1 → fastlsq-0.4.2}/tests/test_device.py +0 -0
  147. {fastlsq-0.4.1 → fastlsq-0.4.2}/tests/test_diagnostics_timing.py +0 -0
  148. {fastlsq-0.4.1 → fastlsq-0.4.2}/tests/test_grad_shafranov.py +0 -0
  149. {fastlsq-0.4.1 → fastlsq-0.4.2}/tests/test_grid_swing.py +0 -0
  150. {fastlsq-0.4.1 → fastlsq-0.4.2}/tests/test_learnable.py +0 -0
  151. {fastlsq-0.4.1 → fastlsq-0.4.2}/tests/test_orbit_hill.py +0 -0
  152. {fastlsq-0.4.1 → fastlsq-0.4.2}/tests/test_projection.py +0 -0
  153. {fastlsq-0.4.1 → fastlsq-0.4.2}/tests/test_vector_basis.py +0 -0
@@ -2,6 +2,42 @@
2
2
 
3
3
  All notable changes to FastLSQ will be documented in this file.
4
4
 
5
+ ## [0.4.2] - 2026-07-20
6
+
7
+ ### Fixed
8
+
9
+ - **`IntegralOperator` with `order >= 2` returned wrong values.** The higher-order
10
+ branch evaluated `F_n(hi) − F_n(lo)`, the difference of n-th antiderivatives, which
11
+ is *not* the n-fold iterated (Volterra) integral: it drops the polynomial terms
12
+ `Σ_{j=1}^{n-1} F_j(lo)·Δ^{n−j}/(n−j)!`. For `order=2` on `[0,1]` this was ~380%
13
+ off against a double cumulative-trapezoid reference. `order=1` was always correct
14
+ and is unchanged. The higher-order path now goes through the new
15
+ `SinusoidalBasis.iterated_integral(..., order=n)`, which implements the Cauchy
16
+ repeated-integration formula `∫_lo^x (x−t)^{n−1}/(n−1)! φ(t) dt` and agrees with
17
+ n-fold quadrature to ~1e-9 (grid-limited) and with 60-digit `mpmath` to ~1e-15.
18
+ - **Near-DC features are now consistent across orders.** The `order >= 2` path went
19
+ through the DC-guarded `derivative`, which *zeroes* any feature with
20
+ `|W_dim| <= dc_eps`, while `order=1` handled it exactly via the `sinc` identity. A
21
+ near-DC feature is constant along the integrated axis, so its iterated integral is
22
+ the finite ramp `φ·Δ^n/n!` — the limit-bearing integral does not diverge, unlike the
23
+ standalone `1/W` antiderivative that `dc_eps` exists to guard. `iterated_integral`
24
+ therefore applies **no DC guard** and is exact as `W_dim → 0` at every order, while
25
+ `derivative` with negative `alpha` keeps its guard because that primitive genuinely
26
+ blows up. Documented in both docstrings.
27
+
28
+ ### Added
29
+
30
+ - **`SinusoidalBasis.iterated_integral(x, dim, lower, upper=None, order=n)`** — the
31
+ n-fold iterated integral in closed form. Evaluated by a cancellation-free Taylor-tail
32
+ series `Δ^n Σ_p θ^p/(n+p)!·Φ_p(Z_lo)` for small `|θ| = |W_dim·Δ|` and by the
33
+ equivalent antiderivative form above it, since the latter's rounding error grows like
34
+ `n!/θ^n`. Holds ~1e-15 (relative to `Δ^n/n!`) for `n ≤ 10`, ~4e-14 by `n = 12`.
35
+ - **`order >= 2` test coverage** in `tests/test_integral.py` (previously every test used
36
+ the default `order=1`): n-fold cumulative-trapezoid and Cauchy-kernel references for
37
+ Volterra and definite integrals, `order=1` agreement between the two code paths,
38
+ near-DC exactness at every order, and an explicit guard that the result is *not* the
39
+ naive difference of n-th antiderivatives.
40
+
5
41
  ## [0.4.1] - 2026-06-23
6
42
 
7
43
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: FastLSQ
3
- Version: 0.4.1
3
+ Version: 0.4.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
@@ -278,7 +278,7 @@ derivative engine:
278
278
  | `SinusoidalBasis` | Evaluates basis functions and arbitrary-order derivatives in O(1) via the cyclic identity |
279
279
  | `BasisCache` | Pre-computes sin(Z)/cos(Z) once, reuses across multiple derivative evaluations |
280
280
  | `DiffOperator` / `Op` | Symbolic linear differential operators that compose via +, -, scalar *; coefficients can be `nn.Parameter` for learnable PDEs |
281
- | `IntegralOperator` / `IntegroDifferentialOperator` | Closed-form **single-axis** definite / running (Volterra) integrals; compose with `Op` into one integro-differential design matrix |
281
+ | `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 |
282
282
  | `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` |
283
283
  | `FeatureBasis` | Adapter for non-sinusoidal solvers (e.g. PIELM with tanh) |
284
284
  | `FastLSQSolver` | Manages feature blocks; exposes `.basis` for all derivative computations |
@@ -35,17 +35,6 @@ examples/run_nonlinear.py
35
35
  examples/tutorial_basic.py
36
36
  examples/tutorial_nonlinear.py
37
37
  examples/vector_basis_stream_vorticity.py
38
- examples/digital_twins/darcy_heat.py
39
- examples/digital_twins/pendulum.py
40
- examples/digital_twins/pendulum_benchmark.py
41
- examples/digital_twins/plasma_wakefield.py
42
- examples/digital_twins/plasma_wakefield_2D_1.py
43
- examples/digital_twins/plasma_wakefield_2D_2.py
44
- examples/digital_twins/plasma_wakefield_2d_3.py
45
- examples/digital_twins/plasma_wakefield_parameteric.py
46
- examples/digital_twins/plot_utils.py
47
- examples/digital_twins/structural_health_simple.py
48
- examples/digital_twins/turbulence_gravity_cooling.py
49
38
  examples/extras/fred_sde.py
50
39
  examples/extras/fred_sde_fastlsq.py
51
40
  examples/extras/gaia_potential.py
@@ -87,22 +76,6 @@ examples/extras/scenarios/s12_mems_resonator.py
87
76
  examples/extras/scenarios/s13_variable_stars_kepler.py
88
77
  examples/extras/scenarios/s14_eeg.py
89
78
  examples/extras/scenarios/s15_circadian.py
90
- examples/inverse/aero_.py
91
- examples/inverse/denoising_parameter_estimation.py
92
- examples/inverse/elastic_wave_animation.py
93
- examples/inverse/heat_from_video.py
94
- examples/inverse/inverse_memory_kernel.py
95
- examples/inverse/inverse_source_integral_sensors.py
96
- examples/inverse/inverse_turbulence.py
97
- examples/inverse/rlc_system_id.py
98
- examples/inverse/shape_ns.py
99
- examples/inverse/subsurface_imaging.py
100
- examples/inverse/tomography_projection.py
101
- examples/inverse/wing_optimize_simple.py
102
- examples/sindy/compare_sindy_methods.py
103
- examples/sindy/sindy_benchmarks.py
104
- examples/sindy/sindy_differentiable.py
105
- examples/sindy/sindy_minimal_diff.py
106
79
  fastlsq/__init__.py
107
80
  fastlsq/api.py
108
81
  fastlsq/basis.py
@@ -126,16 +99,6 @@ fastlsq/problems/__init__.py
126
99
  fastlsq/problems/linear.py
127
100
  fastlsq/problems/nonlinear.py
128
101
  fastlsq/problems/regression.py
129
- misc/fastlsq_teaser.png
130
- misc/ideal_quadrupole.png
131
- misc/inverse_heat_source.gif
132
- misc/inverse_heat_source.png
133
- misc/inverse_magnetostatics.png
134
- misc/inverse_magnetostatics_convergence.png
135
- misc/quadrupole_convergence.png
136
- misc/quadrupole_optimization.png
137
- misc/tutorial_nlpoisson_convergence.png
138
- misc/tutorial_nlpoisson_solution.png
139
102
  tests/test_basic.py
140
103
  tests/test_benchmarks_inverse.py
141
104
  tests/test_block.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: FastLSQ
3
- Version: 0.4.1
3
+ Version: 0.4.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
@@ -278,7 +278,7 @@ derivative engine:
278
278
  | `SinusoidalBasis` | Evaluates basis functions and arbitrary-order derivatives in O(1) via the cyclic identity |
279
279
  | `BasisCache` | Pre-computes sin(Z)/cos(Z) once, reuses across multiple derivative evaluations |
280
280
  | `DiffOperator` / `Op` | Symbolic linear differential operators that compose via +, -, scalar *; coefficients can be `nn.Parameter` for learnable PDEs |
281
- | `IntegralOperator` / `IntegroDifferentialOperator` | Closed-form **single-axis** definite / running (Volterra) integrals; compose with `Op` into one integro-differential design matrix |
281
+ | `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 |
282
282
  | `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` |
283
283
  | `FeatureBasis` | Adapter for non-sinusoidal solvers (e.g. PIELM with tanh) |
284
284
  | `FastLSQSolver` | Manages feature blocks; exposes `.basis` for all derivative computations |
@@ -237,7 +237,7 @@ derivative engine:
237
237
  | `SinusoidalBasis` | Evaluates basis functions and arbitrary-order derivatives in O(1) via the cyclic identity |
238
238
  | `BasisCache` | Pre-computes sin(Z)/cos(Z) once, reuses across multiple derivative evaluations |
239
239
  | `DiffOperator` / `Op` | Symbolic linear differential operators that compose via +, -, scalar *; coefficients can be `nn.Parameter` for learnable PDEs |
240
- | `IntegralOperator` / `IntegroDifferentialOperator` | Closed-form **single-axis** definite / running (Volterra) integrals; compose with `Op` into one integro-differential design matrix |
240
+ | `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 |
241
241
  | `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` |
242
242
  | `FeatureBasis` | Adapter for non-sinusoidal solvers (e.g. PIELM with tanh) |
243
243
  | `FastLSQSolver` | Manages feature blocks; exposes `.basis` for all derivative computations |
@@ -55,7 +55,7 @@ from fastlsq.export import (
55
55
  from fastlsq import viz
56
56
  from fastlsq import benchmark
57
57
 
58
- __version__ = "0.4.1"
58
+ __version__ = "0.4.2"
59
59
  __all__ = [
60
60
  # Device selection (CPU / CUDA / Apple-MPS, dtype-aware)
61
61
  "resolve_device",
@@ -61,6 +61,8 @@ Example
61
61
 
62
62
  from __future__ import annotations
63
63
 
64
+ import math
65
+
64
66
  import torch
65
67
  import numpy as np
66
68
  from typing import Optional, Sequence, Union
@@ -141,8 +143,22 @@ class SinusoidalBasis:
141
143
  sinusoidal antiderivative (its primitive is a ramp that leaves the basis),
142
144
  so its column is zeroed instead of dividing by ~0. Only affects negative
143
145
  orders; ordinary derivatives are untouched.
146
+
147
+ It also does **not** affect the limit-bearing integrals
148
+ (:meth:`definite_integral`, :meth:`iterated_integral`): over a finite interval
149
+ the ramp is perfectly well defined -- a DC feature integrates to ``φ·Δ^n/n!``
150
+ -- so those are computed exactly, with no guard, at every order. Only the
151
+ *standalone* primitive genuinely diverges as ``1/W``, and only it is guarded.
144
152
  """
145
153
 
154
+ # Branch point for :meth:`iterated_integral`. Below |θ| = W_dim·Δ the closed form
155
+ # suffers catastrophic cancellation (its rounding error grows like n!/θ^n), so the
156
+ # equivalent Taylor-tail series is used; above it the series would need many more
157
+ # terms. 40 terms hold both branches to ~1e-15 (relative to Δ^n/n!) for n ≤ 10,
158
+ # degrading gracefully to ~4e-14 by n = 12; verified against 60-digit mpmath.
159
+ _ITER_THETA_MAX = 4.0
160
+ _ITER_TERMS = 40
161
+
146
162
  def __init__(
147
163
  self,
148
164
  W: torch.Tensor,
@@ -358,6 +374,99 @@ class SinusoidalBasis:
358
374
  out = delta * torch.sin(half_sum) * torch.sinc(sinc_arg)
359
375
  return out * self._inv_norm
360
376
 
377
+ def iterated_integral(
378
+ self,
379
+ x: torch.Tensor,
380
+ dim: int,
381
+ lower: float,
382
+ upper: Optional[float] = None,
383
+ order: int = 1,
384
+ cache: Optional[BasisCache] = None,
385
+ ) -> torch.Tensor:
386
+ """``order``-fold *iterated* (Cauchy) integral along one axis, shape (M, N).
387
+
388
+ This is the repeated integral with all ``order`` lower limits pinned at
389
+ ``lower`` -- the operator a Volterra integral equation actually applies --
390
+
391
+ (I^n φ)(x) = ∫_lo^x ∫_lo^{s_1} … ∫_lo^{s_{n-1}} φ dt … ds_1
392
+ = ∫_lo^x (x − t)^{n−1}/(n−1)! · φ(t) dt,
393
+
394
+ **not** the difference of n-th antiderivatives ``F_n(hi) − F_n(lo)``, which
395
+ omits the polynomial terms ``Σ_{j=1}^{n-1} F_j(lo)·Δ^{n−j}/(n−j)!``.
396
+
397
+ Evaluated by a *cancellation-free* series in the small-``|W·Δ|`` regime and by
398
+ the equivalent closed form otherwise. Writing Δ = hi − lo, θ = W_dim·Δ and
399
+ Φ_k(Z) = sin(Z + kπ/2), the two branches are
400
+
401
+ θ small: (I^n φ)(x) = Δ^n · Σ_{p≥0} θ^p/(n+p)! · Φ_p(Z_lo)
402
+ θ large: (I^n φ)(x) = F_n(Z_hi) − Σ_{j=1}^{n} F_j(Z_lo)·Δ^{n−j}/(n−j)!,
403
+ with F_j(Z) = Φ_{−j}(Z) / W_dim^j.
404
+
405
+ The series is the exact Taylor tail left over after that cancellation, so it is
406
+ finite and exact as ``W_dim → 0`` -- **no DC guard is applied or needed here**.
407
+ A near-DC feature is constant along ``dim`` and correctly integrates to
408
+ ``φ · Δ^n/n!``, matching the ``order=1`` sinc identity in
409
+ :meth:`definite_integral`. (This is deliberately unlike :meth:`derivative`
410
+ with negative ``alpha``, whose standalone antiderivative genuinely diverges as
411
+ ``1/W`` and so is DC-guarded to zero.)
412
+
413
+ ``upper=None`` gives the running (Volterra) integral with variable upper limit
414
+ ``x[:, dim]``; a numeric ``upper`` pins it. ``order=1`` reproduces
415
+ :meth:`definite_integral` to machine precision.
416
+ """
417
+ if order < 1:
418
+ raise ValueError("integration order must be a positive integer")
419
+ if x.dtype != self.W.dtype or x.device != self.W.device:
420
+ x = x.to(dtype=self.W.dtype, device=self.W.device)
421
+
422
+ n = int(order)
423
+ Wd = self.W[dim : dim + 1, :] # (1, N)
424
+
425
+ x_lo = x.clone()
426
+ x_lo[:, dim] = lower
427
+ cache_lo = self.cache(x_lo) # phases at the (common) lower limit
428
+
429
+ if upper is None: # Volterra: upper limit is x_dim itself
430
+ cache_hi = cache if cache is not None else self.cache(x)
431
+ t_hi = x[:, dim : dim + 1] # (M, 1)
432
+ else:
433
+ x_hi = x.clone()
434
+ x_hi[:, dim] = upper
435
+ cache_hi = self.cache(x_hi)
436
+ t_hi = torch.full(
437
+ (x.shape[0], 1), float(upper), device=self.W.device, dtype=self.W.dtype
438
+ )
439
+
440
+ delta = t_hi - float(lower) # (M, 1)
441
+ theta = Wd * delta # (M, N)
442
+ small = theta.abs() <= self._ITER_THETA_MAX
443
+
444
+ # 1/k! as plain floats: torch cannot divide a tensor by math.factorial's
445
+ # arbitrary-precision int, and float() keeps it to a single correct rounding.
446
+ inv_fact = [1.0 / float(math.factorial(k)) for k in range(n + self._ITER_TERMS)]
447
+
448
+ # --- Series branch: Δ^n Σ_p θ^p/(n+p)! Φ_p(Z_lo). θ is zeroed outside the
449
+ # small-|θ| set so the powers cannot overflow (matters in float32).
450
+ th_s = torch.where(small, theta, torch.zeros_like(theta))
451
+ series = torch.zeros_like(theta)
452
+ th_pow = torch.ones_like(theta) # θ^p
453
+ for p in range(self._ITER_TERMS):
454
+ series = series + (th_pow * inv_fact[n + p]) * cache_lo.phase(p)
455
+ th_pow = th_pow * th_s
456
+ series = series * (delta ** n)
457
+
458
+ # --- Closed branch: F_n(Z_hi) − Σ_j F_j(Z_lo) Δ^{n-j}/(n-j)!. W is forced to
459
+ # 1 on the small-|θ| set so the unselected branch cannot produce inf/NaN (which
460
+ # would otherwise poison gradients through torch.where).
461
+ Wd_safe = torch.where(small, torch.ones_like(theta), Wd.expand_as(theta))
462
+ closed = cache_hi.phase(-n) / Wd_safe ** n
463
+ for j in range(1, n + 1):
464
+ closed = closed - (cache_lo.phase(-j) / Wd_safe ** j) * (
465
+ delta ** (n - j) * inv_fact[n - j]
466
+ )
467
+
468
+ return torch.where(small, series, closed) * self._inv_norm
469
+
361
470
  # ------------------------------------------------------------------
362
471
  # Convenience: 0th order (basis values)
363
472
  # ------------------------------------------------------------------
@@ -874,7 +983,14 @@ class IntegralOperator:
874
983
 
875
984
  ``apply(basis, x)`` returns an (M, N) design matrix: for Volterra, the running
876
985
  integral up to each ``x``; for a partial definite integral in d>1, the matrix
877
- marginalised along ``dim`` (other coordinates held at ``x``). Composes with
986
+ marginalised along ``dim`` (other coordinates held at ``x``).
987
+
988
+ ``order=n > 1`` is the n-fold **iterated** integral with every lower limit pinned
989
+ at ``lower`` -- ∫_lo^x ∫_lo^{s_1} … φ, equivalently the Cauchy form
990
+ ∫_lo^x (x−t)^{n−1}/(n−1)! φ(t) dt -- which is what a higher-order Volterra equation
991
+ applies. It is *not* the difference of n-th antiderivatives F_n(hi) − F_n(lo);
992
+ see :meth:`SinusoidalBasis.iterated_integral`. Near-DC features are exact and
993
+ finite at every order (no DC guard). Composes with
878
994
  :class:`DiffOperator` via ``+ - *`` into an :class:`IntegroDifferentialOperator`,
879
995
  so an integro-differential / integral equation is one linear-least-squares block:
880
996
 
@@ -931,20 +1047,13 @@ class IntegralOperator:
931
1047
  return basis.definite_integral(
932
1048
  x, self.dim, self.lower, upper=self.upper, cache=cache
933
1049
  )
934
- # order >= 2: difference of (DC-guarded) higher antiderivatives.
935
- alpha = tuple(-self.order if k == self.dim else 0 for k in range(self.d))
936
- F_lo = basis.derivative(self._shift(x, self.lower), alpha)
937
- if self.upper is None:
938
- F_hi = basis.derivative(x, alpha, cache=cache)
939
- else:
940
- F_hi = basis.derivative(self._shift(x, self.upper), alpha)
941
- return F_hi - F_lo
942
-
943
- def _shift(self, x: torch.Tensor, value: float) -> torch.Tensor:
944
- """Copy of ``x`` with the integration axis pinned to ``value``."""
945
- xs = x.clone()
946
- xs[:, self.dim] = value
947
- return xs
1050
+ # order >= 2: the n-fold *iterated* (Cauchy) integral, all lower limits pinned
1051
+ # at ``lower``. Note this is NOT F_n(hi) F_n(lo): that difference drops the
1052
+ # polynomial terms Σ_{j<n} F_j(lo)·Δ^{n−j}/(n−j)!. Like the order-1 branch it
1053
+ # is exact and finite for near-DC features (no DC guard).
1054
+ return basis.iterated_integral(
1055
+ x, self.dim, self.lower, upper=self.upper, order=self.order, cache=cache
1056
+ )
948
1057
 
949
1058
  # ------------------------------------------------------------------
950
1059
  # Arithmetic composition -> IntegroDifferentialOperator
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "FastLSQ"
7
- version = "0.4.1"
7
+ version = "0.4.2"
8
8
  description = "One-shot PDE solving via Fourier features with exact analytical derivatives; rank-revealing solvers, learnable anisotropic bandwidth, and CPU/CUDA/MPS support"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -12,6 +12,11 @@ reciprocal prefactor. These tests make that a *tested* property:
12
12
  * mixed integro-differential multi-indices match the analytic closed form and autodiff;
13
13
  * the DC guard keeps near-zero-frequency antiderivatives finite;
14
14
  * definite / running (Volterra) integrals agree with high-resolution quadrature;
15
+ * higher-order (n-fold ITERATED) integrals agree with n-fold cumulative trapezoid and
16
+ with the Cauchy repeated-integration kernel -- and are *not* the naive difference of
17
+ n-th antiderivatives, which drops the polynomial terms;
18
+ * near-DC features integrate to the exact ramp phi*Delta^n/n! at every order, rather
19
+ than being zeroed as the standalone (genuinely divergent) antiderivative is;
15
20
  * differential and integral terms compose into one (M, N) design matrix, through which
16
21
  gradients flow to learnable coefficients;
17
22
  * a Volterra integro-differential boundary-value problem is solved in one LSQ to <1e-5.
@@ -19,6 +24,8 @@ reciprocal prefactor. These tests make that a *tested* property:
19
24
  Run with ``pytest`` or directly as a script.
20
25
  """
21
26
 
27
+ import math
28
+
22
29
  import numpy as np
23
30
  import torch
24
31
 
@@ -126,6 +133,126 @@ def test_definite_matches_quadrature():
126
133
  assert (rows[0].squeeze() - exact).abs() < 1e-6
127
134
 
128
135
 
136
+ def _iterated_trapz(fg, xs, n):
137
+ """n-fold cumulative trapezoid of ``fg`` on grid ``xs`` (all lower limits at xs[0])."""
138
+ cur = fg
139
+ for _ in range(n):
140
+ nxt = torch.zeros_like(cur)
141
+ nxt[1:] = torch.cumulative_trapezoid(cur, xs)
142
+ cur = nxt
143
+ return cur
144
+
145
+
146
+ def test_iterated_volterra_matches_quadrature():
147
+ """order>=2 is the n-fold ITERATED (Volterra) integral, matching an n-fold cumulative
148
+ trapezoid. Regression guard: the old code returned F_n(hi) - F_n(lo), which drops the
149
+ polynomial terms sum_j F_j(lo)*(x-lo)^(n-j)/(n-j)! and was ~380% off for n=2."""
150
+ torch.manual_seed(3)
151
+ basis = SinusoidalBasis.random(1, 32, sigma=3.0, normalize=False)
152
+ beta = torch.randn(32, 1)
153
+
154
+ xg = torch.linspace(0.0, 1.0, 20001).reshape(-1, 1)
155
+ xs = xg.squeeze()
156
+ fg = (basis.evaluate(xg) @ beta).squeeze()
157
+
158
+ for n in (1, 2, 3, 4):
159
+ ref = _iterated_trapz(fg, xs, n)
160
+ got = (IntegralOperator.volterra(dim=0, lower=0.0, d=1, order=n).apply(basis, xg)
161
+ @ beta).squeeze()
162
+ assert torch.isfinite(got).all()
163
+ rel = (torch.norm(got - ref) / torch.norm(ref)).item()
164
+ assert rel < 1e-6, f"order {n}: volterra vs {n}x-trapz rel-L2 = {rel:.2e}"
165
+
166
+
167
+ def test_iterated_volterra_matches_cauchy_kernel():
168
+ """Independent reference: the n-fold iterated integral equals the SINGLE integral
169
+ int_lo^x (x-t)^(n-1)/(n-1)! phi(t) dt (Cauchy formula for repeated integration)."""
170
+ torch.manual_seed(11)
171
+ basis = SinusoidalBasis.random(1, 24, sigma=2.5, normalize=False)
172
+ beta = torch.randn(24, 1)
173
+
174
+ lo, x_eval = -0.4, 0.85
175
+ tg = torch.linspace(lo, x_eval, 40001)
176
+ fg = (basis.evaluate(tg.reshape(-1, 1)) @ beta).squeeze()
177
+
178
+ for n in (2, 3, 5):
179
+ kernel = (x_eval - tg) ** (n - 1) / float(math.factorial(n - 1))
180
+ ref = torch.trapz(kernel * fg, tg)
181
+ got = (IntegralOperator.volterra(dim=0, lower=lo, d=1, order=n)
182
+ .apply(basis, torch.tensor([[x_eval]])) @ beta).squeeze()
183
+ rel = ((got - ref).abs() / ref.abs()).item()
184
+ assert rel < 1e-6, f"order {n}: vs Cauchy kernel rel = {rel:.2e}"
185
+
186
+
187
+ def test_iterated_definite_matches_quadrature():
188
+ """The order-n definite integral over [a, b] equals the n-fold running integral
189
+ evaluated at b, and is identical for every row in 1D."""
190
+ torch.manual_seed(4)
191
+ basis = SinusoidalBasis.random(1, 48, sigma=4.0, normalize=False)
192
+ beta = torch.randn(48, 1)
193
+
194
+ a, b = 0.2, 0.9
195
+ xg = torch.linspace(a, b, 20001).reshape(-1, 1)
196
+ fg = (basis.evaluate(xg) @ beta).squeeze()
197
+
198
+ for n in (2, 3):
199
+ ref = _iterated_trapz(fg, xg.squeeze(), n)[-1]
200
+ I = IntegralOperator.definite(dim=0, lower=a, upper=b, d=1, order=n)
201
+ rows = I.apply(basis, torch.rand(5, 1)) @ beta # result independent of x in 1D
202
+ assert torch.allclose(rows, rows[:1].expand_as(rows), atol=1e-10)
203
+ rel = ((rows[0].squeeze() - ref).abs() / ref.abs()).item()
204
+ assert rel < 1e-6, f"order {n}: definite vs {n}x-trapz rel = {rel:.2e}"
205
+
206
+
207
+ def test_iterated_order_one_matches_definite_integral():
208
+ """order=1 of the general iterated form reproduces the sinc closed form exactly, so the
209
+ two code paths in IntegralOperator.apply cannot drift apart."""
210
+ torch.manual_seed(12)
211
+ basis = SinusoidalBasis.random(2, 64, sigma=3.0, normalize=False)
212
+ x = sample_box(200, 2)
213
+ for dim, lo, hi in ((0, -0.4, None), (1, 0.1, 0.75)):
214
+ ref = basis.definite_integral(x, dim, lo, upper=hi)
215
+ got = basis.iterated_integral(x, dim, lo, upper=hi, order=1)
216
+ assert torch.allclose(got, ref, atol=1e-14, rtol=1e-11)
217
+
218
+
219
+ def test_iterated_near_dc_is_exact_not_zeroed():
220
+ """Documented semantics: unlike the DC-guarded standalone antiderivative (whose 1/W
221
+ primitive genuinely diverges), the *limit-bearing* iterated integral of a near-DC
222
+ feature is finite and exact -- phi * Delta^n / n! -- and must NOT be zeroed. This keeps
223
+ order>=2 consistent with the order=1 sinc path (cf. test_dc_guard_no_blowup)."""
224
+ W = torch.tensor([[0.0, 1e-13, 2.0]])
225
+ b = torch.tensor([[0.7, 0.7, 0.7]])
226
+ basis = SinusoidalBasis(W, b, normalize=False)
227
+
228
+ delta = 1.3
229
+ x = torch.tensor([[delta]])
230
+ for n in (1, 2, 3, 4):
231
+ got = basis.iterated_integral(x, 0, 0.0, order=n)[0]
232
+ assert torch.isfinite(got).all()
233
+ exact_dc = np.sin(0.7) * delta ** n / float(math.factorial(n))
234
+ # both the exactly-DC and the near-DC column hit the ramp value, not zero
235
+ assert abs(got[0].item() - exact_dc) < 1e-13, f"order {n}: DC col = {got[0].item()}"
236
+ assert abs(got[1].item() - exact_dc) < 1e-13
237
+ assert got[0].abs().item() > 1e-3 # explicitly: not zeroed
238
+
239
+
240
+ def test_iterated_differs_from_antiderivative_difference():
241
+ """Pins the actual bug: for n=2 the correct value is F2(x)-F2(lo)-F1(lo)*(x-lo), so it
242
+ must differ from the naive difference F2(x)-F2(lo) by exactly that ramp term."""
243
+ torch.manual_seed(13)
244
+ basis = SinusoidalBasis.random(1, 16, sigma=2.0, normalize=False)
245
+ x = torch.linspace(0.1, 1.0, 9).reshape(-1, 1)
246
+ lo = 0.0
247
+
248
+ naive = basis.derivative(x, (-2,)) - basis.derivative(torch.full_like(x, lo), (-2,))
249
+ got = basis.iterated_integral(x, 0, lo, order=2)
250
+ ramp = basis.derivative(torch.full_like(x, lo), (-1,)) * (x - lo)
251
+
252
+ assert torch.allclose(got, naive - ramp, atol=1e-10, rtol=1e-8)
253
+ assert not torch.allclose(got, naive, atol=1e-6) # the dropped term is not negligible
254
+
255
+
129
256
  def test_integro_differential_compose():
130
257
  """Differential + integral terms compose into one (M, N) matrix equal to the sum of parts."""
131
258
  torch.manual_seed(5)
@@ -189,6 +316,12 @@ if __name__ == "__main__":
189
316
  test_dc_guard_no_blowup()
190
317
  test_volterra_matches_quadrature()
191
318
  test_definite_matches_quadrature()
319
+ test_iterated_volterra_matches_quadrature()
320
+ test_iterated_volterra_matches_cauchy_kernel()
321
+ test_iterated_definite_matches_quadrature()
322
+ test_iterated_order_one_matches_definite_integral()
323
+ test_iterated_near_dc_is_exact_not_zeroed()
324
+ test_iterated_differs_from_antiderivative_difference()
192
325
  test_integro_differential_compose()
193
326
  test_learnable_coeff_grad_flows()
194
327
  test_volterra_ivp_forward_solve()