Skip to main content

differential_dataflow/operators/
mod.rs

1//! Specialize differential dataflow operators.
2//!
3//! Differential dataflow introduces a small number of specialized operators on collections. These
4//! operators have specialized implementations to make them work efficiently, and are in addition
5//! to several operations defined directly on the `Collection` type (e.g. `map` and `filter`).
6
7pub use self::iterate::Iterate;
8pub use self::count::CountTotal;
9pub use self::threshold::ThresholdTotal;
10
11pub mod arrange;
12pub mod common;
13pub mod int_proxy;
14pub mod reduce;
15pub mod iterate;
16pub mod join;
17pub mod count;
18pub mod threshold;
19
20use crate::lattice::Lattice;
21use crate::trace::Cursor;
22
23/// An accumulation of (value, time, diff) updates.
24pub struct EditList<V, T, D> {
25    values: Vec<(V, usize)>,
26    edits: Vec<(T, D)>,
27}
28
29impl<V: Copy, T: Ord + Lattice, D: crate::difference::Semigroup> EditList<V, T, D> {
30    /// Creates an empty list of edits.
31    #[inline]
32    fn new() -> Self {
33        EditList {
34            values: Vec::new(),
35            edits: Vec::new(),
36        }
37    }
38    /// Walks the cursor's vals at the current key into `self`, advancing times by `meet` if supplied.
39    ///
40    /// The cursor is assumed to be positioned at a key already; callers that need
41    /// to seek should use [`Cursor::populate_key`] (or [`ValueHistory::replay_key`])
42    /// instead. This split avoids a redundant seek in the merge-join inner loop,
43    /// where the cursor is positioned by the upstream merge step.
44    fn load<'a, C>(&mut self, cursor: &mut C, storage: &'a C::Storage, meet: Option<&T>)
45    where
46        C: Cursor<Val<'a> = V, Time = T, Diff = D>,
47    {
48        self.clear();
49        while let Some(val) = cursor.get_val(storage) {
50            cursor.map_times(storage, |time, diff| {
51                let mut t = C::owned_time(time);
52                if let Some(m) = meet { t.join_assign(m); }
53                self.push(t, C::owned_diff(diff));
54            });
55            self.seal(val);
56            cursor.step_val(storage);
57        }
58    }
59    /// Clears the list of edits.
60    #[inline]
61    pub fn clear(&mut self) {
62        self.values.clear();
63        self.edits.clear();
64    }
65    fn len(&self) -> usize { self.edits.len() }
66    /// Inserts a new edit for an as-yet undetermined value.
67    #[inline]
68    pub fn push(&mut self, time: T, diff: D) {
69        // TODO: Could attempt "insertion-sort" like behavior here, where we collapse if possible.
70        self.edits.push((time, diff));
71    }
72    /// Associates all edits pushed since the previous `seal_value` call with `value`.
73    #[inline]
74    pub fn seal(&mut self, value: V) {
75        let prev = self.values.last().map(|x| x.1).unwrap_or(0);
76        crate::consolidation::consolidate_from(&mut self.edits, prev);
77        if self.edits.len() > prev {
78            self.values.push((value, self.edits.len()));
79        }
80    }
81    fn map<F: FnMut(V, &T, &D)>(&self, mut logic: F) {
82        for index in 0 .. self.values.len() {
83            let lower = if index == 0 { 0 } else { self.values[index-1].1 };
84            let upper = self.values[index].1;
85            for edit in lower .. upper {
86                logic(self.values[index].0, &self.edits[edit].0, &self.edits[edit].1);
87            }
88        }
89    }
90}
91
92/// A loaded, time-ordered replay of one key's `(value, time, diff)` edits, with meet-advanced
93/// buffer collapse — the shared machinery under the cursor reduce, the `int_proxy` tactics, and
94/// the [`common`](crate::operators::common) helpers. Its local contract: after
95/// `advance_buffer_by(meet)` with the meet of the un-replayed times, the buffer is consolidated
96/// and replay cost stays linear in edits rather than quadratic.
97pub struct ValueHistory<V, T, D> {
98    edits: EditList<V, T, D>,
99    history: Vec<(T, T, usize, usize)>,     // (time, meet, value_index, edit_offset)
100    buffer: Vec<((V, T), D)>,               // where we accumulate / collapse updates.
101}
102
103impl<V: Copy + Ord, T: Ord + Clone + Lattice, D: crate::difference::Semigroup> ValueHistory<V, T, D> {
104    /// An empty history, to be `load`ed.
105    pub fn new() -> Self {
106        ValueHistory {
107            edits: EditList::new(),
108            history: Vec::new(),
109            buffer: Vec::new(),
110        }
111    }
112    /// Discards all loaded state (capacity retained).
113    pub fn clear(&mut self) {
114        self.edits.clear();
115        self.history.clear();
116        self.buffer.clear();
117    }
118
119    /// Loads and replays a specified key.
120    ///
121    /// If the key is absent, the replayed history will be empty.
122    fn replay_key<'a, 'history, C>(
123        &'history mut self,
124        cursor: &mut C,
125        storage: &'a C::Storage,
126        key: C::Key<'a>,
127        meet: Option<&T>,
128    ) -> HistoryReplay<'history, V, T, D>
129    where
130        C: Cursor<Val<'a> = V, Time = T, Diff = D>,
131    {
132        self.clear();
133        cursor.populate_key(storage, key, meet, &mut self.edits);
134        self.replay()
135    }
136
137    /// Wraps the already-built, sorted `history` for a fresh walk, WITHOUT rebuilding or re-sorting
138    /// it. Valid whenever `history` is intact — e.g. after a `replay_key` whose returned replay was
139    /// only read through [`HistoryReplay::times`] (which does not step it). Used by reduce's
140    /// `reference` tactic, whose determination reads times and whose application then walks values.
141    fn walk<'history>(&'history mut self) -> HistoryReplay<'history, V, T, D> {
142        self.buffer.clear();
143        HistoryReplay { replay: self }
144    }
145
146    /// A time-only, non-destructive walk over the already-built `history` (see [`TimeReplay`]). It
147    /// reads times and their precomputed meets and accumulates only *times* into the caller-supplied
148    /// `buffer`, leaving `history` intact for a later [`walk`](Self::walk) over values. The buffer is
149    /// owned by the caller so the standard value walk pays nothing for this reference-only capability.
150    fn replay_times<'history>(&'history self, buffer: &'history mut Vec<T>) -> TimeReplay<'history, T> {
151        buffer.clear();
152        TimeReplay { history: &self.history[..], buffer }
153    }
154
155    /// Organizes history based on current contents of edits (sort + suffix meets).
156    fn build(&mut self) {
157        self.buffer.clear();
158        self.history.clear();
159        for value_index in 0 .. self.edits.values.len() {
160            let lower = if value_index > 0 { self.edits.values[value_index-1].1 } else { 0 };
161            let upper = self.edits.values[value_index].1;
162            for edit_index in lower .. upper {
163                let time = self.edits.edits[edit_index].0.clone();
164                self.history.push((time.clone(), time, value_index, edit_index));
165            }
166        }
167
168        self.history.sort_by(|x,y| y.cmp(x));
169        self.history.iter_mut().reduce(|prev, cur| { cur.1.meet_assign(&prev.1); cur });
170    }
171
172    /// Organizes history based on current contents of edits, returning a fresh replay.
173    fn replay<'history>(&'history mut self) -> HistoryReplay<'history, V, T, D> {
174        self.build();
175        HistoryReplay { replay: self }
176    }
177
178    /// Loads `edits` from a plain iterator (grouped by consecutive value — the presentation
179    /// order), advancing each time by `advance_by` if supplied, then organizes. This is the
180    /// cursor-free ingestion path: the `int_proxy` tactics present `(value_id, time, diff)`
181    /// runs directly rather than through a `Cursor`, and share this machinery instead of
182    /// re-implementing it. Ungrouped input is still correct, only less compact.
183    pub fn load_iter(&mut self, edits: impl Iterator<Item = (V, T, D)>, advance_by: Option<&T>) {
184        self.edits.clear();
185        let mut cur: Option<V> = None;
186        for (v, mut time, diff) in edits {
187            if cur != Some(v) {
188                if let Some(pv) = cur { self.edits.seal(pv); }
189                cur = Some(v);
190            }
191            if let Some(m) = advance_by { time.join_assign(m); }
192            self.edits.push(time, diff);
193        }
194        if let Some(pv) = cur { self.edits.seal(pv); }
195        self.build();
196    }
197}
198
199impl<V: Copy + Ord, T: Ord + Clone + Lattice, D: Clone + crate::difference::Semigroup> ValueHistory<V, T, D> {
200    /// The next (least) un-replayed time.
201    pub fn time(&self) -> Option<&T> { self.history.last().map(|x| &x.0) }
202    /// The meet of all un-replayed times.
203    pub fn meet(&self) -> Option<&T> { self.history.last().map(|x| &x.1) }
204    /// The next un-replayed edit, as `(value, time, diff)`.
205    pub fn edit(&self) -> Option<(V, &T, &D)> {
206        self.history.last().map(|&(ref t, _, v, e)| (self.edits.values[v].0, t, &self.edits.edits[e].1))
207    }
208    /// The buffered (stepped-in, advanced, consolidated) edits.
209    pub fn buffer(&self) -> &[((V, T), D)] { &self.buffer[..] }
210    /// Move the next edit into the buffer.
211    pub fn step(&mut self) {
212        let (time, _, value_index, edit_offset) = self.history.pop().unwrap();
213        self.buffer.push(((self.edits.values[value_index].0, time), self.edits.edits[edit_offset].1.clone()));
214    }
215    /// Step edits while the next time equals `time`; true iff any did.
216    pub fn step_while_time_is(&mut self, time: &T) -> bool {
217        let mut found = false;
218        while self.time() == Some(time) { found = true; self.step(); }
219        found
220    }
221    /// Step edits while the next time is `<= time` in the TOTAL order (a superset of the
222    /// partially-ordered downset; readers filter the buffer by `less_equal` themselves).
223    pub fn step_through(&mut self, time: &T) {
224        while self.time().is_some_and(|t| t <= time) { self.step(); }
225    }
226    /// Advance buffered times by `meet` and consolidate — the collapse that keeps replay linear.
227    pub fn advance_buffer_by(&mut self, meet: &T) {
228        for element in self.buffer.iter_mut() { (element.0).1.join_assign(meet); }
229        crate::consolidation::consolidate(&mut self.buffer);
230    }
231    /// True when every edit has been replayed.
232    pub fn is_done(&self) -> bool { self.history.is_empty() }
233}
234
235struct HistoryReplay<'history, V, T, D> {
236    replay: &'history mut ValueHistory<V, T, D>,
237}
238
239// A `HistoryReplay` is a thin cursor-facing handle over a `ValueHistory`; the replay
240// machinery lives on `ValueHistory` itself (shared with the `int_proxy` tactics), and
241// these forward to it.
242impl<'history, V: Copy + Ord, T: Ord + Clone + Lattice, D: Clone + crate::difference::Semigroup> HistoryReplay<'history, V, T, D> {
243    fn time(&self) -> Option<&T> { self.replay.time() }
244    fn meet(&self) -> Option<&T> { self.replay.meet() }
245    fn edit(&self) -> Option<(V, &T, &D)> { self.replay.edit() }
246    fn buffer(&self) -> &[((V, T), D)] { self.replay.buffer() }
247    fn step(&mut self) { self.replay.step() }
248    fn step_while_time_is(&mut self, time: &T) -> bool { self.replay.step_while_time_is(time) }
249    fn advance_buffer_by(&mut self, meet: &T) { self.replay.advance_buffer_by(meet) }
250    fn is_done(&self) -> bool { self.replay.is_done() }
251}
252
253/// A time-only, non-destructive walk over an already-built [`ValueHistory`] history.
254///
255/// It mirrors the time-facing half of [`HistoryReplay`] — `time`/`meet`/`step`/`step_while_time_is`
256/// /`advance_buffer_by`/`buffer` — but it carries only *times*, and it walks a shrinking *view* of
257/// `history` rather than popping it, so the underlying history stays intact for a later value walk.
258/// `advance_buffer_by` compacts the accumulated times by joining with `meet` and deduplicating (the
259/// time-only analogue of consolidation), which is what keeps a join-closure over these times an
260/// antichain — and hence non-quadratic.
261struct TimeReplay<'history, T> {
262    history: &'history [(T, T, usize, usize)],   // shrinking view; `last()` is the least time
263    buffer: &'history mut Vec<T>,                 // accumulated (and compacted) times seen so far
264}
265
266impl<'history, T: Ord + Clone + Lattice> TimeReplay<'history, T> {
267    fn time(&self) -> Option<&T> { self.history.last().map(|entry| &entry.0) }
268    fn meet(&self) -> Option<&T> { self.history.last().map(|entry| &entry.1) }
269    fn step(&mut self) {
270        let last = self.history.len() - 1;
271        self.buffer.push(self.history[last].0.clone());
272        self.history = &self.history[..last];
273    }
274    fn step_while_time_is(&mut self, time: &T) -> bool {
275        let mut found = false;
276        while self.time() == Some(time) {
277            found = true;
278            self.step();
279        }
280        found
281    }
282    fn advance_buffer_by(&mut self, meet: &T) {
283        for time in self.buffer.iter_mut() { time.join_assign(meet); }
284        self.buffer.sort();
285        self.buffer.dedup();
286    }
287    fn buffer(&self) -> &[T] { &self.buffer[..] }
288}