FastLSQ 0.1.5__tar.gz → 0.2.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.1.5 → fastlsq-0.2.1}/CHANGELOG.md +81 -20
- {fastlsq-0.1.5 → fastlsq-0.2.1/FastLSQ.egg-info}/PKG-INFO +4 -3
- fastlsq-0.2.1/FastLSQ.egg-info/SOURCES.txt +116 -0
- {fastlsq-0.1.5/FastLSQ.egg-info → fastlsq-0.2.1}/PKG-INFO +4 -3
- fastlsq-0.2.1/examples/extras/fred_sde.py +262 -0
- fastlsq-0.2.1/examples/extras/fred_sde_fastlsq.py +329 -0
- fastlsq-0.2.1/examples/extras/gaia_potential.py +183 -0
- fastlsq-0.2.1/examples/extras/gaia_potential_fastlsq.py +397 -0
- fastlsq-0.2.1/examples/extras/horizons_ephemeris.py +319 -0
- fastlsq-0.2.1/examples/extras/numerai_alpha.py +214 -0
- fastlsq-0.2.1/examples/extras/numerai_alpha_fastlsq.py +374 -0
- fastlsq-0.2.1/examples/extras/run_all_fastlsq.py +107 -0
- fastlsq-0.2.1/examples/extras/scenarios/__init__.py +0 -0
- fastlsq-0.2.1/examples/extras/scenarios/_alsu_lattice.py +196 -0
- fastlsq-0.2.1/examples/extras/scenarios/_common.py +164 -0
- fastlsq-0.2.1/examples/extras/scenarios/run_all.py +70 -0
- fastlsq-0.2.1/examples/extras/scenarios/s01_beamloss_ode.py +340 -0
- fastlsq-0.2.1/examples/extras/scenarios/s01_betatron_tune.py +448 -0
- fastlsq-0.2.1/examples/extras/scenarios/s01_green_fff.py +262 -0
- fastlsq-0.2.1/examples/extras/scenarios/s01_hill_ivp.py +263 -0
- fastlsq-0.2.1/examples/extras/scenarios/s01_observe_fit_act_simulator.py +353 -0
- fastlsq-0.2.1/examples/extras/scenarios/s01_orbit_inverse.py +332 -0
- fastlsq-0.2.1/examples/extras/scenarios/s01_passive_loco.py +246 -0
- fastlsq-0.2.1/examples/extras/scenarios/s01_perturbed_hill.py +247 -0
- fastlsq-0.2.1/examples/extras/scenarios/s01_sofb_observe_fit_act.py +619 -0
- fastlsq-0.2.1/examples/extras/scenarios/s01_streaming_archive_growth.py +310 -0
- fastlsq-0.2.1/examples/extras/scenarios/s01_synchrotron_ode.py +335 -0
- fastlsq-0.2.1/examples/extras/scenarios/s01_tides_3months.py +257 -0
- fastlsq-0.2.1/examples/extras/scenarios/s01_topoff_impulse.py +375 -0
- fastlsq-0.2.1/examples/extras/scenarios/s01_visualize.py +400 -0
- fastlsq-0.2.1/examples/extras/scenarios/s02_plasma_wakefield.py +99 -0
- fastlsq-0.2.1/examples/extras/scenarios/s03_synchrobetatron.py +75 -0
- fastlsq-0.2.1/examples/extras/scenarios/s04_sunspots.py +116 -0
- fastlsq-0.2.1/examples/extras/scenarios/s05_helioseismology.py +105 -0
- fastlsq-0.2.1/examples/extras/scenarios/s06_tides.py +122 -0
- fastlsq-0.2.1/examples/extras/scenarios/s07_iers_earth_rotation.py +97 -0
- fastlsq-0.2.1/examples/extras/scenarios/s08_mauna_loa_co2.py +96 -0
- fastlsq-0.2.1/examples/extras/scenarios/s09_enso_qbo.py +131 -0
- fastlsq-0.2.1/examples/extras/scenarios/s10_pulsar_timing.py +99 -0
- fastlsq-0.2.1/examples/extras/scenarios/s11_modal_analysis.py +109 -0
- fastlsq-0.2.1/examples/extras/scenarios/s12_mems_resonator.py +150 -0
- fastlsq-0.2.1/examples/extras/scenarios/s13_variable_stars_kepler.py +111 -0
- fastlsq-0.2.1/examples/extras/scenarios/s14_eeg.py +94 -0
- fastlsq-0.2.1/examples/extras/scenarios/s15_circadian.py +130 -0
- fastlsq-0.2.1/examples/extras/spectral_expansion.py +96 -0
- fastlsq-0.2.1/examples/grad_shafranov.py +186 -0
- fastlsq-0.2.1/examples/grid_inverse.py +74 -0
- fastlsq-0.2.1/examples/grid_rl_control.py +128 -0
- fastlsq-0.2.1/examples/grid_swing.py +130 -0
- fastlsq-0.2.1/examples/gs_inverse.py +127 -0
- fastlsq-0.2.1/examples/gs_rl_control.py +182 -0
- fastlsq-0.2.1/examples/orbit_hill.py +210 -0
- fastlsq-0.2.1/examples/orbit_inverse.py +98 -0
- fastlsq-0.2.1/examples/orbit_rl.py +154 -0
- fastlsq-0.2.1/examples/vector_basis_stream_vorticity.py +146 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/fastlsq/__init__.py +12 -1
- {fastlsq-0.1.5 → fastlsq-0.2.1}/fastlsq/api.py +10 -5
- {fastlsq-0.1.5 → fastlsq-0.2.1}/fastlsq/basis.py +46 -9
- fastlsq-0.2.1/fastlsq/device.py +130 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/fastlsq/geometry.py +16 -16
- {fastlsq-0.1.5 → fastlsq-0.2.1}/fastlsq/learnable.py +109 -68
- fastlsq-0.2.1/fastlsq/linalg.py +137 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/fastlsq/newton.py +2 -2
- {fastlsq-0.1.5 → fastlsq-0.2.1}/fastlsq/problems/__init__.py +6 -4
- {fastlsq-0.1.5 → fastlsq-0.2.1}/fastlsq/problems/linear.py +29 -55
- {fastlsq-0.1.5 → fastlsq-0.2.1}/fastlsq/solvers.py +7 -7
- {fastlsq-0.1.5 → fastlsq-0.2.1}/fastlsq/tuning.py +3 -4
- {fastlsq-0.1.5 → fastlsq-0.2.1}/fastlsq/utils.py +8 -2
- fastlsq-0.2.1/fastlsq/vector.py +412 -0
- fastlsq-0.2.1/fastlsq/viz.py +456 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/misc/inverse_heat_source.gif +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/pyproject.toml +4 -3
- fastlsq-0.2.1/tests/test_block.py +68 -0
- fastlsq-0.2.1/tests/test_derivatives.py +209 -0
- fastlsq-0.2.1/tests/test_device.py +43 -0
- fastlsq-0.2.1/tests/test_grad_shafranov.py +41 -0
- fastlsq-0.2.1/tests/test_grid_swing.py +40 -0
- fastlsq-0.2.1/tests/test_learnable.py +98 -0
- fastlsq-0.2.1/tests/test_orbit_hill.py +42 -0
- fastlsq-0.2.1/tests/test_vector_basis.py +220 -0
- fastlsq-0.1.5/FastLSQ.egg-info/SOURCES.txt +0 -77
- fastlsq-0.1.5/examples/digital_twins/darcy_heat.py +0 -583
- fastlsq-0.1.5/examples/digital_twins/pendulum.py +0 -242
- fastlsq-0.1.5/examples/digital_twins/pendulum_benchmark.py +0 -281
- fastlsq-0.1.5/examples/digital_twins/plasma_wakefield.py +0 -339
- fastlsq-0.1.5/examples/digital_twins/plasma_wakefield_2D_1.py +0 -863
- fastlsq-0.1.5/examples/digital_twins/plasma_wakefield_2D_2.py +0 -769
- fastlsq-0.1.5/examples/digital_twins/plasma_wakefield_2d_3.py +0 -818
- fastlsq-0.1.5/examples/digital_twins/plasma_wakefield_parameteric.py +0 -475
- fastlsq-0.1.5/examples/digital_twins/plot_utils.py +0 -42
- fastlsq-0.1.5/examples/digital_twins/structural_health_simple.py +0 -343
- fastlsq-0.1.5/examples/digital_twins/turbulence_gravity_cooling.py +0 -387
- fastlsq-0.1.5/examples/inverse/aero_.py +0 -362
- fastlsq-0.1.5/examples/inverse/denoising_parameter_estimation.py +0 -297
- fastlsq-0.1.5/examples/inverse/elastic_wave_animation.py +0 -300
- fastlsq-0.1.5/examples/inverse/heat_from_video.py +0 -394
- fastlsq-0.1.5/examples/inverse/inverse_turbulence.py +0 -811
- fastlsq-0.1.5/examples/inverse/shape_ns.py +0 -1426
- fastlsq-0.1.5/examples/inverse/subsurface_imaging.py +0 -547
- fastlsq-0.1.5/examples/inverse/wing_optimize_simple.py +0 -283
- fastlsq-0.1.5/examples/sindy/compare_sindy_methods.py +0 -760
- fastlsq-0.1.5/examples/sindy/sindy_benchmarks.py +0 -351
- fastlsq-0.1.5/examples/sindy/sindy_differentiable.py +0 -418
- fastlsq-0.1.5/examples/sindy/sindy_minimal_diff.py +0 -716
- fastlsq-0.1.5/fastlsq/linalg.py +0 -34
- {fastlsq-0.1.5 → fastlsq-0.2.1}/FastLSQ.egg-info/dependency_links.txt +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/FastLSQ.egg-info/requires.txt +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/FastLSQ.egg-info/top_level.txt +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/LICENSE +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/MANIFEST.in +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/README.md +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/examples/add_your_own_pde.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/examples/benchmark_comparison.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/examples/custom_features.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/examples/inverse_heat_source.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/examples/inverse_magnetostatics.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/examples/inverse_source_position.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/examples/learnable_helmholtz.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/examples/pde_discovery.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/examples/run_all_extensions.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/examples/run_linear.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/examples/run_nonlinear.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/examples/tutorial_basic.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/examples/tutorial_nonlinear.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/fastlsq/block.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/fastlsq/diagnostics.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/fastlsq/export.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/fastlsq/lightning.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/fastlsq/plotting.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/fastlsq/problems/nonlinear.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/fastlsq/problems/regression.py +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/misc/fastlsq_teaser.png +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/misc/ideal_quadrupole.png +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/misc/inverse_heat_source.png +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/misc/inverse_magnetostatics.png +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/misc/inverse_magnetostatics_convergence.png +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/misc/quadrupole_convergence.png +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/misc/quadrupole_optimization.png +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/misc/tutorial_nlpoisson_convergence.png +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/misc/tutorial_nlpoisson_solution.png +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/requirements.txt +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/setup.cfg +0 -0
- {fastlsq-0.1.5 → fastlsq-0.2.1}/tests/test_basic.py +0 -0
|
@@ -2,29 +2,90 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to FastLSQ will be documented in this file.
|
|
4
4
|
|
|
5
|
-
## [
|
|
5
|
+
## [0.2.1] - 2026-06-02
|
|
6
6
|
|
|
7
7
|
### Added
|
|
8
8
|
|
|
9
|
-
- **
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
- `
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
9
|
+
- **Device abstraction** (`fastlsq/device.py`): `resolve_device`, `set_device`,
|
|
10
|
+
`get_device`, `device_info` for CPU / CUDA / Apple-MPS. dtype-aware -- MPS is
|
|
11
|
+
auto-selected only for float32 (it has no float64), so the default float64
|
|
12
|
+
high-accuracy regime stays on CPU/CUDA. Override with `set_device(...)` or the
|
|
13
|
+
`FASTLSQ_DEVICE` environment variable; internal tensor creation respects the
|
|
14
|
+
active device at call time.
|
|
15
|
+
- **Pluggable linear solver** `solve_lstsq(..., method=...)`:
|
|
16
|
+
- `"svd"` -- rank-revealing truncated SVD (LAPACK `gelsd` fast path on CPU);
|
|
17
|
+
- `"cholesky"` -- fast normal-equations solve for well-conditioned systems;
|
|
18
|
+
- `"rsvd"` -- torch-native randomized SVD (`O(MNk)`) for strongly low-rank `A`;
|
|
19
|
+
- `"auto"` (default) -- Cholesky with a cheap conditioning probe, falling back
|
|
20
|
+
to SVD when ill-conditioned (recovers the fast path without losing accuracy).
|
|
21
|
+
MPS factorizations run on CPU (no robust `svd`/`lstsq` there) and move back.
|
|
22
|
+
- **Working anisotropic Sigma = L Lᵀ learner**: `LearnableFastLSQ` (diagonal &
|
|
23
|
+
cholesky modes) now converges -- `solve_inner` uses a differentiable
|
|
24
|
+
*rank-revealing* solve, the Cholesky factor is log-parameterized (clamped,
|
|
25
|
+
positive-definite), and `train_bandwidth` is robust (gradient clipping,
|
|
26
|
+
best-iterate restore, graceful SVD/gradient-failure handling). Chainable
|
|
27
|
+
`LearnableFastLSQ.fit(problem, ...)` for one-line learn-then-predict.
|
|
28
|
+
- **Vector-valued solutions (`u: ℝᵈ → ℝᵏ`)**: first-class support for coupled and
|
|
29
|
+
decoupled multi-output PDEs via the new `fastlsq.block` module. Problems opt in
|
|
30
|
+
with `self.n_outputs = k`; `solver.beta` is `(N, k)` and `solver.predict(x)`
|
|
31
|
+
returns `(M, k)` (scalar `k=1` is bit-for-bit unchanged). `block_concat`
|
|
32
|
+
assembles a nested list of blocks (`None` = zero block); `pack_beta` /
|
|
33
|
+
`unpack_beta` convert between `(N, k)` and the block-stacked `(N*k, 1)` solve.
|
|
34
|
+
The block-stacked LSQ is solved by the rank-revealing solver, and the
|
|
35
|
+
Σ-learner computes its loss on the flat `_beta_flat` so it stays correct for
|
|
36
|
+
`k>1`.
|
|
37
|
+
- **Learnable operator coefficients**: `Op` accepts `nn.Parameter` (and tensors)
|
|
38
|
+
as coefficients, e.g. `Op.laplacian(d=2) + k**2 * Op.identity(d=2)` with
|
|
39
|
+
`k = nn.Parameter(...)`; gradients flow through the prebuilt linear solve.
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- `solve_lstsq` defaults to the rank-revealing / `auto` solve instead of forming
|
|
44
|
+
the normal equations -- several orders of magnitude more accurate on the
|
|
45
|
+
rank-deficient random-feature systems (at a higher, still one-shot, cost).
|
|
46
|
+
- `solve_linear` is one-shot: the bandwidth-learning hyper-parameters live on
|
|
47
|
+
`LearnableFastLSQ.fit()` / `train_bandwidth`, not the solve signature.
|
|
48
|
+
- Packaging: `requires-python` lowered to `>=3.9` (+3.9 classifier); description
|
|
49
|
+
updated.
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- The previously dead `LearnableFastLSQ(mode="cholesky")` path, which diverged
|
|
54
|
+
from an unstable inner `torch.linalg.lstsq` and an unconstrained `L`.
|
|
55
|
+
|
|
56
|
+
## [0.1.5] - 2026-05-25
|
|
57
|
+
|
|
58
|
+
### Added
|
|
59
|
+
|
|
60
|
+
- **Vector-valued features**: new `VectorBasis` and `VectorFastLSQSolver`
|
|
61
|
+
(`fastlsq/vector.py`) for solving coupled systems where the unknown is a
|
|
62
|
+
vector field `u(x) = (u_1, ..., u_K)`. Use cases include
|
|
63
|
+
streamfunction-vorticity NS `(psi, omega)`, incompressible NS primitive
|
|
64
|
+
variables `(u, v, p)`, multi-species transport, MHD, etc.
|
|
65
|
+
|
|
66
|
+
- `VectorBasis.random(input_dim, n_features, sigma, n_components, sigmas=...)`
|
|
67
|
+
creates K independent random-Fourier `SinusoidalBasis`es; per-component
|
|
68
|
+
bandwidth can be tuned via `sigmas`.
|
|
69
|
+
- Stacked evaluators: `evaluate(x) -> (M, K, N)`, `gradient -> (M, K, d, N)`,
|
|
70
|
+
`laplacian`, `hessian_diag`, `derivative(alpha)`.
|
|
71
|
+
- Block-diagonal assembly helpers (`block_diag_evaluate`,
|
|
72
|
+
`block_diag_laplacian`, `block_diag_derivative`) for systems whose
|
|
73
|
+
rows are independent per component.
|
|
74
|
+
- Coefficient packing utilities (`stack_betas`, `unstack_beta`,
|
|
75
|
+
`predict`) accept per-component lists, stacked columns, or
|
|
76
|
+
`(N, K)` matrices interchangeably.
|
|
77
|
+
- `VectorFastLSQSolver(input_dim, n_components, normalize=True)` is the
|
|
78
|
+
multi-component counterpart of `FastLSQSolver`; `add_block(scale=...)`
|
|
79
|
+
accepts either a scalar or a list of K scalars for per-component
|
|
80
|
+
bandwidth.
|
|
81
|
+
- `component(k)` / `component_solver(k)` give direct access to the k-th
|
|
82
|
+
scalar basis / solver for ad-hoc per-component work.
|
|
83
|
+
|
|
84
|
+
### Other
|
|
85
|
+
|
|
86
|
+
- Synchronised `pyproject.toml`, `fastlsq.__version__`, and CHANGELOG (the
|
|
87
|
+
package source had drifted to `__version__ = "0.1.0"` against
|
|
88
|
+
`pyproject.toml = "0.1.4"`; both now read `"0.1.5"`).
|
|
28
89
|
|
|
29
90
|
## [0.2.0] - 2026-03-01
|
|
30
91
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: FastLSQ
|
|
3
|
-
Version: 0.1
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: One-shot PDE solving via Fourier features with exact analytical derivatives; rank-revealing solvers, learnable anisotropic bandwidth, and CPU/CUDA/MPS support
|
|
5
5
|
Author: Antonin Sulc
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/asulc/FastLSQ
|
|
@@ -14,12 +14,13 @@ Classifier: Development Status :: 4 - Beta
|
|
|
14
14
|
Classifier: Intended Audience :: Science/Research
|
|
15
15
|
Classifier: Operating System :: OS Independent
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
21
|
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
21
22
|
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
22
|
-
Requires-Python: >=3.
|
|
23
|
+
Requires-Python: >=3.9
|
|
23
24
|
Description-Content-Type: text/markdown
|
|
24
25
|
License-File: LICENSE
|
|
25
26
|
Requires-Dist: torch>=2.0
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
CHANGELOG.md
|
|
2
|
+
LICENSE
|
|
3
|
+
MANIFEST.in
|
|
4
|
+
README.md
|
|
5
|
+
pyproject.toml
|
|
6
|
+
requirements.txt
|
|
7
|
+
FastLSQ.egg-info/PKG-INFO
|
|
8
|
+
FastLSQ.egg-info/SOURCES.txt
|
|
9
|
+
FastLSQ.egg-info/dependency_links.txt
|
|
10
|
+
FastLSQ.egg-info/requires.txt
|
|
11
|
+
FastLSQ.egg-info/top_level.txt
|
|
12
|
+
examples/add_your_own_pde.py
|
|
13
|
+
examples/benchmark_comparison.py
|
|
14
|
+
examples/custom_features.py
|
|
15
|
+
examples/grad_shafranov.py
|
|
16
|
+
examples/grid_inverse.py
|
|
17
|
+
examples/grid_rl_control.py
|
|
18
|
+
examples/grid_swing.py
|
|
19
|
+
examples/gs_inverse.py
|
|
20
|
+
examples/gs_rl_control.py
|
|
21
|
+
examples/inverse_heat_source.py
|
|
22
|
+
examples/inverse_magnetostatics.py
|
|
23
|
+
examples/inverse_source_position.py
|
|
24
|
+
examples/learnable_helmholtz.py
|
|
25
|
+
examples/orbit_hill.py
|
|
26
|
+
examples/orbit_inverse.py
|
|
27
|
+
examples/orbit_rl.py
|
|
28
|
+
examples/pde_discovery.py
|
|
29
|
+
examples/run_all_extensions.py
|
|
30
|
+
examples/run_linear.py
|
|
31
|
+
examples/run_nonlinear.py
|
|
32
|
+
examples/tutorial_basic.py
|
|
33
|
+
examples/tutorial_nonlinear.py
|
|
34
|
+
examples/vector_basis_stream_vorticity.py
|
|
35
|
+
examples/extras/fred_sde.py
|
|
36
|
+
examples/extras/fred_sde_fastlsq.py
|
|
37
|
+
examples/extras/gaia_potential.py
|
|
38
|
+
examples/extras/gaia_potential_fastlsq.py
|
|
39
|
+
examples/extras/horizons_ephemeris.py
|
|
40
|
+
examples/extras/numerai_alpha.py
|
|
41
|
+
examples/extras/numerai_alpha_fastlsq.py
|
|
42
|
+
examples/extras/run_all_fastlsq.py
|
|
43
|
+
examples/extras/spectral_expansion.py
|
|
44
|
+
examples/extras/scenarios/__init__.py
|
|
45
|
+
examples/extras/scenarios/_alsu_lattice.py
|
|
46
|
+
examples/extras/scenarios/_common.py
|
|
47
|
+
examples/extras/scenarios/run_all.py
|
|
48
|
+
examples/extras/scenarios/s01_beamloss_ode.py
|
|
49
|
+
examples/extras/scenarios/s01_betatron_tune.py
|
|
50
|
+
examples/extras/scenarios/s01_green_fff.py
|
|
51
|
+
examples/extras/scenarios/s01_hill_ivp.py
|
|
52
|
+
examples/extras/scenarios/s01_observe_fit_act_simulator.py
|
|
53
|
+
examples/extras/scenarios/s01_orbit_inverse.py
|
|
54
|
+
examples/extras/scenarios/s01_passive_loco.py
|
|
55
|
+
examples/extras/scenarios/s01_perturbed_hill.py
|
|
56
|
+
examples/extras/scenarios/s01_sofb_observe_fit_act.py
|
|
57
|
+
examples/extras/scenarios/s01_streaming_archive_growth.py
|
|
58
|
+
examples/extras/scenarios/s01_synchrotron_ode.py
|
|
59
|
+
examples/extras/scenarios/s01_tides_3months.py
|
|
60
|
+
examples/extras/scenarios/s01_topoff_impulse.py
|
|
61
|
+
examples/extras/scenarios/s01_visualize.py
|
|
62
|
+
examples/extras/scenarios/s02_plasma_wakefield.py
|
|
63
|
+
examples/extras/scenarios/s03_synchrobetatron.py
|
|
64
|
+
examples/extras/scenarios/s04_sunspots.py
|
|
65
|
+
examples/extras/scenarios/s05_helioseismology.py
|
|
66
|
+
examples/extras/scenarios/s06_tides.py
|
|
67
|
+
examples/extras/scenarios/s07_iers_earth_rotation.py
|
|
68
|
+
examples/extras/scenarios/s08_mauna_loa_co2.py
|
|
69
|
+
examples/extras/scenarios/s09_enso_qbo.py
|
|
70
|
+
examples/extras/scenarios/s10_pulsar_timing.py
|
|
71
|
+
examples/extras/scenarios/s11_modal_analysis.py
|
|
72
|
+
examples/extras/scenarios/s12_mems_resonator.py
|
|
73
|
+
examples/extras/scenarios/s13_variable_stars_kepler.py
|
|
74
|
+
examples/extras/scenarios/s14_eeg.py
|
|
75
|
+
examples/extras/scenarios/s15_circadian.py
|
|
76
|
+
fastlsq/__init__.py
|
|
77
|
+
fastlsq/api.py
|
|
78
|
+
fastlsq/basis.py
|
|
79
|
+
fastlsq/block.py
|
|
80
|
+
fastlsq/device.py
|
|
81
|
+
fastlsq/diagnostics.py
|
|
82
|
+
fastlsq/export.py
|
|
83
|
+
fastlsq/geometry.py
|
|
84
|
+
fastlsq/learnable.py
|
|
85
|
+
fastlsq/lightning.py
|
|
86
|
+
fastlsq/linalg.py
|
|
87
|
+
fastlsq/newton.py
|
|
88
|
+
fastlsq/plotting.py
|
|
89
|
+
fastlsq/solvers.py
|
|
90
|
+
fastlsq/tuning.py
|
|
91
|
+
fastlsq/utils.py
|
|
92
|
+
fastlsq/vector.py
|
|
93
|
+
fastlsq/viz.py
|
|
94
|
+
fastlsq/problems/__init__.py
|
|
95
|
+
fastlsq/problems/linear.py
|
|
96
|
+
fastlsq/problems/nonlinear.py
|
|
97
|
+
fastlsq/problems/regression.py
|
|
98
|
+
misc/fastlsq_teaser.png
|
|
99
|
+
misc/ideal_quadrupole.png
|
|
100
|
+
misc/inverse_heat_source.gif
|
|
101
|
+
misc/inverse_heat_source.png
|
|
102
|
+
misc/inverse_magnetostatics.png
|
|
103
|
+
misc/inverse_magnetostatics_convergence.png
|
|
104
|
+
misc/quadrupole_convergence.png
|
|
105
|
+
misc/quadrupole_optimization.png
|
|
106
|
+
misc/tutorial_nlpoisson_convergence.png
|
|
107
|
+
misc/tutorial_nlpoisson_solution.png
|
|
108
|
+
tests/test_basic.py
|
|
109
|
+
tests/test_block.py
|
|
110
|
+
tests/test_derivatives.py
|
|
111
|
+
tests/test_device.py
|
|
112
|
+
tests/test_grad_shafranov.py
|
|
113
|
+
tests/test_grid_swing.py
|
|
114
|
+
tests/test_learnable.py
|
|
115
|
+
tests/test_orbit_hill.py
|
|
116
|
+
tests/test_vector_basis.py
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: FastLSQ
|
|
3
|
-
Version: 0.1
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: One-shot PDE solving via Fourier features with exact analytical derivatives; rank-revealing solvers, learnable anisotropic bandwidth, and CPU/CUDA/MPS support
|
|
5
5
|
Author: Antonin Sulc
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/asulc/FastLSQ
|
|
@@ -14,12 +14,13 @@ Classifier: Development Status :: 4 - Beta
|
|
|
14
14
|
Classifier: Intended Audience :: Science/Research
|
|
15
15
|
Classifier: Operating System :: OS Independent
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
21
|
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
21
22
|
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
22
|
-
Requires-Python: >=3.
|
|
23
|
+
Requires-Python: >=3.9
|
|
23
24
|
Description-Content-Type: text/markdown
|
|
24
25
|
License-File: LICENSE
|
|
25
26
|
Requires-Dist: torch>=2.0
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Discover the drift and diffusion of an interest-rate SDE from data.
|
|
3
|
+
|
|
4
|
+
A short-rate model has the generic form
|
|
5
|
+
dr = mu(r,t) dt + sigma(r,t) dW.
|
|
6
|
+
The four classical templates:
|
|
7
|
+
Vasicek: mu = kappa(theta - r), sigma = sigma_0
|
|
8
|
+
CIR: mu = kappa(theta - r), sigma = sigma_0 sqrt(r)
|
|
9
|
+
CKLS: mu = kappa(theta - r), sigma = sigma_0 r^gamma
|
|
10
|
+
Hull-White: mu = (theta(t) - a r), sigma = sigma_0
|
|
11
|
+
For a fixed time-step approximation we have, increment by increment,
|
|
12
|
+
dr_i = mu(r_i) dt + sigma(r_i) sqrt(dt) z_i, z_i ~ N(0,1).
|
|
13
|
+
So given a series r_1,...,r_N we can estimate the conditional mean and
|
|
14
|
+
conditional variance of dr_i given r_i by binning, and fit a symbolic
|
|
15
|
+
mu(r) and sigma^2(r) to the binned estimates.
|
|
16
|
+
|
|
17
|
+
We try to download the FRED DGS10 series (10-year Treasury constant
|
|
18
|
+
maturity); if not available offline we fall back to synthetic Vasicek
|
|
19
|
+
or CIR data.
|
|
20
|
+
|
|
21
|
+
Usage: python fred_sde.py
|
|
22
|
+
"""
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import time
|
|
26
|
+
import io
|
|
27
|
+
import urllib.request
|
|
28
|
+
import numpy as np
|
|
29
|
+
|
|
30
|
+
np.set_printoptions(precision=4, suppress=True)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# ----------------------------------------------------------------------
|
|
34
|
+
# Data fetching (or synthetic fallback).
|
|
35
|
+
# ----------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
def fetch_fred_dgs10(timeout=10):
|
|
38
|
+
"""Try to download FRED DGS10 (10-year CMT) daily yields.
|
|
39
|
+
Returns (dates, values_in_decimal) or None if no internet."""
|
|
40
|
+
url = "https://fred.stlouisfed.org/graph/fredgraph.csv?id=DGS10"
|
|
41
|
+
try:
|
|
42
|
+
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
|
|
43
|
+
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
|
44
|
+
raw = resp.read().decode("utf-8")
|
|
45
|
+
except Exception as e:
|
|
46
|
+
print(f" FRED fetch failed ({type(e).__name__}: {e})", flush=True)
|
|
47
|
+
return None
|
|
48
|
+
# parse: DATE,DGS10 columns; skip header
|
|
49
|
+
dates, vals = [], []
|
|
50
|
+
for line in raw.splitlines()[1:]:
|
|
51
|
+
try:
|
|
52
|
+
d, v = line.split(",")
|
|
53
|
+
f = float(v)
|
|
54
|
+
dates.append(d); vals.append(f / 100.0) # percent → decimal
|
|
55
|
+
except (ValueError, IndexError):
|
|
56
|
+
continue
|
|
57
|
+
if len(vals) < 500:
|
|
58
|
+
return None
|
|
59
|
+
return np.array(dates), np.array(vals)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def synth_vasicek(n=3000, kappa=0.10, theta=0.045, sigma0=0.015, r0=0.04,
|
|
63
|
+
dt=1/252, seed=0):
|
|
64
|
+
rng = np.random.default_rng(seed)
|
|
65
|
+
r = np.empty(n); r[0] = r0
|
|
66
|
+
for i in range(1, n):
|
|
67
|
+
eps = rng.standard_normal()
|
|
68
|
+
r[i] = r[i-1] + kappa*(theta - r[i-1])*dt + sigma0*np.sqrt(dt)*eps
|
|
69
|
+
return r
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def synth_cir(n=3000, kappa=0.08, theta=0.05, sigma0=0.07, r0=0.04,
|
|
73
|
+
dt=1/252, seed=0):
|
|
74
|
+
rng = np.random.default_rng(seed)
|
|
75
|
+
r = np.empty(n); r[0] = r0
|
|
76
|
+
for i in range(1, n):
|
|
77
|
+
eps = rng.standard_normal()
|
|
78
|
+
diff = sigma0 * np.sqrt(max(r[i-1], 1e-8)) * np.sqrt(dt) * eps
|
|
79
|
+
r[i] = max(r[i-1] + kappa*(theta - r[i-1])*dt + diff, 1e-8)
|
|
80
|
+
return r
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# ----------------------------------------------------------------------
|
|
84
|
+
# Conditional moment estimation: bin r and compute E[dr|r], Var[dr|r].
|
|
85
|
+
# ----------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
def conditional_moments(r, n_bins=20, dt=1/252):
|
|
88
|
+
"""Return (r_bin_centres, mu_hat(r_bin), sigma2_hat(r_bin)) per bin."""
|
|
89
|
+
dr = np.diff(r)
|
|
90
|
+
r_at_step = r[:-1]
|
|
91
|
+
bins = np.quantile(r_at_step, np.linspace(0, 1, n_bins + 1))
|
|
92
|
+
bins[0] -= 1e-9; bins[-1] += 1e-9
|
|
93
|
+
idx = np.digitize(r_at_step, bins) - 1
|
|
94
|
+
r_c, m_c, v_c, n_c = [], [], [], []
|
|
95
|
+
for k in range(n_bins):
|
|
96
|
+
mask = (idx == k)
|
|
97
|
+
if mask.sum() < 5:
|
|
98
|
+
continue
|
|
99
|
+
r_c.append(r_at_step[mask].mean())
|
|
100
|
+
m_c.append(dr[mask].mean() / dt) # ≈ mu(r)
|
|
101
|
+
v_c.append(dr[mask].var() / dt) # ≈ sigma^2(r)
|
|
102
|
+
n_c.append(mask.sum())
|
|
103
|
+
return (np.asarray(r_c), np.asarray(m_c),
|
|
104
|
+
np.asarray(v_c), np.asarray(n_c))
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# ----------------------------------------------------------------------
|
|
108
|
+
# Symbolic candidates for mu(r) and sigma^2(r).
|
|
109
|
+
# ----------------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
def fit_drift(r_c, mu_hat, n_c):
|
|
112
|
+
"""Fit candidate drift forms; report best by AIC.
|
|
113
|
+
Candidates:
|
|
114
|
+
(Const) mu = a
|
|
115
|
+
(Linear-MR) mu = a + b*r (mean-reverting: b<0 means kappa>0)
|
|
116
|
+
(Quadratic) mu = a + b*r + c*r^2
|
|
117
|
+
"""
|
|
118
|
+
w = np.sqrt(n_c)
|
|
119
|
+
cands = []
|
|
120
|
+
for name, basis_fn in [
|
|
121
|
+
("Const", lambda r: np.stack([np.ones_like(r)], axis=1)),
|
|
122
|
+
("Linear-MR", lambda r: np.stack([np.ones_like(r), r], axis=1)),
|
|
123
|
+
("Quadratic", lambda r: np.stack([np.ones_like(r), r, r**2], axis=1)),
|
|
124
|
+
]:
|
|
125
|
+
X = basis_fn(r_c)
|
|
126
|
+
Xw = X * w[:, None]; yw = mu_hat * w
|
|
127
|
+
coef, *_ = np.linalg.lstsq(Xw, yw, rcond=None)
|
|
128
|
+
resid = (X @ coef - mu_hat) * w
|
|
129
|
+
rss = float(resid @ resid)
|
|
130
|
+
k = X.shape[1]
|
|
131
|
+
aic = len(r_c) * np.log(max(rss / max(len(r_c), 1), 1e-30)) + 2 * k
|
|
132
|
+
cands.append((name, coef, rss, aic))
|
|
133
|
+
cands.sort(key=lambda x: x[3])
|
|
134
|
+
return cands
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def fit_diffusion(r_c, sigma2_hat, n_c):
|
|
138
|
+
"""Fit candidate sigma^2(r) forms; report best by AIC.
|
|
139
|
+
(Const) sigma^2 = a
|
|
140
|
+
(Linear-r) sigma^2 = a * r ← CIR (after rescaling)
|
|
141
|
+
(Linear-r^2) sigma^2 = a * r^2 ← geometric BM
|
|
142
|
+
(Power) sigma^2 = a * r^(2*gamma) — fit gamma in log space
|
|
143
|
+
"""
|
|
144
|
+
w = np.sqrt(n_c)
|
|
145
|
+
cands = []
|
|
146
|
+
# Const, linear-r, linear-r^2 by weighted LSQ
|
|
147
|
+
for name, basis_fn in [
|
|
148
|
+
("Const", lambda r: np.stack([np.ones_like(r)], axis=1)),
|
|
149
|
+
("Linear-r", lambda r: np.stack([r], axis=1)),
|
|
150
|
+
("Linear-r^2", lambda r: np.stack([r**2], axis=1)),
|
|
151
|
+
]:
|
|
152
|
+
X = basis_fn(r_c)
|
|
153
|
+
Xw = X * w[:, None]; yw = sigma2_hat * w
|
|
154
|
+
coef, *_ = np.linalg.lstsq(Xw, yw, rcond=None)
|
|
155
|
+
resid = (X @ coef - sigma2_hat) * w
|
|
156
|
+
rss = float(resid @ resid)
|
|
157
|
+
k = X.shape[1]
|
|
158
|
+
aic = len(r_c) * np.log(max(rss / max(len(r_c), 1), 1e-30)) + 2 * k
|
|
159
|
+
cands.append((name, coef, rss, aic))
|
|
160
|
+
# Power: log(sigma^2) = log(a) + 2*gamma * log(r), needs r > 0
|
|
161
|
+
pos = r_c > 0
|
|
162
|
+
if pos.sum() > 3:
|
|
163
|
+
lx = np.log(r_c[pos]); ly = np.log(np.maximum(sigma2_hat[pos], 1e-20))
|
|
164
|
+
ww = w[pos]
|
|
165
|
+
X = np.stack([np.ones_like(lx), lx], axis=1)
|
|
166
|
+
Xw = X * ww[:, None]; yw = ly * ww
|
|
167
|
+
coef_p, *_ = np.linalg.lstsq(Xw, yw, rcond=None)
|
|
168
|
+
log_a, two_gamma = coef_p
|
|
169
|
+
# AIC in original space
|
|
170
|
+
pred = np.exp(log_a) * (r_c[pos] ** two_gamma)
|
|
171
|
+
resid = (pred - sigma2_hat[pos]) * w[pos]
|
|
172
|
+
rss = float(resid @ resid)
|
|
173
|
+
k = 2
|
|
174
|
+
aic = pos.sum() * np.log(max(rss / max(pos.sum(), 1), 1e-30)) + 2 * k
|
|
175
|
+
cands.append((f"Power(gamma={two_gamma/2:.2f})", coef_p, rss, aic))
|
|
176
|
+
cands.sort(key=lambda x: x[3])
|
|
177
|
+
return cands
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
# ----------------------------------------------------------------------
|
|
181
|
+
# Pretty-print one analysis
|
|
182
|
+
# ----------------------------------------------------------------------
|
|
183
|
+
|
|
184
|
+
def pretty_drift(name, coef):
|
|
185
|
+
if name == "Const":
|
|
186
|
+
return f"mu(r) = {coef[0]:+.4f}"
|
|
187
|
+
if name == "Linear-MR":
|
|
188
|
+
a, b = coef
|
|
189
|
+
# mean reversion: mu = a + b*r = b*(a/b + r), kappa = -b, theta = -a/b
|
|
190
|
+
kappa = -b
|
|
191
|
+
theta = -a / b if abs(b) > 1e-12 else float("nan")
|
|
192
|
+
return (f"mu(r) = {a:+.4f} + {b:+.4f} r "
|
|
193
|
+
f"(kappa={kappa:+.3f}, theta={theta:+.4f})")
|
|
194
|
+
if name == "Quadratic":
|
|
195
|
+
return (f"mu(r) = {coef[0]:+.4f} + {coef[1]:+.4f} r + {coef[2]:+.4f} r^2")
|
|
196
|
+
return f"{name}: coefs={coef}"
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def pretty_diffusion(name, coef):
|
|
200
|
+
if name == "Const":
|
|
201
|
+
return f"sigma^2(r) = {coef[0]:.4g} => sigma = {np.sqrt(max(coef[0],0)):.4f}"
|
|
202
|
+
if name == "Linear-r":
|
|
203
|
+
return f"sigma^2(r) = {coef[0]:.4g} * r (CIR-like)"
|
|
204
|
+
if name == "Linear-r^2":
|
|
205
|
+
return f"sigma^2(r) = {coef[0]:.4g} * r^2 (geom-BM-like)"
|
|
206
|
+
if name.startswith("Power"):
|
|
207
|
+
log_a, two_gamma = coef
|
|
208
|
+
return (f"sigma^2(r) = {np.exp(log_a):.4g} * r^{two_gamma:.3f} "
|
|
209
|
+
f"({name})")
|
|
210
|
+
return f"{name}: coefs={coef}"
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
# ----------------------------------------------------------------------
|
|
214
|
+
# Driver
|
|
215
|
+
# ----------------------------------------------------------------------
|
|
216
|
+
|
|
217
|
+
def analyse(label, r, dt=1/252):
|
|
218
|
+
print(f"\n=== {label} (N={len(r)} observations)")
|
|
219
|
+
r_c, mu_hat, sig2_hat, n_c = conditional_moments(r, n_bins=20, dt=dt)
|
|
220
|
+
print(f" r range: {r_c.min():.4f} .. {r_c.max():.4f}")
|
|
221
|
+
print(f" binned drifts: range {mu_hat.min():+.4f} .. {mu_hat.max():+.4f}")
|
|
222
|
+
print(f" binned vars: range {sig2_hat.min():.4g} .. {sig2_hat.max():.4g}")
|
|
223
|
+
drift_cands = fit_drift(r_c, mu_hat, n_c)
|
|
224
|
+
diff_cands = fit_diffusion(r_c, sig2_hat, n_c)
|
|
225
|
+
print(" Drift candidates (sorted by AIC):")
|
|
226
|
+
for nm, coef, rss, aic in drift_cands:
|
|
227
|
+
marker = "←" if nm == drift_cands[0][0] else " "
|
|
228
|
+
print(f" {marker} {nm:12s} AIC={aic:9.1f} {pretty_drift(nm, coef)}")
|
|
229
|
+
print(" Diffusion candidates (sorted by AIC):")
|
|
230
|
+
for nm, coef, rss, aic in diff_cands:
|
|
231
|
+
marker = "←" if nm == diff_cands[0][0] else " "
|
|
232
|
+
print(f" {marker} {nm:20s} AIC={aic:9.1f} {pretty_diffusion(nm, coef)}")
|
|
233
|
+
return drift_cands[0], diff_cands[0]
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def main():
|
|
237
|
+
print(">> Short-rate SDE discovery (FRED + synthetic checks)\n", flush=True)
|
|
238
|
+
|
|
239
|
+
# --- 1. Synthetic Vasicek
|
|
240
|
+
r_vas = synth_vasicek(n=3000, kappa=0.10, theta=0.045, sigma0=0.015, seed=0)
|
|
241
|
+
analyse("Synthetic Vasicek truth: drift Linear-MR (kappa=0.10, theta=0.045); "
|
|
242
|
+
"sigma^2 = const = 0.000225", r_vas)
|
|
243
|
+
|
|
244
|
+
# --- 2. Synthetic CIR
|
|
245
|
+
r_cir = synth_cir(n=3000, kappa=0.08, theta=0.05, sigma0=0.07, seed=0)
|
|
246
|
+
analyse("Synthetic CIR truth: drift Linear-MR (kappa=0.08, theta=0.050); "
|
|
247
|
+
"sigma^2 = 0.0049 * r", r_cir)
|
|
248
|
+
|
|
249
|
+
# --- 3. Real FRED data, if reachable
|
|
250
|
+
res = fetch_fred_dgs10()
|
|
251
|
+
if res is None:
|
|
252
|
+
print("\n=== FRED DGS10 unavailable (offline) --- skipping real-data run.")
|
|
253
|
+
else:
|
|
254
|
+
dates, r_real = res
|
|
255
|
+
# drop NaN-ish entries (FRED uses '.' for missing)
|
|
256
|
+
r_real = r_real[~np.isnan(r_real)]
|
|
257
|
+
analyse(f"FRED DGS10 (10-yr Treasury daily yields, {len(r_real)} obs)",
|
|
258
|
+
r_real)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
if __name__ == "__main__":
|
|
262
|
+
main()
|