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 #[inline]
155 fn join(&self, other: &Product<T1, T2>) -> Product<T1, T2> {
156 Product {
157 outer: self.outer.join(&other.outer),
158 inner: self.inner.join(&other.inner),
159 }
160 }
161 #[inline]
162 fn join_assign(&mut self, other: &Self) {
163 self.outer.join_assign(&other.outer);
164 self.inner.join_assign(&other.inner);
165 }
166 #[inline]
167 fn meet(&self, other: &Product<T1, T2>) -> Product<T1, T2> {
168 Product {
169 outer: self.outer.meet(&other.outer),
170 inner: self.inner.meet(&other.inner),
171 }
172 }
173 #[inline]
174 fn meet_assign(&mut self, other: &Self) {
175 self.outer.meet_assign(&other.outer);
176 self.inner.meet_assign(&other.inner);
177 }
178}
179
180/// A type that has a unique maximum element.
181pub trait Maximum {
182 /// The unique maximal element of the set.
183 fn maximum() -> Self;
184}
185
186/// Implements `Maximum` for elements with a `MAX` associated constant.
187macro_rules! implement_maximum {
188 ($($index_type:ty,)*) => (
189 $(
190 impl Maximum for $index_type {
191 fn maximum() -> Self { Self::MAX }
192 }
193 )*
194 )
195}
196
197implement_maximum!(usize, u128, u64, u32, u16, u8, isize, i128, i64, i32, i16, i8, Duration,);
198impl Maximum for () { fn maximum() -> () { () }}
199
200use timely::progress::Timestamp;
201
202// Tuples have the annoyance that they are only a lattice for `T2` with maximal elements,
203// as the `meet` operator on `(x, _)` and `(y, _)` would be `(x meet y, maximum())`.
204impl<T1: Lattice+Clone, T2: Lattice+Clone+Maximum+Timestamp> Lattice for (T1, T2) {
205 #[inline]
206 fn join(&self, other: &(T1, T2)) -> (T1, T2) {
207 if self.0.eq(&other.0) {
208 (self.0.clone(), self.1.join(&other.1))
209 } else if self.0.less_than(&other.0) {
210 other.clone()
211 } else if other.0.less_than(&self.0) {
212 self.clone()
213 } else {
214 (self.0.join(&other.0), T2::minimum())
215 }
216 }
217 #[inline]
218 fn meet(&self, other: &(T1, T2)) -> (T1, T2) {
219 if self.0.eq(&other.0) {
220 (self.0.clone(), self.1.meet(&other.1))
221 } else if self.0.less_than(&other.0) {
222 self.clone()
223 } else if other.0.less_than(&self.0) {
224 other.clone()
225 } else {
226 (self.0.meet(&other.0), T2::maximum())
227 }
228 }
229}
230
231macro_rules! implement_lattice {
232 ($index_type:ty, $minimum:expr) => (
233 impl Lattice for $index_type {
234 #[inline] fn join(&self, other: &Self) -> Self { ::std::cmp::max(*self, *other) }
235 #[inline] fn meet(&self, other: &Self) -> Self { ::std::cmp::min(*self, *other) }
236 }
237 )
238}
239
240use std::time::Duration;
241
242implement_lattice!(Duration, Duration::new(0, 0));
243implement_lattice!(usize, 0);
244implement_lattice!(u128, 0);
245implement_lattice!(u64, 0);
246implement_lattice!(u32, 0);
247implement_lattice!(u16, 0);
248implement_lattice!(u8, 0);
249implement_lattice!(isize, 0);
250implement_lattice!(i128, 0);
251implement_lattice!(i64, 0);
252implement_lattice!(i32, 0);
253implement_lattice!(i16, 0);
254implement_lattice!(i8, 0);
255implement_lattice!((), ());
256
257/// Returns the "smallest" minimal antichain "greater or equal" to both inputs.
258///
259/// This method is primarily meant for cases where one cannot use the methods
260/// of `Antichain`'s `PartialOrder` implementation, such as when one has only
261/// references rather than owned antichains.
262///
263/// # Examples
264///
265/// ```
266/// # use timely::PartialOrder;
267/// # use timely::order::Product;
268/// # use differential_dataflow::lattice::Lattice;
269/// # use differential_dataflow::lattice::antichain_join;
270/// # fn main() {
271///
272/// let f1 = &[Product::new(3, 7), Product::new(5, 6)];
273/// let f2 = &[Product::new(4, 6)];
274/// let join = antichain_join(f1, f2);
275/// assert_eq!(&*join.elements(), &[Product::new(4, 7), Product::new(5, 6)]);
276/// # }
277/// ```
278pub fn antichain_join<T: Lattice>(one: &[T], other: &[T]) -> Antichain<T> {
279 let mut upper = Antichain::new();
280 antichain_join_into(one, other, &mut upper);
281 upper
282}
283
284/// Returns the "smallest" minimal antichain "greater or equal" to both inputs.
285///
286/// This method is primarily meant for cases where one cannot use the methods
287/// of `Antichain`'s `PartialOrder` implementation, such as when one has only
288/// references rather than owned antichains.
289///
290/// This function is similar to [antichain_join] but reuses an existing allocation.
291/// The provided antichain is cleared before inserting elements.
292///
293/// # Examples
294///
295/// ```
296/// # use timely::PartialOrder;
297/// # use timely::order::Product;
298/// # use timely::progress::Antichain;
299/// # use differential_dataflow::lattice::Lattice;
300/// # use differential_dataflow::lattice::antichain_join_into;
301/// # fn main() {
302///
303/// let mut join = Antichain::new();
304/// let f1 = &[Product::new(3, 7), Product::new(5, 6)];
305/// let f2 = &[Product::new(4, 6)];
306/// antichain_join_into(f1, f2, &mut join);
307/// assert_eq!(&*join.elements(), &[Product::new(4, 7), Product::new(5, 6)]);
308/// # }
309/// ```
310pub fn antichain_join_into<T: Lattice>(one: &[T], other: &[T], upper: &mut Antichain<T>) {
311 upper.clear();
312 for time1 in one {
313 for time2 in other {
314 upper.insert(time1.join(time2));
315 }
316 }
317}
318
319/// Returns the "greatest" minimal antichain "less or equal" to both inputs.
320///
321/// This method is primarily meant for cases where one cannot use the methods
322/// of `Antichain`'s `PartialOrder` implementation, such as when one has only
323/// references rather than owned antichains.
324///
325/// # Examples
326///
327/// ```
328/// # use timely::PartialOrder;
329/// # use timely::order::Product;
330/// # use differential_dataflow::lattice::Lattice;
331/// # use differential_dataflow::lattice::antichain_meet;
332/// # fn main() {
333///
334/// let f1 = &[Product::new(3, 7), Product::new(5, 6)];
335/// let f2 = &[Product::new(4, 6)];
336/// let meet = antichain_meet(f1, f2);
337/// assert_eq!(&*meet.elements(), &[Product::new(3, 7), Product::new(4, 6)]);
338/// # }
339/// ```
340pub fn antichain_meet<T: Lattice+Clone>(one: &[T], other: &[T]) -> Antichain<T> {
341 let mut upper = Antichain::new();
342 for time1 in one {
343 upper.insert(time1.clone());
344 }
345 for time2 in other {
346 upper.insert(time2.clone());
347 }
348 upper
349}
350
351impl<T: Lattice+Clone> Lattice for Antichain<T> {
352 fn join(&self, other: &Self) -> Self {
353 let mut upper = Antichain::new();
354 for time1 in self.elements().iter() {
355 for time2 in other.elements().iter() {
356 upper.insert(time1.join(time2));
357 }
358 }
359 upper
360 }
361 fn meet(&self, other: &Self) -> Self {
362 let mut upper = Antichain::new();
363 for time1 in self.elements().iter() {
364 upper.insert(time1.clone());
365 }
366 for time2 in other.elements().iter() {
367 upper.insert(time2.clone());
368 }
369 upper
370 }
371}