differential_dataflow/operators/
threshold.rs1use timely::order::TotalOrder;
7use timely::progress::Timestamp;
8use timely::dataflow::operators::Operator;
9use timely::dataflow::channels::pact::Pipeline;
10
11use crate::lattice::Lattice;
12use crate::{ExchangeData, VecCollection};
13use crate::difference::{Semigroup, Abelian};
14use crate::hashable::Hashable;
15use crate::collection::AsCollection;
16use crate::operators::arrange::Arranged;
17use crate::trace::{BatchCursor, BatchDiff, BatchDiffGat, BatchKey, Cursor, Navigable, TraceReader};
18
19pub trait ThresholdTotal<'scope, T: Timestamp + TotalOrder + Lattice, K: ExchangeData, R: ExchangeData+Semigroup> : Sized {
21 fn threshold_semigroup<R2, F>(self, thresh: F) -> VecCollection<'scope, T, K, R2>
23 where
24 R2: Semigroup+'static,
25 F: FnMut(&K,&R,Option<&R>)->Option<R2>+'static,
26 ;
27 fn threshold_total<R2: Abelian+'static, F: FnMut(&K,&R)->R2+'static>(self, mut thresh: F) -> VecCollection<'scope, T, K, R2> {
45 self.threshold_semigroup(move |key, new, old| {
46 let mut new = if new.is_zero() { R2::zero() } else { thresh(key, new) };
49 if let Some(old) = old.filter(|old| !old.is_zero()) {
50 let mut add = thresh(key, old);
51 add.negate();
52 new.plus_equals(&add);
53 }
54 if !new.is_zero() { Some(new) } else { None }
55 })
56 }
57 fn distinct_total(self) -> VecCollection<'scope, T, K, isize> {
77 self.distinct_total_core()
78 }
79
80 fn distinct_total_core<R2: Abelian+From<i8>+'static>(self) -> VecCollection<'scope, T, K, R2> {
86 self.threshold_total(|_,_| R2::from(1i8))
87 }
88
89}
90
91impl<'scope, T, K: ExchangeData+Hashable, R: ExchangeData+Semigroup> ThresholdTotal<'scope, T, K, R> for VecCollection<'scope, T, K, R>
92where
93 T: Timestamp + TotalOrder + Lattice,
94{
95 fn threshold_semigroup<R2, F>(self, thresh: F) -> VecCollection<'scope, T, K, R2>
96 where
97 R2: Semigroup+'static,
98 F: FnMut(&K,&R,Option<&R>)->Option<R2>+'static,
99 {
100 self.arrange_by_self_named("Arrange: ThresholdTotal")
101 .threshold_semigroup(thresh)
102 }
103}
104
105impl<'scope, K, Tr> ThresholdTotal<'scope, Tr::Time, K, BatchDiff<Tr>> for Arranged<'scope, Tr>
106where
107 Tr: TraceReader<Batch: Navigable, Time: TotalOrder> + Clone + 'static,
108 for<'a> BatchCursor<Tr>: Cursor<
109 Key<'a>=&'a K,
110 Val<'a>=&'a (),
111 Time = Tr::Time,
112 Diff : ExchangeData + Semigroup<BatchDiffGat<'a, Tr>>,
113 >,
114 K: ExchangeData,
115{
116 fn threshold_semigroup<R2, F>(self, mut thresh: F) -> VecCollection<'scope, Tr::Time, K, R2>
117 where
118 R2: Semigroup+'static,
119 F: for<'a> FnMut(BatchKey<'a, Tr>,&BatchDiff<Tr>,Option<&BatchDiff<Tr>>)->Option<R2>+'static,
120 {
121
122 let mut trace = self.trace.clone();
123
124 self.stream.unary_frontier(Pipeline, "ThresholdTotal", move |_,_| {
125
126 let mut lower_limit = timely::progress::frontier::Antichain::from_elem(Tr::Time::minimum());
128 let mut upper_limit = timely::progress::frontier::Antichain::from_elem(Tr::Time::minimum());
129
130 move |(input, _frontier), output| {
131
132 let mut batch_storage = Vec::new();
133
134 lower_limit.clear();
136 lower_limit.extend(upper_limit.borrow().iter().cloned());
137
138 let mut caps = timely::dataflow::operators::CapabilitySet::new();
139 input.for_each(|capability, batches| {
140 for capability in capability.retain_stamp(0).iter() {
141 caps.insert(capability.clone());
142 }
143 for batch in batches.drain(..) {
144 upper_limit.clone_from(batch.upper()); batch_storage.push(batch);
146 }
147 });
148
149 if !caps.is_empty() {
150
151 let mut session = output.session(&caps);
152
153 let (mut batch_cursor, batch_storage) = crate::trace::cursor::cursor_list(batch_storage.into_iter().filter_map(|b| b.inner).collect());
154 let (mut trace_cursor, trace_storage) = crate::trace::cursor::cursor_list(trace.batches_through(lower_limit.borrow()).unwrap());
155
156 while let Some(key) = batch_cursor.get_key(&batch_storage) {
157 let mut count: Option<BatchDiff<Tr>> = None;
158
159 trace_cursor.seek_key(&trace_storage, key);
161 if trace_cursor.get_key(&trace_storage) == Some(key) {
162 trace_cursor.map_times(&trace_storage, |_, diff| {
163 count.as_mut().map(|c| c.plus_equals(&diff));
164 if count.is_none() { count = Some(<BatchCursor<Tr> as Cursor>::owned_diff(diff)); }
165 });
166 }
167
168 batch_cursor.map_times(&batch_storage, |time, diff| {
171
172 let difference =
173 match &count {
174 Some(old) => {
175 let mut temp = old.clone();
176 temp.plus_equals(&diff);
177 thresh(key, &temp, Some(old))
178 },
179 None => { thresh(key, &<BatchCursor<Tr> as Cursor>::owned_diff(diff), None) },
180 };
181
182 if let Some(count) = &mut count {
184 count.plus_equals(&diff);
185 }
186 else {
187 count = Some(<BatchCursor<Tr> as Cursor>::owned_diff(diff));
188 }
189
190 if let Some(difference) = difference {
191 if !difference.is_zero() {
192 session.give((key.clone(), <BatchCursor<Tr> as Cursor>::owned_time(time), difference));
193 }
194 }
195 });
196
197 batch_cursor.step_key(&batch_storage);
198 }
199 }
200
201 trace.advance_upper(&mut upper_limit);
203 trace.set_logical_compaction(upper_limit.borrow());
204 trace.set_physical_compaction(upper_limit.borrow());
205 }
206 })
207 .as_collection()
208 }
209}