differential_dataflow/collection.rs
1//! Types and traits associated with collections of data.
2//!
3//! The `Collection` type is differential dataflow's core abstraction for an updatable pile of data.
4//!
5//! Most differential dataflow programs are "collection-oriented", in the sense that they transform
6//! one collection into another, using operators defined on collections. This contrasts with a more
7//! imperative programming style, in which one might iterate through the contents of a collection
8//! manually. The higher-level of programming allows differential dataflow to provide efficient
9//! implementations, and to support efficient incremental updates to the collections.
10
11use timely::Container;
12use timely::progress::Timestamp;
13use timely::dataflow::{Scope, Stream};
14use timely::dataflow::operators::*;
15
16use crate::difference::Abelian;
17
18/// An evolving collection represented by a stream of abstract containers.
19///
20/// The containers purport to reperesent changes to a collection, and they must implement various traits
21/// in order to expose some of this functionality (e.g. negation, timestamp manipulation). Other actions
22/// on the containers, and streams of containers, are left to the container implementor to describe.
23///
24/// Unchecked assumption: each update in a container has a time greater or equal to some time in the
25/// container's stamp. In particular, a container with an empty stamp holds no updates. Differential
26/// operators rely on this to retire updates as frontiers advance, and may behave unexpectedly if it fails.
27#[derive(Clone)]
28pub struct Collection<'scope, T: Timestamp, C: 'static> {
29 /// The underlying timely dataflow stream.
30 ///
31 /// This field is exposed to support direct timely dataflow manipulation when required, but it is
32 /// not intended to be the idiomatic way to work with the collection.
33 ///
34 /// Each update's time is required to be greater or equal to some time in its container's stamp,
35 /// as documented on [Collection]. If this invariant is not upheld, differential operators may behave
36 /// unexpectedly.
37 pub inner: Stream<'scope, T, C>,
38}
39
40impl<'scope, T: Timestamp, C> Collection<'scope, T, C> {
41 /// Creates a new Collection from a timely dataflow stream.
42 ///
43 /// This method seems to be rarely used, with the `as_collection` method on streams being a more
44 /// idiomatic approach to convert timely streams to collections. Also, the `input::Input` trait
45 /// provides a `new_collection` method which will create a new collection for you without exposing
46 /// the underlying timely stream at all.
47 ///
48 /// This stream should satisfy the timestamp invariant as documented on [Collection]; this
49 /// method does not check it.
50 pub fn new(stream: Stream<'scope, T, C>) -> Self { Self { inner: stream } }
51}
52impl<'scope, T: Timestamp, C: Container> Collection<'scope, T, C> {
53 /// Creates a new collection accumulating the contents of the two collections.
54 ///
55 /// Despite the name, differential dataflow collections are unordered. This method is so named because the
56 /// implementation is the concatenation of the stream of updates, but it corresponds to the addition of the
57 /// two collections.
58 ///
59 /// # Examples
60 ///
61 /// ```
62 /// use differential_dataflow::input::Input;
63 ///
64 /// ::timely::example(|scope| {
65 ///
66 /// let data = scope.new_collection_from(1 .. 10).1;
67 ///
68 /// let odds = data.clone().filter(|x| x % 2 == 1);
69 /// let evens = data.clone().filter(|x| x % 2 == 0);
70 ///
71 /// odds.concat(evens)
72 /// .assert_eq(data);
73 /// });
74 /// ```
75 pub fn concat(self, other: Self) -> Self {
76 self.inner
77 .concat(other.inner)
78 .as_collection()
79 }
80 /// Creates a new collection accumulating the contents of the two collections.
81 ///
82 /// Despite the name, differential dataflow collections are unordered. This method is so named because the
83 /// implementation is the concatenation of the stream of updates, but it corresponds to the addition of the
84 /// two collections.
85 ///
86 /// # Examples
87 ///
88 /// ```
89 /// use differential_dataflow::input::Input;
90 ///
91 /// ::timely::example(|scope| {
92 ///
93 /// let data = scope.new_collection_from(1 .. 10).1;
94 ///
95 /// let odds = data.clone().filter(|x| x % 2 == 1);
96 /// let evens = data.clone().filter(|x| x % 2 == 0);
97 ///
98 /// odds.concatenate(Some(evens))
99 /// .assert_eq(data);
100 /// });
101 /// ```
102 pub fn concatenate<I>(self, sources: I) -> Self
103 where
104 I: IntoIterator<Item=Self>
105 {
106 self.inner
107 .scope()
108 .concatenate(sources.into_iter().map(|x| x.inner).chain([self.inner]))
109 .as_collection()
110 }
111 // Brings a Collection into a nested region.
112 ///
113 /// This method is a specialization of `enter` to the case where the nested scope is a region.
114 /// It removes the need for an operator that adjusts the timestamp.
115 pub fn enter_region<'inner>(self, child: Scope<'inner, T>) -> Collection<'inner, T, C> {
116 self.inner
117 .enter(child)
118 .as_collection()
119 }
120 /// Applies a supplied function to each batch of updates.
121 ///
122 /// This method is analogous to `inspect`, but operates on batches and reveals the timestamp of the
123 /// timely dataflow capability associated with the batch of updates. The observed batching depends
124 /// on how the system executes, and may vary run to run.
125 ///
126 /// # Examples
127 ///
128 /// ```
129 /// use differential_dataflow::input::Input;
130 ///
131 /// ::timely::example(|scope| {
132 /// scope.new_collection_from(1 .. 10).1
133 /// .map_in_place(|x| *x *= 2)
134 /// .filter(|x| x % 2 == 1)
135 /// .inspect_core(|event| println!("event: {:?}", event));
136 /// });
137 /// ```
138 pub fn inspect_core<F>(self, func: F) -> Self
139 where
140 F: FnMut(Result<(&timely::progress::Stamp<T>, &C), &[T]>)+'static,
141 {
142 self.inner
143 .inspect_core(func)
144 .as_collection()
145 }
146 /// Attaches a timely dataflow probe to the output of a Collection.
147 ///
148 /// This probe is used to determine when the state of the Collection has stabilized and can
149 /// be read out.
150 pub fn probe(self) -> (probe::Handle<T>, Self) {
151 let (handle, stream) = self.inner.probe();
152 (handle, stream.as_collection())
153 }
154 /// Attaches a timely dataflow probe to the output of a Collection.
155 ///
156 /// This probe is used to determine when the state of the Collection has stabilized and all updates observed.
157 /// In addition, a probe is also often use to limit the number of rounds of input in flight at any moment; a
158 /// computation can wait until the probe has caught up to the input before introducing more rounds of data, to
159 /// avoid swamping the system.
160 pub fn probe_with(self, handle: &probe::Handle<T>) -> Self {
161 Self::new(self.inner.probe_with(handle))
162 }
163 /// The scope containing the underlying timely dataflow stream.
164 pub fn scope(&self) -> Scope<'scope, T> {
165 self.inner.scope()
166 }
167
168 /// Creates a new collection whose counts are the negation of those in the input.
169 ///
170 /// This method is most commonly used with `concat` to get those element in one collection but not another.
171 /// However, differential dataflow computations are still defined for all values of the difference type `R`,
172 /// including negative counts.
173 ///
174 /// # Examples
175 ///
176 /// ```
177 /// use differential_dataflow::input::Input;
178 ///
179 /// ::timely::example(|scope| {
180 ///
181 /// let data = scope.new_collection_from(1 .. 10).1;
182 ///
183 /// let odds = data.clone().filter(|x| x % 2 == 1);
184 /// let evens = data.clone().filter(|x| x % 2 == 0);
185 ///
186 /// odds.negate()
187 /// .concat(data)
188 /// .assert_eq(evens);
189 /// });
190 /// ```
191 pub fn negate(self) -> Self where C: containers::Negate {
192 use timely::dataflow::channels::pact::Pipeline;
193 self.inner
194 .unary(Pipeline, "Negate", move |_,_| move |input, output| {
195 input.for_each(|time, data| output.session(&time).give_container(&mut std::mem::take(data).negate()));
196 })
197 .as_collection()
198 }
199
200 /// Brings a Collection into a nested scope.
201 ///
202 /// # Examples
203 ///
204 /// ```
205 /// use timely::dataflow::Scope;
206 /// use differential_dataflow::input::Input;
207 ///
208 /// ::timely::example(|scope| {
209 ///
210 /// let data = scope.new_collection_from(1 .. 10).1;
211 ///
212 /// let result = scope.region(|child| {
213 /// data.clone()
214 /// .enter(child)
215 /// .leave(scope)
216 /// });
217 ///
218 /// data.assert_eq(result);
219 /// });
220 /// ```
221 pub fn enter<'inner, TInner>(self, child: Scope<'inner, TInner>) -> Collection<'inner, TInner, <C as containers::Enter<T, TInner>>::InnerContainer>
222 where
223 C: containers::Enter<T, TInner, InnerContainer: Container>,
224 TInner: Refines<T>,
225 {
226 use timely::dataflow::channels::pact::Pipeline;
227 self.inner
228 .enter(child)
229 .unary(Pipeline, "Enter", move |_,_| move |input, output| {
230 input.for_each(|time, data| output.session(&time).give_container(&mut std::mem::take(data).enter()));
231 })
232 .as_collection()
233 }
234
235 /// Advances a timestamp in the stream according to the timestamp actions on the path.
236 ///
237 /// The path may advance the timestamp sufficiently that it is no longer valid, for example if
238 /// incrementing fields would result in integer overflow. In this case, the record is dropped.
239 ///
240 /// # Examples
241 /// ```
242 /// use differential_dataflow::input::Input;
243 ///
244 /// timely::example(|scope| {
245 /// let summary1 = 5;
246 ///
247 /// let data = scope.new_collection_from(1 .. 10).1;
248 /// /// Applies `results_in` on every timestamp in the collection.
249 /// data.results_in(summary1);
250 /// });
251 /// ```
252 pub fn results_in(self, step: T::Summary) -> Self
253 where
254 C: containers::ResultsIn<T::Summary>,
255 {
256 use timely::dataflow::channels::pact::Pipeline;
257 self.inner
258 .unary(Pipeline, "ResultsIn", move |_,_| move |input, output| {
259 input.for_each(|time, data| output.session(&time).give_container(&mut std::mem::take(data).results_in(&step)));
260 })
261 .as_collection()
262 }
263}
264
265use timely::progress::timestamp::Refines;
266
267/// Methods requiring a nested scope.
268impl<'scope, T: Timestamp, C: Container> Collection<'scope, T, C>
269{
270 /// Returns the final value of a Collection from a nested scope to its containing scope.
271 ///
272 /// # Examples
273 ///
274 /// ```
275 /// use timely::dataflow::Scope;
276 /// use differential_dataflow::input::Input;
277 ///
278 /// ::timely::example(|scope| {
279 ///
280 /// let data = scope.new_collection_from(1 .. 10).1;
281 ///
282 /// let result = scope.region(|child| {
283 /// data.clone()
284 /// .enter(child)
285 /// .leave(scope)
286 /// });
287 ///
288 /// data.assert_eq(result);
289 /// });
290 /// ```
291 pub fn leave<'outer, TOuter>(self, outer: Scope<'outer, TOuter>) -> Collection<'outer, TOuter, <C as containers::Leave<T, TOuter>>::OuterContainer>
292 where
293 TOuter: Timestamp,
294 T: Refines<TOuter>,
295 C: containers::Leave<T, TOuter, OuterContainer: Container>,
296 {
297 use timely::dataflow::channels::pact::Pipeline;
298 self.inner
299 .leave(outer)
300 .unary(Pipeline, "Leave", move |_,_| move |input, output| {
301 input.for_each(|time, data| output.session(&time).give_container(&mut std::mem::take(data).leave()));
302 })
303 .as_collection()
304 }
305
306 /// Returns the value of a Collection from a nested region to its containing scope.
307 ///
308 /// This method is a specialization of `leave` to the case that of a nested region.
309 /// It removes the need for an operator that adjusts the timestamp.
310 pub fn leave_region<'outer>(self, outer: Scope<'outer, T>) -> Collection<'outer, T, C> {
311 self.inner
312 .leave(outer)
313 .as_collection()
314 }
315}
316
317pub use vec::Collection as VecCollection;
318/// Specializations of `Collection` that use `Vec` as the container.
319pub mod vec {
320
321 use std::hash::Hash;
322
323 use timely::progress::Timestamp;
324 use timely::order::Product;
325 use timely::dataflow::scope::Iterative;
326 use timely::dataflow::operators::*;
327 use timely::dataflow::operators::vec::*;
328
329 use crate::collection::AsCollection;
330 use crate::difference::{Semigroup, Abelian, Multiply};
331 use crate::lattice::Lattice;
332 use crate::hashable::Hashable;
333 use crate::trace::{BatchCursor, BatchDiff, BatchKey, BatchVal, Navigable};
334 use crate::trace::Cursor;
335
336 /// An evolving collection of values of type `D`, backed by Rust `Vec` types as containers.
337 ///
338 /// The `Collection` type is the core abstraction in differential dataflow programs. As you write your
339 /// differential dataflow computation, you write as if the collection is a static dataset to which you
340 /// apply functional transformations, creating new collections. Once your computation is written, you
341 /// are able to mutate the collection (by inserting and removing elements); differential dataflow will
342 /// propagate changes through your functional computation and report the corresponding changes to the
343 /// output collections.
344 ///
345 /// Each vec collection has three generic parameters. The parameter `T` is the timestamp type of the
346 /// scope in which the collection exists; as you write more complicated programs you may wish to
347 /// introduce nested scopes (e.g. for iteration), and this parameter tracks the scope's timestamp
348 /// (for timely dataflow's benefit). The `D` parameter is the type of data in your collection, for
349 /// example `String`, or `(u32, Vec<Option<()>>)`.
350 /// The `R` parameter represents the types of changes that the data undergo, and is most commonly (and
351 /// defaults to) `isize`, representing changes to the occurrence count of each record.
352 ///
353 /// This type definition instantiates the [`Collection`] type with a `Vec<(D, T, R)>`.
354 pub type Collection<'scope, T, D, R = isize> = super::Collection<'scope, T, Vec<(D, T, R)>>;
355
356
357 impl<'scope, T: Timestamp, D: Clone+'static, R: Clone+'static> Collection<'scope, T, D, R> {
358 /// Creates a new collection by applying the supplied function to each input element.
359 ///
360 /// # Examples
361 ///
362 /// ```
363 /// use differential_dataflow::input::Input;
364 ///
365 /// ::timely::example(|scope| {
366 /// scope.new_collection_from(1 .. 10).1
367 /// .map(|x| x * 2)
368 /// .filter(|x| x % 2 == 1)
369 /// .assert_empty();
370 /// });
371 /// ```
372 pub fn map<D2, L>(self, mut logic: L) -> Collection<'scope, T, D2, R>
373 where
374 D2: Clone+'static,
375 L: FnMut(D) -> D2 + 'static,
376 {
377 self.inner
378 .map(move |(data, time, delta)| (logic(data), time, delta))
379 .as_collection()
380 }
381 /// Creates a new collection by applying the supplied function to each input element.
382 ///
383 /// Although the name suggests in-place mutation, this function does not change the source collection,
384 /// but rather re-uses the underlying allocations in its implementation. The method is semantically
385 /// equivalent to `map`, but can be more efficient.
386 ///
387 /// # Examples
388 ///
389 /// ```
390 /// use differential_dataflow::input::Input;
391 ///
392 /// ::timely::example(|scope| {
393 /// scope.new_collection_from(1 .. 10).1
394 /// .map_in_place(|x| *x *= 2)
395 /// .filter(|x| x % 2 == 1)
396 /// .assert_empty();
397 /// });
398 /// ```
399 pub fn map_in_place<L>(self, mut logic: L) -> Collection<'scope, T, D, R>
400 where
401 L: FnMut(&mut D) + 'static,
402 {
403 self.inner
404 .map_in_place(move |&mut (ref mut data, _, _)| logic(data))
405 .as_collection()
406 }
407 /// Creates a new collection by applying the supplied function to each input element and accumulating the results.
408 ///
409 /// This method extracts an iterator from each input element, and extracts the full contents of the iterator. Be
410 /// warned that if the iterators produce substantial amounts of data, they are currently fully drained before
411 /// attempting to consolidate the results.
412 ///
413 /// # Examples
414 ///
415 /// ```
416 /// use differential_dataflow::input::Input;
417 ///
418 /// ::timely::example(|scope| {
419 /// scope.new_collection_from(1 .. 10).1
420 /// .flat_map(|x| 0 .. x);
421 /// });
422 /// ```
423 pub fn flat_map<I, L>(self, mut logic: L) -> Collection<'scope, T, I::Item, R>
424 where
425 T: Clone,
426 I: IntoIterator<Item: Clone+'static>,
427 L: FnMut(D) -> I + 'static,
428 {
429 self.inner
430 .flat_map(move |(data, time, delta)| logic(data).into_iter().map(move |x| (x, time.clone(), delta.clone())))
431 .as_collection()
432 }
433 /// Creates a new collection containing those input records satisfying the supplied predicate.
434 ///
435 /// # Examples
436 ///
437 /// ```
438 /// use differential_dataflow::input::Input;
439 ///
440 /// ::timely::example(|scope| {
441 /// scope.new_collection_from(1 .. 10).1
442 /// .map(|x| x * 2)
443 /// .filter(|x| x % 2 == 1)
444 /// .assert_empty();
445 /// });
446 /// ```
447 pub fn filter<L>(self, mut logic: L) -> Collection<'scope, T, D, R>
448 where
449 L: FnMut(&D) -> bool + 'static,
450 {
451 self.inner
452 .filter(move |(data, _, _)| logic(data))
453 .as_collection()
454 }
455 /// Replaces each record with another, with a new difference type.
456 ///
457 /// This method is most commonly used to take records containing aggregatable data (e.g. numbers to be summed)
458 /// and move the data into the difference component. This will allow differential dataflow to update in-place.
459 ///
460 /// # Examples
461 ///
462 /// ```
463 /// use differential_dataflow::input::Input;
464 ///
465 /// ::timely::example(|scope| {
466 ///
467 /// let nums = scope.new_collection_from(0 .. 10).1;
468 /// let x1 = nums.clone().flat_map(|x| 0 .. x);
469 /// let x2 = nums.map(|x| (x, 9 - x))
470 /// .explode(|(x,y)| Some((x,y)));
471 ///
472 /// x1.assert_eq(x2);
473 /// });
474 /// ```
475 pub fn explode<D2, R2, I, L>(self, mut logic: L) -> Collection<'scope, T, D2, <R2 as Multiply<R>>::Output>
476 where
477 D2: Clone+'static,
478 R2: Semigroup+Multiply<R, Output: Semigroup+'static>,
479 I: IntoIterator<Item=(D2,R2)>,
480 L: FnMut(D)->I+'static,
481 {
482 self.inner
483 .flat_map(move |(x, t, d)| logic(x).into_iter().map(move |(x,d2)| (x, t.clone(), d2.multiply(&d))))
484 .as_collection()
485 }
486
487 /// Joins each record against a collection defined by the function `logic`.
488 ///
489 /// This method performs what is essentially a join with the collection of records `(x, logic(x))`.
490 /// Rather than materialize this second relation, `logic` is applied to each record and the appropriate
491 /// modifications made to the results, namely joining timestamps and multiplying differences.
492 ///
493 /// #Examples
494 ///
495 /// ```
496 /// use differential_dataflow::input::Input;
497 ///
498 /// ::timely::example(|scope| {
499 /// // creates `x` copies of `2*x` from time `3*x` until `4*x`,
500 /// // for x from 0 through 9.
501 /// scope.new_collection_from(0 .. 10isize).1
502 /// .join_function(|x|
503 /// // data time diff
504 /// vec![(2*x, (3*x) as u64, x),
505 /// (2*x, (4*x) as u64, -x)]
506 /// );
507 /// });
508 /// ```
509 pub fn join_function<D2, R2, I, L>(self, mut logic: L) -> Collection<'scope, T, D2, <R2 as Multiply<R>>::Output>
510 where
511 T: Lattice,
512 D2: Clone+'static,
513 R2: Semigroup+Multiply<R, Output: Semigroup+'static>,
514 I: IntoIterator<Item=(D2,T,R2)>,
515 L: FnMut(D)->I+'static,
516 {
517 self.inner
518 .flat_map(move |(x, t, d)| logic(x).into_iter().map(move |(x,t2,d2)| (x, t.join(&t2), d2.multiply(&d))))
519 .as_collection()
520 }
521
522 /// Brings a Collection into a nested scope, at varying times.
523 ///
524 /// The `initial` function indicates the time at which each element of the Collection should appear.
525 ///
526 /// # Examples
527 ///
528 /// ```
529 /// use timely::dataflow::Scope;
530 /// use differential_dataflow::input::Input;
531 ///
532 /// ::timely::example(|scope| {
533 ///
534 /// let data = scope.new_collection_from(1 .. 10).1;
535 ///
536 /// let result = scope.iterative::<u64,_,_>(|child| {
537 /// data.clone()
538 /// .enter_at(child, |x| *x)
539 /// .leave(scope)
540 /// });
541 ///
542 /// data.assert_eq(result);
543 /// });
544 /// ```
545 pub fn enter_at<'inner, TInner, F>(self, child: Iterative<'inner, T, TInner>, mut initial: F) -> Collection<'inner, Product<T, TInner>, D, R>
546 where
547 TInner: Timestamp+Hash,
548 F: FnMut(&D) -> TInner + Clone + 'static,
549 {
550 self.inner
551 .enter(child)
552 .map(move |(data, time, diff)| {
553 let new_time = Product::new(time, initial(&data));
554 (data, new_time, diff)
555 })
556 .as_collection()
557 }
558
559 /// Delays each difference by a supplied function.
560 ///
561 /// It is assumed that `func` only advances timestamps; this is not verified, and things may go horribly
562 /// wrong if that assumption is incorrect. It is also critical that `func` be monotonic: if two times are
563 /// ordered, they should have the same order or compare equal once `func` is applied to them (this
564 /// is because we advance the timely capability with the same logic, and it must remain `less_equal`
565 /// to all of the data timestamps).
566 pub fn delay<F>(self, mut func: F) -> Collection<'scope, T, D, R>
567 where
568 F: FnMut(&T) -> T + 'static,
569 {
570 use timely::dataflow::channels::pact::Pipeline;
571 use timely::dataflow::operators::CapabilitySet;
572 self.inner
573 .unary(Pipeline, "Delay", move |_, _| move |input, output| {
574 input.for_each_stamp(|time, data| {
575 // Each element of the stamp is delayed by `func`, and each update's time likewise;
576 // by monotonicity the delayed capabilities still cover the delayed updates.
577 let caps: CapabilitySet<T> = time.stamp().iter().map(|t| time.delayed(&func(t), output.output_index())).collect();
578 let mut session = output.session(&caps);
579 for data in data {
580 for (_, t, _) in data.iter_mut() { *t = func(t); }
581 session.give_container(data);
582 }
583 });
584 })
585 .as_collection()
586 }
587
588 /// Applies a supplied function to each update.
589 ///
590 /// This method is most commonly used to report information back to the user, often for debugging purposes.
591 /// Any function can be used here, but be warned that the incremental nature of differential dataflow does
592 /// not guarantee that it will be called as many times as you might expect.
593 ///
594 /// The `(data, time, diff)` triples indicate a change `diff` to the frequency of `data` which takes effect
595 /// at the logical time `time`. When times are totally ordered (for example, `usize`), these updates reflect
596 /// the changes along the sequence of collections. For partially ordered times, the mathematics are more
597 /// interesting and less intuitive, unfortunately.
598 ///
599 /// # Examples
600 ///
601 /// ```
602 /// use differential_dataflow::input::Input;
603 ///
604 /// ::timely::example(|scope| {
605 /// scope.new_collection_from(1 .. 10).1
606 /// .map_in_place(|x| *x *= 2)
607 /// .filter(|x| x % 2 == 1)
608 /// .inspect(|x| println!("error: {:?}", x));
609 /// });
610 /// ```
611 pub fn inspect<F>(self, func: F) -> Collection<'scope, T, D, R>
612 where
613 F: FnMut(&(D, T, R))+'static,
614 {
615 self.inner
616 .inspect(func)
617 .as_collection()
618 }
619 /// Applies a supplied function to each batch of updates.
620 ///
621 /// This method is analogous to `inspect`, but operates on batches and reveals the stamp under which
622 /// the batch of updates travels: the timestamps of the timely dataflow capabilities associated with it. The observed batching depends
623 /// on how the system executes, and may vary run to run.
624 ///
625 /// # Examples
626 ///
627 /// ```
628 /// use differential_dataflow::input::Input;
629 ///
630 /// ::timely::example(|scope| {
631 /// scope.new_collection_from(1 .. 10).1
632 /// .map_in_place(|x| *x *= 2)
633 /// .filter(|x| x % 2 == 1)
634 /// .inspect_batch(|t,xs| println!("errors @ {:?}: {:?}", t, xs));
635 /// });
636 /// ```
637 pub fn inspect_batch<F>(self, mut func: F) -> Collection<'scope, T, D, R>
638 where
639 F: FnMut(&timely::progress::Stamp<T>, &[(D, T, R)])+'static,
640 {
641 self.inner
642 .inspect_core(move |event| if let Ok((stamp, data)) = event { func(stamp, data) })
643 .as_collection()
644 }
645
646 /// Assert if the collection is ever non-empty.
647 ///
648 /// Because this is a dataflow fragment, the test is only applied as the computation is run. If the computation
649 /// is not run, or not run to completion, there may be un-exercised times at which the collection could be
650 /// non-empty. Typically, a timely dataflow computation runs to completion on drop, and so clean exit from a
651 /// program should indicate that this assertion never found cause to complain.
652 ///
653 /// # Examples
654 ///
655 /// ```
656 /// use differential_dataflow::input::Input;
657 ///
658 /// ::timely::example(|scope| {
659 /// scope.new_collection_from(1 .. 10).1
660 /// .map(|x| x * 2)
661 /// .filter(|x| x % 2 == 1)
662 /// .assert_empty();
663 /// });
664 /// ```
665 pub fn assert_empty(self)
666 where
667 D: crate::ExchangeData+Hashable,
668 R: crate::ExchangeData+Hashable + Semigroup,
669 T: Lattice+Ord,
670 {
671 self.consolidate()
672 .inspect(|x| panic!("Assertion failed: non-empty collection: {:?}", x));
673 }
674 }
675
676 /// Methods requiring an Abelian difference, to support negation.
677 impl<'scope, T: Timestamp + Clone + 'static, D: Clone+'static, R: Abelian+'static> Collection<'scope, T, D, R> {
678 /// Assert if the collections are ever different.
679 ///
680 /// Because this is a dataflow fragment, the test is only applied as the computation is run. If the computation
681 /// is not run, or not run to completion, there may be un-exercised times at which the collections could vary.
682 /// Typically, a timely dataflow computation runs to completion on drop, and so clean exit from a program should
683 /// indicate that this assertion never found cause to complain.
684 ///
685 /// # Examples
686 ///
687 /// ```
688 /// use differential_dataflow::input::Input;
689 ///
690 /// ::timely::example(|scope| {
691 ///
692 /// let data = scope.new_collection_from(1 .. 10).1;
693 ///
694 /// let odds = data.clone().filter(|x| x % 2 == 1);
695 /// let evens = data.clone().filter(|x| x % 2 == 0);
696 ///
697 /// odds.concat(evens)
698 /// .assert_eq(data);
699 /// });
700 /// ```
701 pub fn assert_eq(self, other: Self)
702 where
703 D: crate::ExchangeData+Hashable,
704 R: crate::ExchangeData+Hashable,
705 T: Lattice+Ord,
706 {
707 self.negate()
708 .concat(other)
709 .assert_empty();
710 }
711 }
712
713 use crate::trace::{Trace, Builder};
714 use crate::operators::arrange::{Arranged, TraceAgent};
715
716 impl <'scope, T, K, V, R> Collection<'scope, T, (K, V), R>
717 where
718 T: Timestamp + Lattice + Ord,
719 K: crate::ExchangeData+Hashable,
720 V: crate::ExchangeData,
721 R: crate::ExchangeData+Semigroup,
722 {
723 /// Applies a reduction function on records grouped by key.
724 ///
725 /// Input data must be structured as `(key, val)` pairs.
726 /// The user-supplied reduction function takes as arguments
727 ///
728 /// 1. a reference to the key,
729 /// 2. a reference to the slice of values and their accumulated updates,
730 /// 3. a mutuable reference to a vector to populate with output values and accumulated updates.
731 ///
732 /// The user logic is only invoked for non-empty input collections, and it is safe to assume that the
733 /// slice of input values is non-empty. The values are presented in sorted order, as defined by their
734 /// `Ord` implementations.
735 ///
736 /// # Examples
737 ///
738 /// ```
739 /// use differential_dataflow::input::Input;
740 ///
741 /// ::timely::example(|scope| {
742 /// // report the smallest value for each group
743 /// scope.new_collection_from(1 .. 10).1
744 /// .map(|x| (x / 3, x))
745 /// .reduce(|_key, input, output| {
746 /// output.push((*input[0].0, 1))
747 /// });
748 /// });
749 /// ```
750 pub fn reduce<L, V2: crate::Data, R2: Ord+Abelian+'static>(self, logic: L) -> Collection<'scope, T, (K, V2), R2>
751 where L: FnMut(&K, &[(&V, R)], &mut Vec<(V2, R2)>)+'static {
752 self.reduce_named("Reduce", logic)
753 }
754
755 /// As `reduce` with the ability to name the operator.
756 pub fn reduce_named<L, V2: crate::Data, R2: Ord+Abelian+'static>(self, name: &str, logic: L) -> Collection<'scope, T, (K, V2), R2>
757 where L: FnMut(&K, &[(&V, R)], &mut Vec<(V2, R2)>)+'static {
758 use crate::trace::implementations::{ValBuilder, ValSpine};
759
760 self.arrange_by_key_named(&format!("Arrange: {}", name))
761 .reduce_abelian::<_,ValBuilder<_,_,_,_>,ValSpine<K,V2,_,_>,_,_>(
762 name,
763 logic,
764 |vec, key, upds| { vec.clear(); vec.extend(upds.drain(..).map(|(v,t,r)| ((key.clone(), v),t,r))); },
765 )
766 .as_collection(|k,v| (k.clone(), v.clone()))
767 }
768
769 /// Applies `reduce` to arranged data, and returns an arrangement of output data.
770 ///
771 /// This method is used by the more ergonomic `reduce`, `distinct`, and `count` methods, although
772 /// it can be very useful if one needs to manually attach and re-use existing arranged collections.
773 ///
774 /// # Examples
775 ///
776 /// ```
777 /// use differential_dataflow::input::Input;
778 /// use differential_dataflow::trace::Trace;
779 /// use differential_dataflow::trace::implementations::{ValBuilder, ValSpine};
780 ///
781 /// ::timely::example(|scope| {
782 ///
783 /// let trace =
784 /// scope.new_collection_from(1 .. 10u32).1
785 /// .map(|x| (x, x))
786 /// .reduce_abelian::<_,ValBuilder<_,_,_,_>,ValSpine<_,_,_,_>>(
787 /// "Example",
788 /// move |_key, src, dst| dst.push((*src[0].0, 1))
789 /// )
790 /// .trace;
791 /// });
792 /// ```
793 pub fn reduce_abelian<L, Bu, T2>(self, name: &str, mut logic: L) -> Arranged<'scope, TraceAgent<T2>>
794 where
795 T2: Trace<Batch: Navigable, Time=T>+'static,
796 for<'a> BatchCursor<T2>: Cursor<Key<'a>= &'a K, ValOwn = V, Time = T2::Time, Diff: Abelian>,
797 Bu: Builder<Time=T2::Time, Input = Vec<((K, V), T2::Time, BatchDiff<T2>)>, Output: Into<T2::Batch>> + 'static,
798 L: FnMut(&K, &[(&V, R)], &mut Vec<(V, BatchDiff<T2>)>)+'static,
799 {
800 self.reduce_core::<_,Bu,T2>(name, move |key, input, output, change| {
801 if !input.is_empty() { logic(key, input, change); }
802 change.extend(output.drain(..).map(|(x,mut d)| { d.negate(); (x, d) }));
803 crate::consolidation::consolidate(change);
804 })
805 }
806
807 /// Solves for output updates when presented with inputs and would-be outputs.
808 ///
809 /// Unlike `reduce_arranged`, this method may be called with an empty `input`,
810 /// and it may not be safe to index into the first element.
811 /// At least one of the two collections will be non-empty.
812 pub fn reduce_core<L, Bu, T2>(self, name: &str, logic: L) -> Arranged<'scope, TraceAgent<T2>>
813 where
814 V: Clone+'static,
815 T2: Trace<Batch: Navigable, Time=T>+'static,
816 for<'a> BatchCursor<T2>: Cursor<Key<'a>=&'a K, ValOwn = V, Time = T2::Time>,
817 Bu: Builder<Time=T2::Time, Input = Vec<((K, V), T2::Time, BatchDiff<T2>)>, Output: Into<T2::Batch>> + 'static,
818 L: FnMut(&K, &[(&V, R)], &mut Vec<(V,BatchDiff<T2>)>, &mut Vec<(V, BatchDiff<T2>)>)+'static,
819 {
820 self.arrange_by_key_named(&format!("Arrange: {}", name))
821 .reduce_core::<_,Bu,_,_,_>(
822 name,
823 logic,
824 |vec, key, upds| { vec.clear(); vec.extend(upds.drain(..).map(|(v,t,r)| ((key.clone(), v),t,r))); },
825 )
826 }
827 }
828
829 impl<'scope, T, K, R1> Collection<'scope, T, K, R1>
830 where
831 T: Timestamp + Lattice + Ord,
832 K: crate::ExchangeData+Hashable,
833 R1: crate::ExchangeData+Semigroup
834 {
835
836 /// Reduces the collection to one occurrence of each distinct element.
837 ///
838 /// # Examples
839 ///
840 /// ```
841 /// use differential_dataflow::input::Input;
842 ///
843 /// ::timely::example(|scope| {
844 /// // report at most one of each key.
845 /// scope.new_collection_from(1 .. 10).1
846 /// .map(|x| x / 3)
847 /// .distinct();
848 /// });
849 /// ```
850 pub fn distinct(self) -> Collection<'scope, T, K, isize> {
851 self.distinct_core()
852 }
853
854 /// Distinct for general integer differences.
855 ///
856 /// This method allows `distinct` to produce collections whose difference
857 /// type is something other than an `isize` integer, for example perhaps an
858 /// `i32`.
859 pub fn distinct_core<R2: Ord+Abelian+'static+From<i8>>(self) -> Collection<'scope, T, K, R2> {
860 self.threshold_named("Distinct", |_,_| R2::from(1i8))
861 }
862
863 /// Transforms the multiplicity of records.
864 ///
865 /// The `threshold` function is obliged to map `R1::zero` to `R2::zero`, or at
866 /// least the computation may behave as if it does. Otherwise, the transformation
867 /// can be nearly arbitrary: the code does not assume any properties of `threshold`.
868 ///
869 /// # Examples
870 ///
871 /// ```
872 /// use differential_dataflow::input::Input;
873 ///
874 /// ::timely::example(|scope| {
875 /// // report at most one of each key.
876 /// scope.new_collection_from(1 .. 10).1
877 /// .map(|x| x / 3)
878 /// .threshold(|_,c| c % 2);
879 /// });
880 /// ```
881 pub fn threshold<R2: Ord+Abelian+'static, F: FnMut(&K, &R1)->R2+'static>(self, thresh: F) -> Collection<'scope, T, K, R2> {
882 self.threshold_named("Threshold", thresh)
883 }
884
885 /// A `threshold` with the ability to name the operator.
886 pub fn threshold_named<R2: Ord+Abelian+'static, F: FnMut(&K,&R1)->R2+'static>(self, name: &str, mut thresh: F) -> Collection<'scope, T, K, R2> {
887 use crate::trace::implementations::{KeyBuilder, KeySpine};
888
889 self.arrange_by_self_named(&format!("Arrange: {}", name))
890 .reduce_abelian::<_,KeyBuilder<K,T,R2>,KeySpine<K,T,R2>,_,_>(
891 name,
892 move |k,s,t| t.push(((), thresh(k, &s[0].1))),
893 |vec, key, upds| { vec.clear(); vec.extend(upds.drain(..).map(|(v,t,r)| ((key.clone(), v),t,r))); },
894 )
895 .as_collection(|k,_| k.clone())
896 }
897
898 }
899
900 impl<'scope, T, K, R> Collection<'scope, T, K, R>
901 where
902 T: Timestamp + Lattice + Ord,
903 K: crate::ExchangeData+Hashable,
904 R: crate::ExchangeData+Semigroup
905 {
906
907 /// Counts the number of occurrences of each element.
908 ///
909 /// # Examples
910 ///
911 /// ```
912 /// use differential_dataflow::input::Input;
913 ///
914 /// ::timely::example(|scope| {
915 /// // report the number of occurrences of each key
916 /// scope.new_collection_from(1 .. 10).1
917 /// .map(|x| x / 3)
918 /// .count();
919 /// });
920 /// ```
921 pub fn count(self) -> Collection<'scope, T, (K, R), isize> { self.count_core() }
922
923 /// Count for general integer differences.
924 ///
925 /// This method allows `count` to produce collections whose difference
926 /// type is something other than an `isize` integer, for example perhaps an
927 /// `i32`.
928 pub fn count_core<R2: Ord + Abelian + From<i8> + 'static>(self) -> Collection<'scope, T, (K, R), R2> {
929 use crate::trace::implementations::{ValBuilder, ValSpine};
930 self.arrange_by_self_named("Arrange: Count")
931 .reduce_abelian::<_,ValBuilder<K,R,T,R2>,ValSpine<K,R,T,R2>,_,_>(
932 "Count",
933 |_k,s,t| t.push((s[0].1.clone(), R2::from(1i8))),
934 |vec, key, upds| { vec.clear(); vec.extend(upds.drain(..).map(|(v,t,r)| ((key.clone(), v),t,r))); },
935 )
936 .as_collection(|k,c| (k.clone(), c.clone()))
937 }
938 }
939
940 /// Methods which require data be arrangeable.
941 impl<'scope, T, D, R> Collection<'scope, T, D, R>
942 where
943 T: Timestamp + Clone + 'static + Lattice,
944 D: crate::ExchangeData+Hashable,
945 R: crate::ExchangeData+Semigroup,
946 {
947 /// Aggregates the weights of equal records into at most one record.
948 ///
949 /// This method uses the type `D`'s `hashed()` method to partition the data. The data are
950 /// accumulated in place, each held back until their timestamp has completed.
951 ///
952 /// # Examples
953 ///
954 /// ```
955 /// use differential_dataflow::input::Input;
956 ///
957 /// ::timely::example(|scope| {
958 ///
959 /// let x = scope.new_collection_from(1 .. 10u32).1;
960 ///
961 /// x.clone()
962 /// .negate()
963 /// .concat(x)
964 /// .consolidate() // <-- ensures cancellation occurs
965 /// .assert_empty();
966 /// });
967 /// ```
968 pub fn consolidate(self) -> Self {
969 use crate::trace::implementations::{KeyBatcher, KeySpine};
970 self.consolidate_named::<_, KeySpine<_,_,_>,_>("Consolidate", KeyBatcher::new, |key,&()| key.clone())
971 }
972
973 /// As `consolidate` but with the ability to name the operator, specify the trace type,
974 /// and provide the function `reify` to produce owned keys and values..
975 pub fn consolidate_named<Ba, Tr, F>(self, name: &str, batcher: impl FnOnce(Option<crate::logging::Logger>, usize) -> Ba, reify: F) -> Self
976 where
977 Ba: crate::batcher::Batcher<Vec<((D, ()), T, R)>, Time = T, Output: Into<Tr::Batch>> + 'static,
978 Tr: crate::trace::Trace<Batch: Navigable, Time=T>+'static,
979 for<'a> BatchCursor<Tr>: Cursor<Time=Tr::Time, Diff=R>,
980 F: Fn(BatchKey<'_, Tr>, BatchVal<'_, Tr>) -> D + 'static,
981 {
982 self.map(|k| (k, ()))
983 .arrange_named::<Ba, Tr>(name, batcher)
984 .as_collection(reify)
985 }
986
987 /// Aggregates the weights of equal records.
988 ///
989 /// Unlike `consolidate`, this method does not exchange data and does not
990 /// ensure that at most one copy of each `(data, time)` pair exists in the
991 /// results. Instead, it acts on each batch of data and collapses equivalent
992 /// `(data, time)` pairs found therein, suppressing any that accumulate to
993 /// zero.
994 ///
995 /// # Examples
996 ///
997 /// ```
998 /// use differential_dataflow::input::Input;
999 ///
1000 /// ::timely::example(|scope| {
1001 ///
1002 /// let x = scope.new_collection_from(1 .. 10u32).1;
1003 ///
1004 /// // nothing to assert, as no particular guarantees.
1005 /// x.clone()
1006 /// .negate()
1007 /// .concat(x)
1008 /// .consolidate_stream();
1009 /// });
1010 /// ```
1011 pub fn consolidate_stream(self) -> Self {
1012
1013 use timely::dataflow::channels::pact::Pipeline;
1014 use timely::dataflow::operators::Operator;
1015 use crate::collection::AsCollection;
1016 use crate::consolidation::ConsolidatingContainerBuilder;
1017
1018 self.inner
1019 .unary::<ConsolidatingContainerBuilder<_>, _, _, _>(Pipeline, "ConsolidateStream", |_cap, _info| {
1020
1021 move |input, output| {
1022 input.for_each(|time, data| {
1023 output.session_with_builder(&time).give_iterator(data.drain(..));
1024 })
1025 }
1026 })
1027 .as_collection()
1028 }
1029 }
1030
1031 use crate::trace::implementations::{ValSpine, ValBatcher};
1032 use crate::trace::implementations::{KeySpine, KeyBatcher};
1033 impl<'scope, T, K, V, R> Collection<'scope, T, (K, V), R>
1034 where
1035 T: Timestamp + Lattice,
1036 K: crate::ExchangeData + Hashable,
1037 V: crate::ExchangeData,
1038 R: crate::ExchangeData + Semigroup,
1039 {
1040 /// Arranges updates into a shared trace, exchanged by a hash of the key.
1041 ///
1042 /// The batcher must accept the stream container; the default chunker only consolidates
1043 /// same-type containers. For chunker setups that convert between container types (e.g.
1044 /// columnar layouts), call [`arrange_core`](crate::operators::arrange::arrangement::arrange_core)
1045 /// directly.
1046 pub fn arrange<Ba, Tr>(self, batcher: impl FnOnce(Option<crate::logging::Logger>, usize) -> Ba) -> Arranged<'scope, TraceAgent<Tr>>
1047 where
1048 Ba: crate::batcher::Batcher<Vec<((K, V), T, R)>, Time = T, Output: Into<Tr::Batch>> + 'static,
1049 Tr: crate::trace::Trace<Time=T> + 'static,
1050 {
1051 self.arrange_named::<Ba, Tr>("Arrange", batcher)
1052 }
1053
1054 /// As [`Collection::arrange`] but with the ability to name the operator.
1055 pub fn arrange_named<Ba, Tr>(self, name: &str, batcher: impl FnOnce(Option<crate::logging::Logger>, usize) -> Ba) -> Arranged<'scope, TraceAgent<Tr>>
1056 where
1057 Ba: crate::batcher::Batcher<Vec<((K, V), T, R)>, Time = T, Output: Into<Tr::Batch>> + 'static,
1058 Tr: crate::trace::Trace<Time=T> + 'static,
1059 {
1060 let exchange = timely::dataflow::channels::pact::Exchange::new(move |update: &((K,V),T,R)| (update.0).0.hashed().into());
1061 crate::operators::arrange::arrangement::arrange_core::<_, _, Ba, _>(self.inner, exchange, name, batcher)
1062 }
1063 }
1064
1065 impl<'scope, T, K: crate::ExchangeData+Hashable, V: crate::ExchangeData, R: crate::ExchangeData+Semigroup> Collection<'scope, T, (K,V), R>
1066 where
1067 T: Timestamp + Lattice + Ord,
1068 {
1069 /// Arranges a collection of `(Key, Val)` records by `Key`.
1070 ///
1071 /// This operator arranges a stream of values into a shared trace, whose contents it maintains.
1072 /// This trace is current for all times completed by the output stream, which can be used to
1073 /// safely identify the stable times and values in the trace.
1074 pub fn arrange_by_key(self) -> Arranged<'scope, TraceAgent<ValSpine<K, V, T, R>>> {
1075 self.arrange_by_key_named("ArrangeByKey")
1076 }
1077
1078 /// As `arrange_by_key` but with the ability to name the arrangement.
1079 pub fn arrange_by_key_named(self, name: &str) -> Arranged<'scope, TraceAgent<ValSpine<K, V, T, R>>> {
1080 self.arrange_named::<_,_>(name, ValBatcher::new)
1081 }
1082 }
1083
1084 impl<'scope, T, K: crate::ExchangeData+Hashable, R: crate::ExchangeData+Semigroup> Collection<'scope, T, K, R>
1085 where
1086 T: Timestamp + Lattice + Ord,
1087 {
1088 /// Arranges a collection of `Key` records by `Key`.
1089 ///
1090 /// This operator arranges a collection of records into a shared trace, whose contents it maintains.
1091 /// This trace is current for all times complete in the output stream, which can be used to safely
1092 /// identify the stable times and values in the trace.
1093 pub fn arrange_by_self(self) -> Arranged<'scope, TraceAgent<KeySpine<K, T, R>>> {
1094 self.arrange_by_self_named("ArrangeBySelf")
1095 }
1096
1097 /// As `arrange_by_self` but with the ability to name the arrangement.
1098 pub fn arrange_by_self_named(self, name: &str) -> Arranged<'scope, TraceAgent<KeySpine<K, T, R>>> {
1099 self.map(|k| (k, ()))
1100 .arrange_named::<_,_>(name, KeyBatcher::new)
1101 }
1102 }
1103
1104 impl<'scope, T, K, V, R> Collection<'scope, T, (K, V), R>
1105 where
1106 T: Timestamp + Lattice + Ord,
1107 K: crate::ExchangeData+Hashable,
1108 V: crate::ExchangeData,
1109 R: crate::ExchangeData+Semigroup,
1110 {
1111 /// Matches pairs `(key,val1)` and `(key,val2)` based on `key` and yields pairs `(key, (val1, val2))`.
1112 ///
1113 /// The [`join_map`](Collection::join_map) method may be more convenient for non-trivial processing pipelines.
1114 ///
1115 /// # Examples
1116 ///
1117 /// ```
1118 /// use differential_dataflow::input::Input;
1119 ///
1120 /// ::timely::example(|scope| {
1121 ///
1122 /// let x = scope.new_collection_from(vec![(0, 1), (1, 3)]).1;
1123 /// let y = scope.new_collection_from(vec![(0, 'a'), (1, 'b')]).1;
1124 /// let z = scope.new_collection_from(vec![(0, (1, 'a')), (1, (3, 'b'))]).1;
1125 ///
1126 /// x.join(y)
1127 /// .assert_eq(z);
1128 /// });
1129 /// ```
1130 pub fn join<V2, R2>(self, other: Collection<'scope, T, (K,V2), R2>) -> Collection<'scope, T, (K,(V,V2)), <R as Multiply<R2>>::Output>
1131 where
1132 K: crate::ExchangeData,
1133 V2: crate::ExchangeData,
1134 R2: crate::ExchangeData+Semigroup,
1135 R: Multiply<R2, Output: Semigroup+'static>,
1136 {
1137 self.join_map(other, |k,v,v2| (k.clone(),(v.clone(),v2.clone())))
1138 }
1139
1140 /// Matches pairs `(key,val1)` and `(key,val2)` based on `key` and then applies a function.
1141 ///
1142 /// # Examples
1143 ///
1144 /// ```
1145 /// use differential_dataflow::input::Input;
1146 ///
1147 /// ::timely::example(|scope| {
1148 ///
1149 /// let x = scope.new_collection_from(vec![(0, 1), (1, 3)]).1;
1150 /// let y = scope.new_collection_from(vec![(0, 'a'), (1, 'b')]).1;
1151 /// let z = scope.new_collection_from(vec![(1, 'a'), (3, 'b')]).1;
1152 ///
1153 /// x.join_map(y, |_key, &a, &b| (a,b))
1154 /// .assert_eq(z);
1155 /// });
1156 /// ```
1157 pub fn join_map<V2: crate::ExchangeData, R2: crate::ExchangeData+Semigroup, D: crate::Data, L>(self, other: Collection<'scope, T, (K, V2), R2>, mut logic: L) -> Collection<'scope, T, D, <R as Multiply<R2>>::Output>
1158 where R: Multiply<R2, Output: Semigroup+'static>, L: FnMut(&K, &V, &V2)->D+'static {
1159 let arranged1 = self.arrange_by_key();
1160 let arranged2 = other.arrange_by_key();
1161 arranged1.join_core(arranged2, move |k,v1,v2| Some(logic(k,v1,v2)))
1162 }
1163
1164 /// Matches pairs `(key, val)` and `key` based on `key`, producing the former with frequencies multiplied.
1165 ///
1166 /// When the second collection contains frequencies that are either zero or one this is the more traditional
1167 /// relational semijoin. When the second collection may contain multiplicities, this operation may scale up
1168 /// the counts of the records in the first input.
1169 ///
1170 /// # Examples
1171 ///
1172 /// ```
1173 /// use differential_dataflow::input::Input;
1174 ///
1175 /// ::timely::example(|scope| {
1176 ///
1177 /// let x = scope.new_collection_from(vec![(0, 1), (1, 3)]).1;
1178 /// let y = scope.new_collection_from(vec![0, 2]).1;
1179 /// let z = scope.new_collection_from(vec![(0, 1)]).1;
1180 ///
1181 /// x.semijoin(y)
1182 /// .assert_eq(z);
1183 /// });
1184 /// ```
1185 pub fn semijoin<R2: crate::ExchangeData+Semigroup>(self, other: Collection<'scope, T, K, R2>) -> Collection<'scope, T, (K, V), <R as Multiply<R2>>::Output>
1186 where R: Multiply<R2, Output: Semigroup+'static> {
1187 let arranged1 = self.arrange_by_key();
1188 let arranged2 = other.arrange_by_self();
1189 arranged1.join_core(arranged2, |k,v,_| Some((k.clone(), v.clone())))
1190 }
1191
1192 /// Subtracts the semijoin with `other` from `self`.
1193 ///
1194 /// In the case that `other` has multiplicities zero or one this results
1195 /// in a relational antijoin, in which we discard input records whose key
1196 /// is present in `other`. If the multiplicities could be other than zero
1197 /// or one, the semantic interpretation of this operator is less clear.
1198 ///
1199 /// In almost all cases, you should ensure that `other` has multiplicities
1200 /// that are zero or one, perhaps by using the `distinct` operator.
1201 ///
1202 /// # Examples
1203 ///
1204 /// ```
1205 /// use differential_dataflow::input::Input;
1206 ///
1207 /// ::timely::example(|scope| {
1208 ///
1209 /// let x = scope.new_collection_from(vec![(0, 1), (1, 3)]).1;
1210 /// let y = scope.new_collection_from(vec![0, 2]).1;
1211 /// let z = scope.new_collection_from(vec![(1, 3)]).1;
1212 ///
1213 /// x.antijoin(y)
1214 /// .assert_eq(z);
1215 /// });
1216 /// ```
1217 pub fn antijoin<R2: crate::ExchangeData+Semigroup>(self, other: Collection<'scope, T, K, R2>) -> Collection<'scope, T, (K, V), R>
1218 where R: Multiply<R2, Output=R>, R: Abelian+'static {
1219 self.clone().concat(self.semijoin(other).negate())
1220 }
1221
1222 /// Joins two arranged collections with the same key type.
1223 ///
1224 /// Each matching pair of records `(key, val1)` and `(key, val2)` are subjected to the `result` function,
1225 /// which produces something implementing `IntoIterator`, where the output collection will have an entry for
1226 /// every value returned by the iterator.
1227 ///
1228 /// # Examples
1229 ///
1230 /// ```
1231 /// use differential_dataflow::input::Input;
1232 /// use differential_dataflow::trace::Trace;
1233 ///
1234 /// ::timely::example(|scope| {
1235 ///
1236 /// let x = scope.new_collection_from(vec![(0u32, 1), (1, 3)]).1
1237 /// .arrange_by_key();
1238 /// let y = scope.new_collection_from(vec![(0, 'a'), (1, 'b')]).1
1239 /// .arrange_by_key();
1240 ///
1241 /// let z = scope.new_collection_from(vec![(1, 'a'), (3, 'b')]).1;
1242 ///
1243 /// x.join_core(y, |_key, &a, &b| Some((a, b)))
1244 /// .assert_eq(z);
1245 /// });
1246 /// ```
1247 pub fn join_core<Tr2,I,L,R2> (self, stream2: Arranged<'scope, Tr2>, result: L) -> Collection<'scope, T,I::Item,<R as Multiply<R2>>::Output>
1248 where
1249 Tr2: crate::trace::TraceReader<Batch: Navigable, Time=T>+Clone+'static,
1250 for<'a> BatchCursor<Tr2>: Cursor<Key<'a>=&'a K>,
1251 // Pin the cursor diff to a named param `R2`: a `Multiply` bound on a projection does not
1252 // connect to its use-site (the solver normalizes the use but not the bound's subject).
1253 BatchCursor<Tr2>: Cursor<Diff = R2, Time = T>,
1254 R: Multiply<R2, Output: Semigroup+'static>,
1255 I: IntoIterator<Item: crate::Data>,
1256 L: FnMut(&K,&V,BatchVal<'_, Tr2>)->I+'static,
1257 {
1258 self.arrange_by_key()
1259 .join_core(stream2, result)
1260 }
1261 }
1262}
1263
1264/// Conversion to a differential dataflow Collection.
1265pub trait AsCollection<'scope, T: Timestamp, C> {
1266 /// Converts the type to a differential dataflow collection.
1267 fn as_collection(self) -> Collection<'scope, T, C>;
1268}
1269
1270impl<'scope, T: Timestamp, C> AsCollection<'scope, T, C> for Stream<'scope, T, C> {
1271 /// Converts the type to a differential dataflow collection.
1272 ///
1273 /// By calling this method, you guarantee that the timestamp invariant (as documented on
1274 /// [Collection]) is upheld. This method will not check it.
1275 fn as_collection(self) -> Collection<'scope, T, C> {
1276 Collection::<T,C>::new(self)
1277 }
1278}
1279
1280/// Concatenates multiple collections.
1281///
1282/// This method has the effect of a sequence of calls to `concat`, but it does
1283/// so in one operator rather than a chain of many operators.
1284///
1285/// # Examples
1286///
1287/// ```
1288/// use differential_dataflow::input::Input;
1289///
1290/// ::timely::example(|scope| {
1291///
1292/// let data = scope.new_collection_from(1 .. 10).1;
1293///
1294/// let odds = data.clone().filter(|x| x % 2 == 1);
1295/// let evens = data.clone().filter(|x| x % 2 == 0);
1296///
1297/// differential_dataflow::collection::concatenate(scope, vec![odds, evens])
1298/// .assert_eq(data);
1299/// });
1300/// ```
1301pub fn concatenate<'scope, T, C, I>(scope: Scope<'scope, T>, iterator: I) -> Collection<'scope, T, C>
1302where
1303 T: Timestamp,
1304 C: Container,
1305 I: IntoIterator<Item=Collection<'scope, T, C>>,
1306{
1307 scope
1308 .concatenate(iterator.into_iter().map(|x| x.inner))
1309 .as_collection()
1310}
1311
1312/// Traits that can be implemented by containers to provide functionality to collections based on them.
1313pub mod containers {
1314
1315 /// A container that can negate its updates.
1316 pub trait Negate {
1317 /// Negates Abelian differences of each update.
1318 fn negate(self) -> Self;
1319 }
1320
1321 /// A container that can enter from timestamp `T1` into timestamp `T2`.
1322 pub trait Enter<T1, T2> {
1323 /// The resulting container type.
1324 type InnerContainer;
1325 /// Update timestamps from `T1` to `T2`.
1326 fn enter(self) -> Self::InnerContainer;
1327 }
1328
1329 /// A container that can leave from timestamp `T1` into timestamp `T2`.
1330 pub trait Leave<T1, T2> {
1331 /// The resulting container type.
1332 type OuterContainer;
1333 /// Update timestamps from `T1` to `T2`.
1334 fn leave(self) -> Self::OuterContainer;
1335 }
1336
1337 /// A container that can advance timestamps by a summary `TS`.
1338 pub trait ResultsIn<TS> {
1339 /// Advance times in the container by `step`.
1340 fn results_in(self, step: &TS) -> Self;
1341 }
1342
1343
1344 /// Implementations of container traits for the `Vec` container.
1345 mod vec {
1346
1347 use timely::progress::{Timestamp, timestamp::Refines};
1348 use crate::collection::Abelian;
1349
1350 use super::{Negate, Enter, Leave, ResultsIn};
1351
1352 impl<D, T, R: Abelian> Negate for Vec<(D, T, R)> {
1353 fn negate(mut self) -> Self {
1354 for (_data, _time, diff) in self.iter_mut() { diff.negate(); }
1355 self
1356 }
1357 }
1358
1359 impl<D, T1: Timestamp, T2: Refines<T1>, R> Enter<T1, T2> for Vec<(D, T1, R)> {
1360 type InnerContainer = Vec<(D, T2, R)>;
1361 fn enter(self) -> Self::InnerContainer {
1362 self.into_iter().map(|(d,t1,r)| (d,T2::to_inner(t1),r)).collect()
1363 }
1364 }
1365
1366 impl<D, T1: Refines<T2>, T2: Timestamp, R> Leave<T1, T2> for Vec<(D, T1, R)> {
1367 type OuterContainer = Vec<(D, T2, R)>;
1368 fn leave(self) -> Self::OuterContainer {
1369 self.into_iter().map(|(d,t1,r)| (d,t1.to_outer(),r)).collect()
1370 }
1371 }
1372
1373 impl<D, T: Timestamp, R> ResultsIn<T::Summary> for Vec<(D, T, R)> {
1374 fn results_in(self, step: &T::Summary) -> Self {
1375 use timely::progress::PathSummary;
1376 self.into_iter().filter_map(move |(d,t,r)| step.results_in(&t).map(|t| (d,t,r))).collect()
1377 }
1378 }
1379 }
1380
1381 /// Implementations of container traits for the `Rc` container.
1382 mod rc {
1383 use std::rc::Rc;
1384
1385 use timely::progress::{Timestamp, timestamp::Refines};
1386
1387 use super::{Negate, Enter, Leave, ResultsIn};
1388
1389 impl<C: Negate+Clone+Default> Negate for Rc<C> {
1390 fn negate(mut self) -> Self {
1391 std::mem::take(Rc::make_mut(&mut self)).negate().into()
1392 }
1393 }
1394
1395 impl<C: Enter<T1, T2>+Clone+Default, T1: Timestamp, T2: Refines<T1>> Enter<T1, T2> for Rc<C> {
1396 type InnerContainer = Rc<C::InnerContainer>;
1397 fn enter(mut self) -> Self::InnerContainer {
1398 std::mem::take(Rc::make_mut(&mut self)).enter().into()
1399 }
1400 }
1401
1402 impl<C: Leave<T1, T2>+Clone+Default, T1: Refines<T2>, T2: Timestamp> Leave<T1, T2> for Rc<C> {
1403 type OuterContainer = Rc<C::OuterContainer>;
1404 fn leave(mut self) -> Self::OuterContainer {
1405 std::mem::take(Rc::make_mut(&mut self)).leave().into()
1406 }
1407 }
1408
1409 impl<C: ResultsIn<TS>+Clone+Default, TS> ResultsIn<TS> for Rc<C> {
1410 fn results_in(mut self, step: &TS) -> Self {
1411 std::mem::take(Rc::make_mut(&mut self)).results_in(step).into()
1412 }
1413 }
1414 }
1415}