Skip to main content

differential_dataflow/
lattice.rs

1//! Partially ordered elements with a least upper bound.
2//!
3//! Lattices form the basis of differential dataflow's efficient execution in the presence of
4//! iterative sub-computations. All logical times in differential dataflow must implement the
5//! `Lattice` trait, and all reasoning in operators are done it terms of `Lattice` methods.
6
7use timely::order::PartialOrder;
8use timely::progress::{Antichain, frontier::AntichainRef};
9
10/// A bounded partially ordered type supporting joins and meets.
11pub trait Lattice : PartialOrder {
12
13    /// The smallest element greater than or equal to both arguments.
14    ///
15    /// # Examples
16    ///
17    /// ```
18    /// # use timely::PartialOrder;
19    /// # use timely::order::Product;
20    /// # use differential_dataflow::lattice::Lattice;
21    /// # fn main() {
22    ///
23    /// let time1 = Product::new(3, 7);
24    /// let time2 = Product::new(4, 6);
25    /// let join = time1.join(&time2);
26    ///
27    /// assert_eq!(join, Product::new(4, 7));
28    /// # }
29    /// ```
30    #[must_use]
31    fn join(&self, other: &Self) -> Self;
32
33    /// Updates `self` to the smallest element greater than or equal to both arguments.
34    ///
35    /// # Examples
36    ///
37    /// ```
38    /// # use timely::PartialOrder;
39    /// # use timely::order::Product;
40    /// # use differential_dataflow::lattice::Lattice;
41    /// # fn main() {
42    ///
43    /// let mut time1 = Product::new(3, 7);
44    /// let time2 = Product::new(4, 6);
45    /// time1.join_assign(&time2);
46    ///
47    /// assert_eq!(time1, Product::new(4, 7));
48    /// # }
49    /// ```
50    fn join_assign(&mut self, other: &Self) where Self: Sized {
51        *self = self.join(other);
52    }
53
54    /// The largest element less than or equal to both arguments.
55    ///
56    /// # Examples
57    ///
58    /// ```
59    /// # use timely::PartialOrder;
60    /// # use timely::order::Product;
61    /// # use differential_dataflow::lattice::Lattice;
62    /// # fn main() {
63    ///
64    /// let time1 = Product::new(3, 7);
65    /// let time2 = Product::new(4, 6);
66    /// let meet = time1.meet(&time2);
67    ///
68    /// assert_eq!(meet, Product::new(3, 6));
69    /// # }
70    /// ```
71    #[must_use]
72    fn meet(&self, other: &Self) -> Self;
73
74    /// Updates `self` to the largest element less than or equal to both arguments.
75    ///
76    /// # Examples
77    ///
78    /// ```
79    /// # use timely::PartialOrder;
80    /// # use timely::order::Product;
81    /// # use differential_dataflow::lattice::Lattice;
82    /// # fn main() {
83    ///
84    /// let mut time1 = Product::new(3, 7);
85    /// let time2 = Product::new(4, 6);
86    /// time1.meet_assign(&time2);
87    ///
88    /// assert_eq!(time1, Product::new(3, 6));
89    /// # }
90    /// ```
91    fn meet_assign(&mut self, other: &Self) where Self: Sized  {
92        *self = self.meet(other);
93    }
94
95    /// Advances self to the largest time indistinguishable under `frontier`.
96    ///
97    /// This method produces the "largest" lattice element with the property that for every
98    /// lattice element greater than some element of `frontier`, both the result and `self`
99    /// compare identically to the lattice element. The result is the "largest" element in
100    /// the sense that any other element with the same property (compares identically to times
101    /// greater or equal to `frontier`) must be less or equal to the result.
102    ///
103    /// Both properties are machine-checked in `formal/Differential/Compaction.lean`, as
104    /// `advance_le_iff` (soundness) and `advance_eq_iff` (canonicity).
105    ///
106    /// When provided an empty frontier `self` is not modified.
107    ///
108    /// # Examples
109    ///
110    /// ```
111    /// # use timely::PartialOrder;
112    /// # use timely::order::Product;
113    /// # use differential_dataflow::lattice::Lattice;
114    /// # fn main() {
115    ///
116    /// use timely::progress::frontier::{Antichain, AntichainRef};
117    ///
118    /// let time = Product::new(3, 7);
119    /// let mut advanced = Product::new(3, 7);
120    /// let frontier = Antichain::from(vec![Product::new(4, 8), Product::new(5, 3)]);
121    /// advanced.advance_by(frontier.borrow());
122    ///
123    /// // `time` and `advanced` are indistinguishable to elements >= an element of `frontier`
124    /// for i in 0 .. 10 {
125    ///     for j in 0 .. 10 {
126    ///         let test = Product::new(i, j);
127    ///         // for `test` in the future of `frontier` ..
128    ///         if frontier.less_equal(&test) {
129    ///             assert_eq!(time.less_equal(&test), advanced.less_equal(&test));
130    ///         }
131    ///     }
132    /// }
133    ///
134    /// assert_eq!(advanced, Product::new(4, 7));
135    /// # }
136    /// ```
137    #[inline]
138    fn advance_by(&mut self, frontier: AntichainRef<Self>) where Self: Sized {
139        match &*frontier {
140            [] => {}
141            [first] => self.join_assign(first),
142            [first, rest @ ..] => {
143                let mut result = self.join(first);
144                for f in rest { result.meet_assign(&self.join(f)); }
145                *self = result;
146            }
147        }
148    }
149}
150
151use timely::order::Product;
152
153impl<T1: Lattice, T2: Lattice> Lattice for Product<T1, T2> {
154    // Expose the coordinate joins' short/equal-length paths to timestamp callers.
155    #[inline(always)]
156    fn join(&self, other: &Product<T1, T2>) -> Product<T1, T2> {
157        Product {
158            outer: self.outer.join(&other.outer),
159            inner: self.inner.join(&other.inner),
160        }
161    }
162    #[inline]
163    fn join_assign(&mut self, other: &Self) {
164        self.outer.join_assign(&other.outer);
165        self.inner.join_assign(&other.inner);
166    }
167    #[inline]
168    fn meet(&self, other: &Product<T1, T2>) -> Product<T1, T2> {
169        Product {
170            outer: self.outer.meet(&other.outer),
171            inner: self.inner.meet(&other.inner),
172        }
173    }
174    #[inline]
175    fn meet_assign(&mut self, other: &Self) {
176        self.outer.meet_assign(&other.outer);
177        self.inner.meet_assign(&other.inner);
178    }
179}
180
181/// A type that has a unique maximum element.
182pub trait Maximum {
183    /// The unique maximal element of the set.
184    fn maximum() -> Self;
185}
186
187/// Implements `Maximum` for elements with a `MAX` associated constant.
188macro_rules! implement_maximum {
189    ($($index_type:ty,)*) => (
190        $(
191            impl Maximum for $index_type {
192                fn maximum() -> Self { Self::MAX }
193            }
194        )*
195    )
196}
197
198implement_maximum!(usize, u128, u64, u32, u16, u8, isize, i128, i64, i32, i16, i8, Duration,);
199impl Maximum for () { fn maximum() -> () { () }}
200
201use timely::progress::Timestamp;
202
203// Tuples have the annoyance that they are only a lattice for `T2` with maximal elements,
204// as the `meet` operator on `(x, _)` and `(y, _)` would be `(x meet y, maximum())`.
205impl<T1: Lattice+Clone, T2: Lattice+Clone+Maximum+Timestamp> Lattice for (T1, T2) {
206    #[inline]
207    fn join(&self, other: &(T1, T2)) -> (T1, T2) {
208        if self.0.eq(&other.0) {
209            (self.0.clone(), self.1.join(&other.1))
210        } else if self.0.less_than(&other.0) {
211            other.clone()
212        } else if other.0.less_than(&self.0) {
213            self.clone()
214        } else {
215            (self.0.join(&other.0), T2::minimum())
216        }
217    }
218    #[inline]
219    fn meet(&self, other: &(T1, T2)) -> (T1, T2) {
220        if self.0.eq(&other.0) {
221            (self.0.clone(), self.1.meet(&other.1))
222        } else if self.0.less_than(&other.0) {
223            self.clone()
224        } else if other.0.less_than(&self.0) {
225            other.clone()
226        } else {
227            (self.0.meet(&other.0), T2::maximum())
228        }
229    }
230}
231
232macro_rules! implement_lattice {
233    ($index_type:ty, $minimum:expr) => (
234        impl Lattice for $index_type {
235            #[inline] fn join(&self, other: &Self) -> Self { ::std::cmp::max(*self, *other) }
236            #[inline] fn meet(&self, other: &Self) -> Self { ::std::cmp::min(*self, *other) }
237        }
238    )
239}
240
241use std::time::Duration;
242
243implement_lattice!(Duration, Duration::new(0, 0));
244implement_lattice!(usize, 0);
245implement_lattice!(u128, 0);
246implement_lattice!(u64, 0);
247implement_lattice!(u32, 0);
248implement_lattice!(u16, 0);
249implement_lattice!(u8, 0);
250implement_lattice!(isize, 0);
251implement_lattice!(i128, 0);
252implement_lattice!(i64, 0);
253implement_lattice!(i32, 0);
254implement_lattice!(i16, 0);
255implement_lattice!(i8, 0);
256implement_lattice!((), ());
257
258/// Returns the "smallest" minimal antichain "greater or equal" to both inputs.
259///
260/// This method is primarily meant for cases where one cannot use the methods
261/// of `Antichain`'s `PartialOrder` implementation, such as when one has only
262/// references rather than owned antichains.
263///
264/// # Examples
265///
266/// ```
267/// # use timely::PartialOrder;
268/// # use timely::order::Product;
269/// # use differential_dataflow::lattice::Lattice;
270/// # use differential_dataflow::lattice::antichain_join;
271/// # fn main() {
272///
273/// let f1 = &[Product::new(3, 7), Product::new(5, 6)];
274/// let f2 = &[Product::new(4, 6)];
275/// let join = antichain_join(f1, f2);
276/// assert_eq!(&*join.elements(), &[Product::new(4, 7), Product::new(5, 6)]);
277/// # }
278/// ```
279pub fn antichain_join<T: Lattice>(one: &[T], other: &[T]) -> Antichain<T> {
280    let mut upper = Antichain::new();
281    antichain_join_into(one, other, &mut upper);
282    upper
283}
284
285/// Returns the "smallest" minimal antichain "greater or equal" to both inputs.
286///
287/// This method is primarily meant for cases where one cannot use the methods
288/// of `Antichain`'s `PartialOrder` implementation, such as when one has only
289/// references rather than owned antichains.
290///
291/// This function is similar to [antichain_join] but reuses an existing allocation.
292/// The provided antichain is cleared before inserting elements.
293///
294/// # Examples
295///
296/// ```
297/// # use timely::PartialOrder;
298/// # use timely::order::Product;
299/// # use timely::progress::Antichain;
300/// # use differential_dataflow::lattice::Lattice;
301/// # use differential_dataflow::lattice::antichain_join_into;
302/// # fn main() {
303///
304/// let mut join = Antichain::new();
305/// let f1 = &[Product::new(3, 7), Product::new(5, 6)];
306/// let f2 = &[Product::new(4, 6)];
307/// antichain_join_into(f1, f2, &mut join);
308/// assert_eq!(&*join.elements(), &[Product::new(4, 7), Product::new(5, 6)]);
309/// # }
310/// ```
311pub fn antichain_join_into<T: Lattice>(one: &[T], other: &[T], upper: &mut Antichain<T>) {
312    upper.clear();
313    for time1 in one {
314        for time2 in other {
315            upper.insert(time1.join(time2));
316        }
317    }
318}
319
320/// Returns the "greatest" minimal antichain "less or equal" to both inputs.
321///
322/// This method is primarily meant for cases where one cannot use the methods
323/// of `Antichain`'s `PartialOrder` implementation, such as when one has only
324/// references rather than owned antichains.
325///
326/// # Examples
327///
328/// ```
329/// # use timely::PartialOrder;
330/// # use timely::order::Product;
331/// # use differential_dataflow::lattice::Lattice;
332/// # use differential_dataflow::lattice::antichain_meet;
333/// # fn main() {
334///
335/// let f1 = &[Product::new(3, 7), Product::new(5, 6)];
336/// let f2 = &[Product::new(4, 6)];
337/// let meet = antichain_meet(f1, f2);
338/// assert_eq!(&*meet.elements(), &[Product::new(3, 7), Product::new(4, 6)]);
339/// # }
340/// ```
341pub fn antichain_meet<T: Lattice+Clone>(one: &[T], other: &[T]) -> Antichain<T> {
342    let mut upper = Antichain::new();
343    for time1 in one {
344        upper.insert(time1.clone());
345    }
346    for time2 in other {
347        upper.insert(time2.clone());
348    }
349    upper
350}
351
352impl<T: Lattice+Clone> Lattice for Antichain<T> {
353    fn join(&self, other: &Self) -> Self {
354        let mut upper = Antichain::new();
355        for time1 in self.elements().iter() {
356            for time2 in other.elements().iter() {
357                upper.insert(time1.join(time2));
358            }
359        }
360        upper
361    }
362    fn meet(&self, other: &Self) -> Self {
363        let mut upper = Antichain::new();
364        for time1 in self.elements().iter() {
365            upper.insert(time1.clone());
366        }
367        for time2 in other.elements().iter() {
368            upper.insert(time2.clone());
369        }
370        upper
371    }
372}