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.
- SFI/__init__.py +64 -0
- SFI/bases/__init__.py +85 -0
- SFI/bases/constants.py +492 -0
- SFI/bases/linear.py +325 -0
- SFI/bases/monomials.py +218 -0
- SFI/bases/pairs.py +998 -0
- SFI/bases/spde.py +1537 -0
- SFI/diagnostics/__init__.py +60 -0
- SFI/diagnostics/assess.py +87 -0
- SFI/diagnostics/dynamics_order.py +621 -0
- SFI/diagnostics/plotting.py +226 -0
- SFI/diagnostics/report.py +238 -0
- SFI/diagnostics/residual_tests.py +395 -0
- SFI/diagnostics/residuals.py +688 -0
- SFI/inference/__init__.py +58 -0
- SFI/inference/base.py +1460 -0
- SFI/inference/optimizers.py +200 -0
- SFI/inference/overdamped.py +1214 -0
- SFI/inference/parametric_core/__init__.py +34 -0
- SFI/inference/parametric_core/covariance.py +232 -0
- SFI/inference/parametric_core/driver.py +272 -0
- SFI/inference/parametric_core/flow.py +149 -0
- SFI/inference/parametric_core/flow_multi.py +362 -0
- SFI/inference/parametric_core/flow_ud.py +168 -0
- SFI/inference/parametric_core/jacobians.py +540 -0
- SFI/inference/parametric_core/objective.py +286 -0
- SFI/inference/parametric_core/objective_ud.py +253 -0
- SFI/inference/parametric_core/precision.py +229 -0
- SFI/inference/parametric_core/solve.py +763 -0
- SFI/inference/result.py +362 -0
- SFI/inference/serialization.py +245 -0
- SFI/inference/sparse/__init__.py +67 -0
- SFI/inference/sparse/base.py +43 -0
- SFI/inference/sparse/beam.py +303 -0
- SFI/inference/sparse/greedy.py +151 -0
- SFI/inference/sparse/hillclimb.py +307 -0
- SFI/inference/sparse/lasso.py +178 -0
- SFI/inference/sparse/metrics.py +78 -0
- SFI/inference/sparse/result.py +278 -0
- SFI/inference/sparse/scorer.py +323 -0
- SFI/inference/sparse/stlsq.py +165 -0
- SFI/inference/sparsity.py +40 -0
- SFI/inference/underdamped.py +1355 -0
- SFI/integrate/__init__.py +38 -0
- SFI/integrate/api.py +920 -0
- SFI/integrate/integrand.py +402 -0
- SFI/integrate/rk4.py +156 -0
- SFI/integrate/timeops.py +174 -0
- SFI/langevin/__init__.py +29 -0
- SFI/langevin/base.py +863 -0
- SFI/langevin/chunked.py +225 -0
- SFI/langevin/noise.py +446 -0
- SFI/langevin/overdamped.py +560 -0
- SFI/langevin/underdamped.py +448 -0
- SFI/statefunc/__init__.py +49 -0
- SFI/statefunc/basis.py +87 -0
- SFI/statefunc/core/runtime.py +47 -0
- SFI/statefunc/factory.py +346 -0
- SFI/statefunc/interactor.py +90 -0
- SFI/statefunc/layout/__init__.py +29 -0
- SFI/statefunc/layout/_base.py +186 -0
- SFI/statefunc/layout/_eval_compiler.py +453 -0
- SFI/statefunc/layout/_fd_atoms.py +196 -0
- SFI/statefunc/layout/_grid.py +878 -0
- SFI/statefunc/layout/_sectors.py +166 -0
- SFI/statefunc/memhint.py +222 -0
- SFI/statefunc/nodes/__init__.py +56 -0
- SFI/statefunc/nodes/base.py +318 -0
- SFI/statefunc/nodes/contract.py +422 -0
- SFI/statefunc/nodes/interactions/__init__.py +23 -0
- SFI/statefunc/nodes/interactions/dispatcher.py +1365 -0
- SFI/statefunc/nodes/interactions/prepare.py +161 -0
- SFI/statefunc/nodes/interactions/specs.py +362 -0
- SFI/statefunc/nodes/interactions/stencils.py +718 -0
- SFI/statefunc/nodes/leaf.py +530 -0
- SFI/statefunc/nodes/ops/__init__.py +27 -0
- SFI/statefunc/nodes/ops/concat.py +28 -0
- SFI/statefunc/nodes/ops/derivative.py +447 -0
- SFI/statefunc/nodes/ops/einsum.py +120 -0
- SFI/statefunc/nodes/ops/linear.py +153 -0
- SFI/statefunc/nodes/ops/mapn.py +138 -0
- SFI/statefunc/nodes/ops/reshape_rank.py +158 -0
- SFI/statefunc/nodes/ops/slice.py +83 -0
- SFI/statefunc/params.py +309 -0
- SFI/statefunc/psf.py +112 -0
- SFI/statefunc/sf.py +104 -0
- SFI/statefunc/stateexpr.py +1243 -0
- SFI/statefunc/structexpr.py +1003 -0
- SFI/trajectory/__init__.py +31 -0
- SFI/trajectory/collection.py +1122 -0
- SFI/trajectory/dataset.py +1164 -0
- SFI/trajectory/degrade.py +1108 -0
- SFI/trajectory/io.py +1027 -0
- SFI/trajectory/reserved_extras.py +129 -0
- SFI/utils/__init__.py +17 -0
- SFI/utils/formatting.py +308 -0
- SFI/utils/maths.py +185 -0
- SFI/utils/neighbors.py +162 -0
- SFI/utils/plotting.py +1936 -0
- stochasticforceinference-2.0.0.dist-info/METADATA +203 -0
- stochasticforceinference-2.0.0.dist-info/RECORD +104 -0
- stochasticforceinference-2.0.0.dist-info/WHEEL +5 -0
- stochasticforceinference-2.0.0.dist-info/licenses/LICENSE +21 -0
- stochasticforceinference-2.0.0.dist-info/top_level.txt +1 -0
SFI/integrate/timeops.py
ADDED
|
@@ -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)
|
SFI/langevin/__init__.py
ADDED
|
@@ -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
|
+
]
|