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