Skip to main content

differential_dataflow/operators/
count.rs

1//! Count the number of occurrences of each element.
2
3use timely::order::TotalOrder;
4use timely::progress::Timestamp;
5use timely::dataflow::operators::Operator;
6use timely::dataflow::channels::pact::Pipeline;
7
8use crate::lattice::Lattice;
9use crate::{ExchangeData, VecCollection};
10use crate::difference::{IsZero, Semigroup};
11use crate::hashable::Hashable;
12use crate::collection::AsCollection;
13use crate::operators::arrange::Arranged;
14use crate::trace::{BatchCursor, BatchDiff, BatchDiffGat, BatchReader, Cursor, Navigable, TraceReader};
15
16/// Extension trait for the `count` differential dataflow method.
17pub trait CountTotal<'scope, T: Timestamp + TotalOrder + Lattice, K: ExchangeData, R: Semigroup> : Sized {
18    /// Counts the number of occurrences of each element.
19    ///
20    /// # Examples
21    ///
22    /// ```
23    /// use differential_dataflow::input::Input;
24    /// use differential_dataflow::operators::CountTotal;
25    ///
26    /// ::timely::example(|scope| {
27    ///     // report the number of occurrences of each key
28    ///     scope.new_collection_from(1 .. 10).1
29    ///          .map(|x| x / 3)
30    ///          .count_total();
31    /// });
32    /// ```
33    fn count_total(self) -> VecCollection<'scope, T, (K, R), isize> {
34        self.count_total_core()
35    }
36
37    /// Count for general integer differences.
38    ///
39    /// This method allows `count_total` to produce collections whose difference
40    /// type is something other than an `isize` integer, for example perhaps an
41    /// `i32`.
42    fn count_total_core<R2: Semigroup + From<i8> + 'static>(self) -> VecCollection<'scope, T, (K, R), R2>;
43}
44
45impl<'scope, T, K: ExchangeData+Hashable, R: ExchangeData+Semigroup> CountTotal<'scope, T, K, R> for VecCollection<'scope, T, K, R>
46where
47    T: Timestamp + TotalOrder + Lattice,
48{
49    fn count_total_core<R2: Semigroup + From<i8> + 'static>(self) -> VecCollection<'scope, T, (K, R), R2> {
50        self.arrange_by_self_named("Arrange: CountTotal")
51            .count_total_core()
52    }
53}
54
55impl<'scope, K, Tr> CountTotal<'scope, Tr::Time, K, BatchDiff<Tr>> for Arranged<'scope, Tr>
56where
57    Tr: TraceReader<Batch: Navigable, Time: TotalOrder> + Clone + 'static,
58    for<'a> BatchCursor<Tr>: Cursor<
59        Key<'a> = &'a K,
60        Val<'a> = &'a (),
61        Time = Tr::Time,
62        Diff: ExchangeData + Semigroup<BatchDiffGat<'a, Tr>>,
63    >,
64    K: ExchangeData,
65{
66    fn count_total_core<R2: Semigroup + From<i8> + 'static>(self) -> VecCollection<'scope, Tr::Time, (K, BatchDiff<Tr>), R2> {
67
68        let mut trace = self.trace.clone();
69
70        self.stream.unary_frontier(Pipeline, "CountTotal", move |_,_| {
71
72            // tracks the lower and upper limit of received batches.
73            let mut lower_limit = timely::progress::frontier::Antichain::from_elem(Tr::Time::minimum());
74            let mut upper_limit = timely::progress::frontier::Antichain::from_elem(Tr::Time::minimum());
75
76            move |(input, _frontier), output| {
77
78                let mut batch_storage = Vec::new();
79
80                // Downgrade previous upper limit to be current lower limit.
81                lower_limit.clear();
82                lower_limit.extend(upper_limit.borrow().iter().cloned());
83
84                let mut cap = None;
85                input.for_each(|capability, batches| {
86                    if cap.is_none() {                          // NB: Assumes batches are in-order
87                        cap = Some(capability.retain(0));
88                    }
89                    for batch in batches.drain(..) {
90                        upper_limit.clone_from(batch.upper());  // NB: Assumes batches are in-order
91                        batch_storage.push(batch);
92                    }
93                });
94
95                if let Some(capability) = cap {
96
97                    let mut session = output.session(&capability);
98
99                    let (mut batch_cursor, batch_storage) = crate::trace::cursor::cursor_list(batch_storage);
100                    let (mut trace_cursor, trace_storage) = trace.cursor_through(lower_limit.borrow()).unwrap();
101
102                    while let Some(key) = batch_cursor.get_key(&batch_storage) {
103                        let mut count: Option<BatchDiff<Tr>> = None;
104
105                        trace_cursor.seek_key(&trace_storage, key);
106                        if trace_cursor.get_key(&trace_storage) == Some(key) {
107                            trace_cursor.map_times(&trace_storage, |_, diff| {
108                                count.as_mut().map(|c| c.plus_equals(&diff));
109                                if count.is_none() { count = Some(<BatchCursor<Tr> as Cursor>::owned_diff(diff)); }
110                            });
111                        }
112
113                        batch_cursor.map_times(&batch_storage, |time, diff| {
114
115                            if let Some(count) = count.as_ref() {
116                                if !count.is_zero() {
117                                    session.give(((key.clone(), count.clone()), <BatchCursor<Tr> as Cursor>::owned_time(time), R2::from(-1i8)));
118                                }
119                            }
120                            count.as_mut().map(|c| c.plus_equals(&diff));
121                            if count.is_none() { count = Some(<BatchCursor<Tr> as Cursor>::owned_diff(diff)); }
122                            if let Some(count) = count.as_ref() {
123                                if !count.is_zero() {
124                                    session.give(((key.clone(), count.clone()), <BatchCursor<Tr> as Cursor>::owned_time(time), R2::from(1i8)));
125                                }
126                            }
127                        });
128
129                        batch_cursor.step_key(&batch_storage);
130                    }
131                }
132
133                // tidy up the shared input trace.
134                trace.advance_upper(&mut upper_limit);
135                trace.set_logical_compaction(upper_limit.borrow());
136                trace.set_physical_compaction(upper_limit.borrow());
137            }
138        })
139        .as_collection()
140    }
141}