Skip to content

Update mdbook to Timely 0.11.1 and Differential 0.11.0 #267

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions mdbook/src/chapter_0/chapter_0_0.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ Instead, edit your `Cargo.toml` file, which tells Rust about your dependencies,
authors = ["Your Name <[email protected]>"]

[dependencies]
timely = "0.7"
differential-dataflow = "0.7"
timely = "0.11.1"
differential-dataflow = "0.11.0"
Echidnatron%

You should only need to add those last two lines there, which bring in dependencies on both [timely dataflow](https://github.com/TimelyDataflow/timely-dataflow) and [differential dataflow](https://github.com/TimelyDataflow/differential-dataflow). We will be using both of those.
Expand Down
24 changes: 12 additions & 12 deletions mdbook/src/chapter_0/chapter_0_1.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ If you are following along at home, put this in your `src/main.rs` file.
// create a new collection from our input.
let manages = input.to_collection(scope);

// if (m2, m1) and (m1, p), then output (m1, m2, p)
// if (m2, m1) and (m1, p), then output (m1, (m2, p))
manages
.map(|(m2, m1)| (m1, m2))
.join(&manages)
Expand All @@ -50,7 +50,7 @@ If you are following along at home, put this in your `src/main.rs` file.
This program has a bit of boilerplate, but at its heart it defines a new input `manages` and then joins it with itself, once the fields have been re-ordered. The intent is as stated in the comment:

```rust,no_run
// if (m2, m1) and (m1, p), then output (m1, m2, p)
// if (m2, m1) and (m1, p), then output (m1, (m2, p))
```

We want to report each pair `(m2, p)`, and we happen to also produce as evidence the `m1` connecting them.
Expand All @@ -59,16 +59,16 @@ When we execute this program we get to see the skip-level reports for the small

Echidnatron% cargo run -- 10
Running `target/debug/my_project`
((0, 0, 0), (Root, 0), 1)
((0, 0, 1), (Root, 0), 1)
((1, 0, 2), (Root, 0), 1)
((1, 0, 3), (Root, 0), 1)
((2, 1, 4), (Root, 0), 1)
((2, 1, 5), (Root, 0), 1)
((3, 1, 6), (Root, 0), 1)
((3, 1, 7), (Root, 0), 1)
((4, 2, 8), (Root, 0), 1)
((4, 2, 9), (Root, 0), 1)
((0, (0, 0)), 0, 1)
((0, (0, 1)), 0, 1)
((1, (0, 2)), 0, 1)
((1, (0, 3)), 0, 1)
((2, (1, 4)), 0, 1)
((2, (1, 5)), 0, 1)
((3, (1, 6)), 0, 1)
((3, (1, 7)), 0, 1)
((4, (2, 8)), 0, 1)
((4, (2, 9)), 0, 1)
Echidnatron%

This is a bit crazy, but what we are seeing is many triples of the form
Expand Down
90 changes: 45 additions & 45 deletions mdbook/src/chapter_0/chapter_0_2.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,65 +35,65 @@ We do this for each of the non-boss employees and get to see a bunch of outputs.

Echidnatron% cargo run -- 10
Running `target/debug/my_project`
((0, 0, 0), (Root, 0), 1)
((0, 0, 1), (Root, 0), 1)
((0, 0, 2), (Root, 2), 1)
((1, 0, 2), (Root, 0), 1)
((1, 0, 2), (Root, 2), -1)
((1, 0, 3), (Root, 0), 1)
((1, 0, 4), (Root, 4), 1)
((1, 0, 5), (Root, 5), 1)
((2, 0, 4), (Root, 2), 1)
((2, 0, 4), (Root, 4), -1)
((2, 0, 5), (Root, 2), 1)
((2, 0, 5), (Root, 5), -1)
((2, 0, 6), (Root, 6), 1)
((2, 0, 7), (Root, 7), 1)
((2, 0, 8), (Root, 8), 1)
((2, 1, 4), (Root, 0), 1)
((2, 1, 4), (Root, 2), -1)
((2, 1, 5), (Root, 0), 1)
((2, 1, 5), (Root, 2), -1)
((3, 1, 6), (Root, 0), 1)
((3, 1, 6), (Root, 6), -1)
((3, 1, 7), (Root, 0), 1)
((3, 1, 7), (Root, 7), -1)
((3, 1, 9), (Root, 9), 1)
((4, 1, 8), (Root, 4), 1)
((4, 1, 8), (Root, 8), -1)
((4, 1, 9), (Root, 4), 1)
((4, 1, 9), (Root, 9), -1)
((4, 2, 8), (Root, 0), 1)
((4, 2, 8), (Root, 4), -1)
((4, 2, 9), (Root, 0), 1)
((4, 2, 9), (Root, 4), -1)
((0, (0, 0)), 0, 1)
((0, (0, 1)), 0, 1)
((0, (0, 2)), 2, 1)
((1, (0, 2)), 0, 1)
((1, (0, 2)), 2, -1)
((1, (0, 3)), 0, 1)
((1, (0, 4)), 4, 1)
((1, (0, 5)), 5, 1)
((2, (0, 4)), 2, 1)
((2, (0, 4)), 4, -1)
((2, (0, 5)), 2, 1)
((2, (0, 5)), 5, -1)
((2, (0, 6)), 6, 1)
((2, (0, 7)), 7, 1)
((2, (0, 8)), 8, 1)
((2, (1, 4)), 0, 1)
((2, (1, 4)), 2, -1)
((2, (1, 5)), 0, 1)
((2, (1, 5)), 2, -1)
((3, (1, 6)), 0, 1)
((3, (1, 6)), 6, -1)
((3, (1, 7)), 0, 1)
((3, (1, 7)), 7, -1)
((3, (1, 9)), 9, 1)
((4, (1, 8)), 4, 1)
((4, (1, 8)), 8, -1)
((4, (1, 9)), 4, 1)
((4, (1, 9)), 9, -1)
((4, (2, 8)), 0, 1)
((4, (2, 8)), 4, -1)
((4, (2, 9)), 0, 1)
((4, (2, 9)), 4, -1)
Echidnatron%

Gaaaaaaah! What in the !#$!?

It turns out our input changes result in output changes. Let's try and break this down and make some sense. If we group the columns by time, those `(Root, _)` fields, we see a bit more structure.
It turns out our input changes result in output changes. Let's try and break this down and make some sense. If we group the columns by time, the second element of the tuples, we see a bit more structure.

1. The `(Root, 0)` entries are exactly the same as for our prior computation, where we just loaded the data.
1. The entries with time `0` are exactly the same as for our prior computation, where we just loaded the data.

2. There aren't any `(Root, 1)` entries (go check). That is because the input didn't change in our first step, because 1/2 == 1/3 == 0. Since the input didn't change, the output doesn't change.
2. There aren't any entries at time `1` (go check). That is because the input didn't change in our first step, because 1/2 == 1/3 == 0. Since the input didn't change, the output doesn't change.

3. The other times are more complicated.

Let's look at times `(Root, 4)`.
Let's look at the entries for time `4`.

((1, 0, 4), (Root, 4), 1)
((2, 0, 4), (Root, 4), -1)
((4, 1, 8), (Root, 4), 1)
((4, 1, 9), (Root, 4), 1)
((4, 2, 8), (Root, 4), -1)
((4, 2, 9), (Root, 4), -1)
((1, (0, 4)), 4, 1)
((2, (0, 4)), 4, -1)
((4, (1, 8)), 4, 1)
((4, (1, 9)), 4, 1)
((4, (2, 8)), 4, -1)
((4, (2, 9)), 4, -1)

There is a bit going on here. Four's manager changed from two to one, and while their skip-level manager remained zero the explanation changed. The first two lines record this change. The next four lines record the change in the skip-level manager of four's reports, eight and nine.

At the end, `(Root, 9)`, things are a bit simpler because we have reached the employees with no reports, and so the only changes are their skip-level manager, without any implications for other people.
At the end, time `9`, things are a bit simpler because we have reached the employees with no reports, and so the only changes are their skip-level manager, without any implications for other people.

((3, 1, 9), (Root, 9), 1)
((4, 1, 9), (Root, 9), -1)
((3, (1, 9)), 9, 1)
((4, (1, 9)), 9, -1)

Oof. Well, we probably *could* have figured these things out by hand, right?

Expand Down
2 changes: 1 addition & 1 deletion mdbook/src/chapter_0/chapter_0_3.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ Instead of loading all of our changes and only waiting for the result, we can lo
// create a new collection from an input session.
let manages = input.to_collection(scope);

// if (m2, m1) and (m1, p), then output (m1, m2, p)
// if (m2, m1) and (m1, p), then output (m1, (m2, p))
manages
.map(|(m2, m1)| (m1, m2))
.join(&manages)
Expand Down
4 changes: 2 additions & 2 deletions mdbook/src/chapter_2/chapter_2_3.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ This collection likely has at most one copy of each record, unless perhaps any m

Importantly, `concat` doesn't do the hard work of ensuring that there is only one physical of each element. If we inspect the output of the `concat` above, we might see

((0,0), (Root, 0), 1)
((0,0), (Root, 0), 1)
((0, 0), 0, 1)
((0, 0), 0, 1)

Although these are two updates to the same element at the same time, `concat` is a bit lazy (read: efficient) and doesn't do the hard work until we ask it. For that, we'll need the `consolidate` operator.
6 changes: 3 additions & 3 deletions mdbook/src/chapter_2/chapter_2_4.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ As an example, if we were to inspect

we might see two copies of the same element:

((0,0), (Root, 0), 1)
((0,0), (Root, 0), 1)
((0, 0), 0, 1)
((0, 0), 0, 1)

However, by introducing `consolidate`

Expand All @@ -30,6 +30,6 @@ However, by introducing `consolidate`

we are guaranteed to see at most one `(0,0)` update at each time:

((0,0), (Root, 0), 2)
((0, 0), 0, 2)

The `consolidate` operator is mostly useful before `inspect`ing data, but it can also be important for efficiency; knowing when to spend the additional computation to consolidate the representation of your data is an advanced topic!
4 changes: 2 additions & 2 deletions mdbook/src/chapter_2/chapter_2_5.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## The Join Operator

The `join` operator takes two input collections, each of which must have records with a `(key, value)` structure, and must have the same type of `key`. For each pair of elements with matching key, one from each input, the join operator produces the output `(key, value1, value2)`.
The `join` operator takes two input collections, each of which must have records with a `(key, value)` structure, and must have the same type of `key`. For each pair of elements with matching key, one from each input, the join operator produces the output `(key, (value1, value2))`.

Our example from earlier uses a join to match up pairs `(m2, m1)` and `(m1, p)` when the `m1` is in common. To do this, we first have to switch the records in the first collection around, so that they are keyed by `m1` instead of `m2`.

Expand All @@ -11,4 +11,4 @@ Our example from earlier uses a join to match up pairs `(m2, m1)` and `(m1, p)`
.inspect(|x| println!("{:?}", x));
```

The join operator multiplies frequencies, so if a record `(key, val1)` has multiplicity five, and a matching record `(key, val2)` has multiplicity three, the output result will be `(key, val1, val2)` with multiplicity fifteen.
The join operator multiplies frequencies, so if a record `(key, val1)` has multiplicity five, and a matching record `(key, val2)` has multiplicity three, the output result will be `(key, (val1, val2))` with multiplicity fifteen.
2 changes: 1 addition & 1 deletion mdbook/src/chapter_2/chapter_2_6.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ For example, to produce for each manager their managee with the lowest identifie

// Each element of input is a `(&Value, Count)`
for index in 1 .. input.len() {
if input[min_index] > input[index].0 {
if input[min_index].0 > input[index].0 {
min_index = index;
}
}
Expand Down
6 changes: 3 additions & 3 deletions mdbook/src/chapter_2/chapter_2_7.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ As an example, we can take our `manages` relation and determine for all employee
transitive
.map(|(mk, m1)| (m1, mk))
.join(&transitive)
.map(|(m1, mk, p)| (mk, p))
.map(|(m1, (mk, p))| (mk, p))
.concat(&transitive)
.distinct()
});
Expand All @@ -34,12 +34,12 @@ In the example above, we could rewrite
manages // transitive contains (manager, person) for many hops.
.iterate(|transitive| {

let manages = manages.enter(transivite.scope());
let manages = manages.enter(transitive.scope());

transitive
.map(|(mk, m1)| (m1, mk))
.join(&manages)
.map(|(m1, mk, p)| (mk, p))
.map(|(m1, (mk, p))| (mk, p))
.concat(&manages)
.distinct()
});
Expand Down
2 changes: 1 addition & 1 deletion mdbook/src/chapter_3/chapter_3_2.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ For example, recall our example of interacting with our management computation,
// create a new collection from an input session.
let manages = input.to_collection(scope);

// if (m2, m1) and (m1, p), then output (m1, m2, p)
// if (m2, m1) and (m1, p), then output (m1, (m2, p))
manages
.map(|(m2, m1)| (m1, m2))
.join(&manages)
Expand Down
2 changes: 1 addition & 1 deletion mdbook/src/chapter_4/chapter_4_1.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Let's write this computation starting from a collection `edges`, using different
let labels = labels.enter(inner.scope());
let edges = edges.enter(inner.scope());
inner.join(&edges)
.map(|(_src,lbl,dst)| (dst,lbl))
.map(|(_src,(lbl,dst))| (dst,lbl))
.concat(&labels)
.reduce(|_dst, lbls, out| {
let min_lbl =
Expand Down
2 changes: 0 additions & 2 deletions mdbook/src/chapter_5/chapter_5_3.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

Arrangements have the additional appealing property that they can be shared not only within a dataflow, but *across* dataflows.

Imagine we take our `knows` collection from before, and want to make it available for others to use

Imagine we want to build and maintain a relatively large and continually changing collection. But we want to do this in a way that allows an arbitrary number of subsequent queries to access the collection at almost no additional cost.

The following example demonstrates going from an interactive input session (`input`) to an arrangement (`trace`) returned from the dataflow and available for use by others.
Expand Down
10 changes: 5 additions & 5 deletions mdbook/src/chapter_a/chapter_a_2.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Differential dataflow works great using multiple threads and computers. It even
For this to work out, we'll want to ask each worker to load up a fraction of the input. If we just run the same code with multiple workers, then each of the workers will run

```rust,ignore
for person in 0 .. people {
for person in 0 .. size {
input.insert((person/2, person));
}
```
Expand All @@ -16,7 +16,7 @@ Instead, each timely dataflow worker has methods `index()` and `peers()`, which

```rust,ignore
let mut person = worker.index();
while person < people {
while person < size {
input.insert((person/2, person));
person += worker.peers();
}
Expand All @@ -25,12 +25,12 @@ Instead, each timely dataflow worker has methods `index()` and `peers()`, which
We can also make the same changes to the code that supplies the change, where each worker is responsible for those people whose number equals `worker.index()` modulo `worker.peers()`.

```rust,ignore
let mut person = index;
while person < people {
let mut person = worker.index();
while person < size {
input.remove((person/2, person));
input.insert((person/3, person));
input.advance_to(person);
person += peers;
person += worker.peers();
}
```

Expand Down
14 changes: 7 additions & 7 deletions mdbook/src/chapter_a/chapter_a_3.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Instead of loading all of our changes and only waiting for the result, we can lo
// create a new collection from an input session.
let manages = input.to_collection(scope);

// if (m2, m1) and (m1, p), then output (m1, m2, p)
// if (m2, m1) and (m1, p), then output (m1, (m2, p))
manages
.map(|(m2, m1)| (m1, m2))
.join(&manages)
Expand All @@ -22,7 +22,7 @@ We can then use this probe to limit the introduction of new data, by waiting for

```rust,ignore
let mut person = worker.index();
while person < people {
while person < size {
input.insert((person/2, person));
person += worker.peers();
}
Expand All @@ -31,7 +31,7 @@ We can then use this probe to limit the introduction of new data, by waiting for
input.advance_to(1);
input.flush();
while probe.less_than(&input.time()) { worker.step(); }
println!("{:?}\tdata loaded", timer.elapsed());
println!("{:?}\tdata loaded", worker.timer().elapsed());
```

These four new lines are each important, especially the one that prints things out. The other three do a bit of magic that get timely dataflow to work for us until we are certain that inputs have been completely processed.
Expand All @@ -40,15 +40,15 @@ We can make the same changes for the interactive loading, but we'll synchronize

```rust,ignore
// make changes, but await completion.
let mut person = 1 + index;
while person < people {
let mut person = 1 + worker.index();
while person < size {
input.remove((person/2, person));
input.insert((person/3, person));
input.advance_to(person);
input.flush();
while probe.less_than(&input.time()) { worker.step(); }
println!("{:?}\tstep {} complete", timer.elapsed(), person);
person += peers;
println!("{:?}\tstep {} complete", worker.timer().elapsed(), person);
person += worker.peers();
}
```

Expand Down