CS195Y Lecture 15

3/1/2017


Inductive Properties

Creating MultiTrees

Want to write a spec to allow multiple trees

Q: Why am I defining this such that trees can "re-use" a Node? E.g. such that each Node can be in multiple Trees.
A: This is much easier with Alloy so that you can simply have fewer things in your Alloy instance.

Q: Does run Everything {} just mean the same thing as run{} ?
A: This is just the name Tim has given his run statement to make it easier to keep up.

When we run our instance, it doesn't quite work yet... We haven't added our BSTree constraints.

We have to modify our BSTree to take in a Tree because it is no longer globally applicable.

We have an instance, but it's not exactly what we expect. When we project over Tree, we just get a bunch of unrelated nodes.
This is because we're actually seeing the empty instance! However, looking at the other Tree gives us something that looks like a binary tree!

If this were a piece of software, what would we do with it?

Let's start with adding a Node. As you're adding, what do you want to hold true?

Separate out the things that we are defining, and what we think this implies in our universe. E.g. do not define something like prev.tree is BSTree => post.tree is BSTree. This is actually a property that you want to hold true about your model, not a property you want to explicitly bake into your logic.

Tim is saving this as a module multitrees so that he can partition this model into discrete pieces of code.

We define a sig Descent. What does it mean to be a Descent, algorithmically something that follows a path looking for a particular value?

So, we define in our descent

all idx: path.inds | {
  path[idx].num = val implies idx = path.lastIdx
  path[idx] in Empty implies idx = path.lastIdx
  path[idx].num > val implies path[add[idx, 1]] = t.lefts[path[idx]]
  path[idx].num < val implies path[add[idx,1]] = t.rights[path[idx]]
}

Some explanations for the above code:
If the current number of the current node > the value we're looking for path[idx].num > val
In the tree that we are currently searching, the left child of our current node path[add[idx, 1]] = t.lefts[path[idx]]

This is how we have encoded our algorithm into alloy without specifying properties. We haven't said that the other Nodes must hold true or they must still be a BSTree. We've merely specified the algorithm to look for a particular value in our nodes.

We get Descents, but they are not particularly interesting... so we specify #(d.path.elems) > 2

The spec is broken! The Descent returns the Empty Node as a valid found Node because there's nothing forcing the Empty Node to be different or non-returnable.

By fixing this, Tim found an instance that works in Alloy! Does this mean it's right? Not necessarily...

To test, we define a pred:

pred uhoh {
  some d:Descent | {
      testDescentForFound[d]
      some (d.path.elems & Empty)
  }
}

This is the kind of thing you want to do if there is something about your spec that looks suspicious and you want to check.