StochasticForceInference 2.0.0__py3-none-any.whl

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 (104) hide show
  1. SFI/__init__.py +64 -0
  2. SFI/bases/__init__.py +85 -0
  3. SFI/bases/constants.py +492 -0
  4. SFI/bases/linear.py +325 -0
  5. SFI/bases/monomials.py +218 -0
  6. SFI/bases/pairs.py +998 -0
  7. SFI/bases/spde.py +1537 -0
  8. SFI/diagnostics/__init__.py +60 -0
  9. SFI/diagnostics/assess.py +87 -0
  10. SFI/diagnostics/dynamics_order.py +621 -0
  11. SFI/diagnostics/plotting.py +226 -0
  12. SFI/diagnostics/report.py +238 -0
  13. SFI/diagnostics/residual_tests.py +395 -0
  14. SFI/diagnostics/residuals.py +688 -0
  15. SFI/inference/__init__.py +58 -0
  16. SFI/inference/base.py +1460 -0
  17. SFI/inference/optimizers.py +200 -0
  18. SFI/inference/overdamped.py +1214 -0
  19. SFI/inference/parametric_core/__init__.py +34 -0
  20. SFI/inference/parametric_core/covariance.py +232 -0
  21. SFI/inference/parametric_core/driver.py +272 -0
  22. SFI/inference/parametric_core/flow.py +149 -0
  23. SFI/inference/parametric_core/flow_multi.py +362 -0
  24. SFI/inference/parametric_core/flow_ud.py +168 -0
  25. SFI/inference/parametric_core/jacobians.py +540 -0
  26. SFI/inference/parametric_core/objective.py +286 -0
  27. SFI/inference/parametric_core/objective_ud.py +253 -0
  28. SFI/inference/parametric_core/precision.py +229 -0
  29. SFI/inference/parametric_core/solve.py +763 -0
  30. SFI/inference/result.py +362 -0
  31. SFI/inference/serialization.py +245 -0
  32. SFI/inference/sparse/__init__.py +67 -0
  33. SFI/inference/sparse/base.py +43 -0
  34. SFI/inference/sparse/beam.py +303 -0
  35. SFI/inference/sparse/greedy.py +151 -0
  36. SFI/inference/sparse/hillclimb.py +307 -0
  37. SFI/inference/sparse/lasso.py +178 -0
  38. SFI/inference/sparse/metrics.py +78 -0
  39. SFI/inference/sparse/result.py +278 -0
  40. SFI/inference/sparse/scorer.py +323 -0
  41. SFI/inference/sparse/stlsq.py +165 -0
  42. SFI/inference/sparsity.py +40 -0
  43. SFI/inference/underdamped.py +1355 -0
  44. SFI/integrate/__init__.py +38 -0
  45. SFI/integrate/api.py +920 -0
  46. SFI/integrate/integrand.py +402 -0
  47. SFI/integrate/rk4.py +156 -0
  48. SFI/integrate/timeops.py +174 -0
  49. SFI/langevin/__init__.py +29 -0
  50. SFI/langevin/base.py +863 -0
  51. SFI/langevin/chunked.py +225 -0
  52. SFI/langevin/noise.py +446 -0
  53. SFI/langevin/overdamped.py +560 -0
  54. SFI/langevin/underdamped.py +448 -0
  55. SFI/statefunc/__init__.py +49 -0
  56. SFI/statefunc/basis.py +87 -0
  57. SFI/statefunc/core/runtime.py +47 -0
  58. SFI/statefunc/factory.py +346 -0
  59. SFI/statefunc/interactor.py +90 -0
  60. SFI/statefunc/layout/__init__.py +29 -0
  61. SFI/statefunc/layout/_base.py +186 -0
  62. SFI/statefunc/layout/_eval_compiler.py +453 -0
  63. SFI/statefunc/layout/_fd_atoms.py +196 -0
  64. SFI/statefunc/layout/_grid.py +878 -0
  65. SFI/statefunc/layout/_sectors.py +166 -0
  66. SFI/statefunc/memhint.py +222 -0
  67. SFI/statefunc/nodes/__init__.py +56 -0
  68. SFI/statefunc/nodes/base.py +318 -0
  69. SFI/statefunc/nodes/contract.py +422 -0
  70. SFI/statefunc/nodes/interactions/__init__.py +23 -0
  71. SFI/statefunc/nodes/interactions/dispatcher.py +1365 -0
  72. SFI/statefunc/nodes/interactions/prepare.py +161 -0
  73. SFI/statefunc/nodes/interactions/specs.py +362 -0
  74. SFI/statefunc/nodes/interactions/stencils.py +718 -0
  75. SFI/statefunc/nodes/leaf.py +530 -0
  76. SFI/statefunc/nodes/ops/__init__.py +27 -0
  77. SFI/statefunc/nodes/ops/concat.py +28 -0
  78. SFI/statefunc/nodes/ops/derivative.py +447 -0
  79. SFI/statefunc/nodes/ops/einsum.py +120 -0
  80. SFI/statefunc/nodes/ops/linear.py +153 -0
  81. SFI/statefunc/nodes/ops/mapn.py +138 -0
  82. SFI/statefunc/nodes/ops/reshape_rank.py +158 -0
  83. SFI/statefunc/nodes/ops/slice.py +83 -0
  84. SFI/statefunc/params.py +309 -0
  85. SFI/statefunc/psf.py +112 -0
  86. SFI/statefunc/sf.py +104 -0
  87. SFI/statefunc/stateexpr.py +1243 -0
  88. SFI/statefunc/structexpr.py +1003 -0
  89. SFI/trajectory/__init__.py +31 -0
  90. SFI/trajectory/collection.py +1122 -0
  91. SFI/trajectory/dataset.py +1164 -0
  92. SFI/trajectory/degrade.py +1108 -0
  93. SFI/trajectory/io.py +1027 -0
  94. SFI/trajectory/reserved_extras.py +129 -0
  95. SFI/utils/__init__.py +17 -0
  96. SFI/utils/formatting.py +308 -0
  97. SFI/utils/maths.py +185 -0
  98. SFI/utils/neighbors.py +162 -0
  99. SFI/utils/plotting.py +1936 -0
  100. stochasticforceinference-2.0.0.dist-info/METADATA +203 -0
  101. stochasticforceinference-2.0.0.dist-info/RECORD +104 -0
  102. stochasticforceinference-2.0.0.dist-info/WHEEL +5 -0
  103. stochasticforceinference-2.0.0.dist-info/licenses/LICENSE +21 -0
  104. stochasticforceinference-2.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,174 @@
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ from typing import Callable, FrozenSet, Iterable, Optional
5
+
6
+ import jax.numpy as jnp
7
+
8
+ __all__ = [
9
+ "TimeOp",
10
+ "timeop",
11
+ "stream",
12
+ "velocity",
13
+ "scale",
14
+ "add",
15
+ ]
16
+
17
+
18
+ class TimeOp:
19
+ """
20
+ A time-local operator evaluated on one time slice.
21
+
22
+ A TimeOp declares which streamed fields it needs either via its function
23
+ signature or the explicit *requires* parameter.
24
+
25
+ Example
26
+ -------
27
+ >>> @timeop
28
+ ... def vel(dX_minus, dt_minus):
29
+ ... return dX_minus / dt_minus[..., None]
30
+ >>> vel.requires
31
+ frozenset({'dX_minus', 'dt_minus'})
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ fn: Callable[..., jnp.ndarray],
37
+ name: str | None = None,
38
+ *,
39
+ batch_safe: bool = False,
40
+ requires: Optional[FrozenSet[str]] = None,
41
+ ):
42
+ self.fn = fn
43
+ self._name = name or fn.__name__
44
+ self.batch_safe = batch_safe
45
+ if requires is not None:
46
+ self._requires: FrozenSet[str] = frozenset(requires)
47
+ else:
48
+ sig = inspect.signature(fn)
49
+ self._requires = frozenset(
50
+ p.name
51
+ for p in sig.parameters.values()
52
+ if p.kind in (
53
+ inspect.Parameter.POSITIONAL_OR_KEYWORD,
54
+ inspect.Parameter.KEYWORD_ONLY,
55
+ )
56
+ )
57
+
58
+ def __call__(self, **streams) -> jnp.ndarray:
59
+ return self.fn(**{k: streams[k] for k in self._requires})
60
+
61
+ @property
62
+ def requires(self) -> FrozenSet[str]:
63
+ return self._requires
64
+
65
+ @property
66
+ def name(self) -> str:
67
+ return self._name
68
+
69
+
70
+ def timeop(
71
+ fn: Callable[..., jnp.ndarray] | None = None,
72
+ *,
73
+ name: str | None = None,
74
+ batch_safe: bool = False,
75
+ requires: Optional[FrozenSet[str]] = None,
76
+ ):
77
+ """
78
+ Decorator: convert a function into a TimeOp.
79
+
80
+ Parameters
81
+ ----------
82
+ batch_safe : bool
83
+ If True, the function already handles a leading batch (K) axis
84
+ in its inputs without requiring an additional ``jax.vmap``.
85
+ requires : frozenset, optional
86
+ Explicit set of required stream keys. When given, the function
87
+ signature is not inspected for parameter names. Use this when
88
+ the function accepts ``**streams`` and the required keys cannot
89
+ be inferred from the signature.
90
+
91
+ Notes
92
+ -----
93
+ Without *requires*, the function's parameter names define the required
94
+ stream keys (``**kwargs``/``*args`` parameters are ignored).
95
+ """
96
+
97
+ def _wrap(f: Callable[..., jnp.ndarray]) -> TimeOp:
98
+ return TimeOp(f, name=name, batch_safe=batch_safe, requires=requires)
99
+
100
+ return _wrap if fn is None else _wrap(fn)
101
+
102
+
103
+ # --------- Stock, composable timeops ---------
104
+
105
+
106
+ def stream(key: str, *, name: str | None = None) -> TimeOp:
107
+ """Return a TimeOp that passes stream *key* through unchanged."""
108
+
109
+ def _op(**streams: jnp.ndarray) -> jnp.ndarray:
110
+ return streams[key]
111
+
112
+ return TimeOp(_op, name=name or f"stream[{key}]", batch_safe=True, requires=frozenset({key}))
113
+
114
+
115
+ def velocity(dx_key: str, dt_key: str, *, name: str | None = None) -> TimeOp:
116
+ """Return a TimeOp that computes dx/dt, broadcasting dt over arbitrary leading dims."""
117
+
118
+ def _op(**streams: jnp.ndarray) -> jnp.ndarray:
119
+ dx = streams[dx_key]
120
+ dt = streams[dt_key]
121
+ # Expand dt to broadcast with dx for arbitrary leading batch dims.
122
+ # Single-row: dt scalar, dx (N, d) -> dt becomes (1, 1) -> OK.
123
+ # Batch: dt (K,), dx (K, N, d) -> dt becomes (K, 1, 1) -> OK.
124
+ while dt.ndim < dx.ndim:
125
+ dt = dt[..., jnp.newaxis]
126
+ return dx / dt
127
+
128
+ return TimeOp(
129
+ _op,
130
+ name=name or f"V[{dx_key}/{dt_key}]",
131
+ batch_safe=True,
132
+ requires=frozenset({dx_key, dt_key}),
133
+ )
134
+
135
+
136
+ def scale(op: TimeOp, alpha: float | int, *, name: str | None = None) -> TimeOp:
137
+ """Return a TimeOp that multiplies *op* by scalar *alpha*."""
138
+
139
+ def _op(**streams: jnp.ndarray) -> jnp.ndarray:
140
+ return alpha * op(**streams)
141
+
142
+ return TimeOp(
143
+ _op,
144
+ name=name or f"{alpha}*{op.name}",
145
+ batch_safe=op.batch_safe,
146
+ requires=op.requires,
147
+ )
148
+
149
+
150
+ def add(ops: Iterable[TimeOp], *, name: str | None = None) -> TimeOp:
151
+ """Return a TimeOp that sums the outputs of all *ops* element-wise.
152
+
153
+ Parameters
154
+ ----------
155
+ ops : iterable of TimeOp
156
+ At least one operand is required.
157
+ """
158
+ ops = tuple(ops)
159
+ if not ops:
160
+ raise ValueError(
161
+ "add() requires at least one TimeOp; got an empty sequence."
162
+ )
163
+ nm = name or " + ".join(o.name for o in ops)
164
+ req = frozenset().union(*(o.requires for o in ops))
165
+ all_safe = all(o.batch_safe for o in ops)
166
+
167
+ def _op(**streams: jnp.ndarray) -> jnp.ndarray:
168
+ out = None
169
+ for o in ops:
170
+ cur = o(**streams)
171
+ out = cur if out is None else out + cur
172
+ return out # type: ignore[return-value] # out is set because ops is non-empty
173
+
174
+ return TimeOp(_op, name=nm, batch_safe=all_safe, requires=req)
@@ -0,0 +1,29 @@
1
+ """
2
+ SFI.langevin
3
+ ============
4
+
5
+ Langevin simulators (overdamped, underdamped, …).
6
+
7
+ This submodule provides simulation classes built on top of the statefunc API.
8
+ They accept `PSF`/`SF` force and diffusion models, enforce shape contracts,
9
+ and integrate trajectories using stochastic schemes.
10
+
11
+ Public classes
12
+ --------------
13
+ - OverdampedProcess : overdamped Langevin simulator (Euler–Maruyama or
14
+ stochastic Heun) with entropy/information observables.
15
+ - UnderdampedProcess : underdamped Langevin simulator (velocity-Verlet).
16
+ """
17
+
18
+ from .noise import CompositeNoise, ConservedNoise, NoiseModel, WhiteNoise
19
+ from .overdamped import OverdampedProcess
20
+ from .underdamped import UnderdampedProcess
21
+
22
+ __all__ = [
23
+ "OverdampedProcess",
24
+ "UnderdampedProcess",
25
+ "NoiseModel",
26
+ "WhiteNoise",
27
+ "ConservedNoise",
28
+ "CompositeNoise",
29
+ ]