.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "gallery/velocity_dependent_noise_demo.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_gallery_velocity_dependent_noise_demo.py: Velocity-dependent noise — underdamped multiplicative diffusion ================================================================= Recover a **velocity-dependent diffusion field** :math:`D(v)` for an inertial particle, from positions only. Many active and driven systems fluctuate harder the faster they move — motility noise, flight-force fluctuations, turbulent drag. Underdamped SFI reconstructs the unobserved velocity and infers :math:`F(x,v)` and :math:`D(x,v)` jointly; here the noise amplitude doubles within the explored speed range and is recovered model-free. .. rubric:: Tags synthetic · underdamped · multiplicative-noise · diffusion-field · 1D .. GENERATED FROM PYTHON SOURCE LINES 17-19 .. code-block:: Python :dedent: 1 .. GENERATED FROM PYTHON SOURCE LINES 40-54 Model and simulation --------------------- A damped harmonic oscillator whose bath kicks grow with speed: .. math:: \dot x = v, \qquad dv = (-k x - \gamma v)\,dt + \sqrt{2 D(v)}\,dW, \qquad D(v) = D_0\left(1 + (v/v_c)^2\right), in the **Itô convention**: the force SFI infers is the Itô drift of the velocity equation. Both fields are composed from coordinate primitives — note the velocity primitive ``v`` entering the diffusion. .. GENERATED FROM PYTHON SOURCE LINES 54-82 .. code-block:: Python import SFI from SFI.bases import identity_matrix_basis, unit_axes, v_components, x_components from SFI.langevin import UnderdampedProcess k = 1.0 # stiffness gamma = 1.0 # friction D0 = 0.3 # diffusion at rest vc = 1.0 # velocity scale of the noise growth dt = 0.01 Nsteps = 80_000 (x,) = x_components(1) (v,) = v_components(1) (ex,) = unit_axes(1) I = identity_matrix_basis(1) F_model = (-k * x - gamma * v) * ex # Itô drift of dv D_model = D0 * (1.0 + (v / vc) ** 2) * I # D(v) — multiplicative in v proc = UnderdampedProcess(F=F_model, D=D_model, theta_F=jnp.ones(F_model.n_features), theta_D=jnp.ones(D_model.n_features)) proc.initialize(jnp.array([0.0]), v0=jnp.array([0.0])) coll = proc.simulate(dt=dt, Nsteps=Nsteps, key=random.PRNGKey(5), prerun=500, oversampling=20) print(f"Trajectory: {coll.T} frames, dt={dt} (positions only)") .. rst-class:: sphx-glr-script-out .. code-block:: none Trajectory: 80000 frames, dt=0.01 (positions only) .. GENERATED FROM PYTHON SOURCE LINES 83-89 Phase portrait --------------- Velocity is *not* observed — for plotting we reconstruct it by finite differences, exactly as the inference engine does internally. Fast excursions (top and bottom) are noticeably noisier than slow ones. .. GENERATED FROM PYTHON SOURCE LINES 89-90 .. code-block:: Python :dedent: 1 .. image-sg:: /gallery/images/sphx_glr_velocity_dependent_noise_demo_001.png :alt: Phase portrait — colored by local noise amplitude :srcset: /gallery/images/sphx_glr_velocity_dependent_noise_demo_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 112-121 Inference ---------- The underdamped parametric sequence: :meth:`infer_force` fits the Itô drift :math:`F(x,v)`, then :meth:`infer_diffusion` fits :math:`D(x,v)` on a polynomial basis in *both* variables (``include_v=True``). The basis spans :math:`\{1, x, v, x^2, xv, v^2\}` — the inference must discover that only :math:`1` and :math:`v^2` carry weight. .. GENERATED FROM PYTHON SOURCE LINES 121-136 .. code-block:: Python from SFI.bases import monomials_up_to B_force = monomials_up_to(order=1, dim=1, include_v=True, rank="vector") B_diff = monomials_up_to(order=2, dim=1, include_v=True, rank="symmetric_matrix") inf = SFI.UnderdampedLangevinInference(coll) inf.infer_force(B_force) inf.infer_diffusion(B_diff) inf.compare_to_exact(model_exact=proc) inf.print_report() print(inf.summary(field="diffusion")) print(f" (true: [1] = {D0:+.3f}, [v²] = {D0 / vc**2:+.3f}, rest 0)") .. rst-class:: sphx-glr-script-out .. code-block:: none --- StochasticForceInference Report --- Average diffusion tensor: [[0.41754755]] Measurement noise tensor: [[4.550176e-10]] Normalized MSE (force): 0.0051 Normalized MSE (diffusion): 0.0003 Force Coefficient Table ────────────────────────────────────── # Label Coefficient Sig ────────────────────────────────────── 0 b0 1.79458e-02 · 1 b1 -9.22756e-01 · 2 b2 -1.05463e+00 · ────────────────────────────────────── 3/3 basis functions in support Diffusion Coefficient Table ────────────────────────────────────── # Label Coefficient Sig ────────────────────────────────────── 0 b0 2.93479e-01 · 1 b1 -3.04928e-03 · 2 b2 -3.01497e-03 · 3 b3 3.06650e-03 · 4 b4 3.53166e-03 · 5 b5 3.07988e-01 · ────────────────────────────────────── 6/6 basis functions in support (true: [1] = +0.300, [v²] = +0.300, rest 0) .. GENERATED FROM PYTHON SOURCE LINES 137-143 Recovered diffusion profile ----------------------------- Evaluating the inferred tensor along the velocity axis recovers the parabolic noise profile; a constant-:math:`D` analysis would report only the mean. .. GENERATED FROM PYTHON SOURCE LINES 143-156 .. code-block:: Python # The inferred tensor as a callable on phase-space points [x, v]; sweeping the # v axis (x held at 0) recovers the parabolic noise profile. def D_inferred(pts): pts = jnp.asarray(pts) return inf.diffusion_inferred(pts[:, :1], v=pts[:, 1:2]) # (N, 1, 1) def D_exact(pts): v = np.asarray(pts)[:, 1] return D0 * (1 + (v / vc) ** 2) .. image-sg:: /gallery/images/sphx_glr_velocity_dependent_noise_demo_002.png :alt: Velocity-dependent diffusion recovery :srcset: /gallery/images/sphx_glr_velocity_dependent_noise_demo_002.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 169-183 When is this hard? -------------------- Velocity-dependent noise has a genuinely hard regime. With :math:`D(v) \propto v^2` the velocity distribution develops **power-law tails** (tail exponent :math:`2 + \gamma/D_0`): if friction is weak relative to the noise gradient, rare high-speed bursts dominate the statistics, and no finite sampling rate resolves them — estimates of the noise floor :math:`D_0` then degrade no matter how small :math:`dt` is. Here :math:`\gamma/D_0 \approx 3.3` keeps the tails integrable and the inference quantitative. For strongly driven systems, prefer *saturating* noise models (e.g. :math:`D_0 + \Delta D\, v^2/(v^2 + v_s^2)`) — see the state-dependent diffusion benchmark for that case. .. GENERATED FROM PYTHON SOURCE LINES 185-189 Thumbnail --------- Dedicated single-panel figure for the gallery thumbnail. .. GENERATED FROM PYTHON SOURCE LINES 189-192 .. code-block:: Python stamp_output() .. image-sg:: /gallery/images/sphx_glr_velocity_dependent_noise_demo_003.png :alt: velocity dependent noise demo :srcset: /gallery/images/sphx_glr_velocity_dependent_noise_demo_003.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none [Generated: 2026-06-30 10:07] .. rst-class:: sphx-glr-timing **Total running time of the script:** (2 minutes 23.116 seconds) .. rst-class:: sphx-glr-example-tags 🏷 Tags: synthetic, underdamped, multiplicative-noise, diffusion-field, 1D .. _sphx_glr_download_gallery_velocity_dependent_noise_demo.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: velocity_dependent_noise_demo.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: velocity_dependent_noise_demo.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: velocity_dependent_noise_demo.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_