1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
//! Unified [`Compute`] trait — the single public entry point for any analysis.
use FrameAccess;
use crateComputeError;
use crateComputeResult;
/// Run an analysis over a sequence of frames and produce a finalized result.
///
/// A single frame is just a length-1 slice — single-frame, trajectory, and
/// dataset analyses are structurally identical. The `Args` GAT carries any
/// non-frame input the analysis needs (neighbor lists, upstream Compute
/// outputs, masses). Concrete `Compute` impls decide whether to iterate the
/// slice, take the first frame as a reference, or treat the whole sequence as
/// a matrix.
///
/// # Contract
///
/// - `&self` is an **immutable parameter bag** (bin count, cutoff, seed, …).
/// No hidden mutable state — two `compute` calls with identical `frames` +
/// `args` must produce identical [`Output`](Self::Output) values.
/// - `Output` is always [`'static`](::std::marker::Send) +
/// [`Send`] + [`Sync`] + [`Clone`]: it must be shareable across
/// downstream consumers.
/// - `Output: ComputeResult` — callers should invoke
/// [`finalize`](ComputeResult::finalize) after `compute` to obtain
/// the user-facing final form.
///
/// # Example (conceptual)
///
/// ```ignore
/// struct COM;
/// struct COMResult { /* ... */ }
/// impl ComputeResult for COMResult {}
///
/// impl Compute for COM {
/// type Args<'a> = &'a Vec<ClusterResult>;
/// type Output = Vec<COMResult>;
/// fn compute<'a, FA: FrameAccess + 'a>(
/// &self,
/// frames: &[&'a FA],
/// clusters: Self::Args<'a>,
/// ) -> Result<Self::Output, ComputeError> {
/// // one COMResult per frame, aligned by index
/// # unimplemented!()
/// }
/// }
/// ```
/// Fit / smooth / spectral-transform an **upstream compute result** into a
/// scalar, curve, or spectrum.
///
/// `Fit` is the companion of [`Compute`]: where a `Compute` consumes raw
/// frames and produces a raw observable (MSD curve, ACF, …), a `Fit` consumes
/// that observable — a single [`Array1<f64>`](ndarray::Array1) curve or a
/// metadata-carrying raw result — and produces a derived quantity (an OLS
/// slope, a running trapezoid integral, a plateau mean, a windowed FFT
/// spectrum). The split keeps "what the simulation measured" separate from
/// "how the analyst processes it": the same raw curve can feed many fits with
/// different windows / fit ranges without recomputing the observable.
///
/// # Contract
///
/// - `&self` is an **immutable parameter bag** (fit window, prefactor,
/// dimension `d`, …). Identical `input` + identical `&self` ⇒ identical
/// [`Output`](Self::Output); no hidden mutable state.
/// - `Input<'a>` is a [GAT](https://doc.rust-lang.org/reference/items/associated-items.html)
/// so a fit may borrow its upstream input (`&Array1<f64>`, a raw ACF, …)
/// without cloning.
/// - `Output: ComputeResult + Clone + Send + Sync + 'static` — same shareable
/// bound as [`Compute::Output`], so fit outputs slot into the same
/// downstream consumers.
/// - Reuses [`ComputeError`]; no new error enum. Degenerate fit windows use
/// [`ComputeError::OutOfRange`], shape mismatches use
/// [`ComputeError::DimensionMismatch`], empty/too-short input uses
/// [`ComputeError::EmptyInput`].
///
/// # Example (conceptual)
///
/// ```ignore
/// struct LinearFit { window: (f64, f64) }
/// struct LinearFitResult { /* slope, intercept, r2, … */ }
/// impl ComputeResult for LinearFitResult {}
///
/// impl Fit for LinearFit {
/// type Input<'a> = (&'a Array1<f64>, &'a Array1<f64>); // (x, y)
/// type Output = LinearFitResult;
/// fn fit<'a>(&self, input: Self::Input<'a>) -> Result<Self::Output, ComputeError> {
/// # unimplemented!()
/// }
/// }
/// ```