CS195Y Lecture 12

2/26/16

Announcements:

Transitive Closure Continued

Let’s continue last week’s example of a City graph.

sig City {roads : set City}

pred canDrive[start, end]: City {
    // start->end in ^roads
}

You can compose predicated like you might want to in a standard programming language:

pred isolated[c1, c2: City] {
    not canDrive[c1, c2]
}

Q: In a previous lecture, Tim mentioned that composing predicated may be dangerous. Why was that?
A: Predicates that are used as helpers (called from another predicate) do not have their argument types enforced by Alloy.

Q: Why not type check the whole spec?
A: Tim previously asked Daniel Jackson (the creator of Alloy), and he has an example of a spec that got particuarly jumbled in this case. Good question though!

What if we want a predicate that says that every city is isolated from some other city? Hint: sentences like this should fire the neuron in your brain for quantifiers (some, all, etc)!

pred alwaysIsolateCities {
    all c: City | some c2: City |
        isolated[c, c2]
}

When does this predicate hold? You just need a graph with at least two unconnected components.

Q: Couldn’t we then replace the all with a some?
A: Yes! Once we know that some city is isolated from some other city, we know we have at least two unconnected components, so the predicate holds!

Q: What about replacing all with one?
A: Hm, that won’t work. If one city is isolated from another, by symmetry the reverse is true, so two cities are isolated from another city, so the spec will become unsatisfiable (for nontrivial graphs)! The exception is for exactly one city, since there cannot a road from a city to itself, so the city is isolated from itself.

Skolemization

Let’s talk about the funky $'s in Alloy instances!

If City5, for example, is annotated in the visualizer by something like $isolated_c2, Alloy is indicating that it has chosen that atom as the argument c2 to the predicate isolated. It’s an witness to the predicate.

Skolem depth is the number of universal quantifiers that Alloy will descend through to create these helpful annotations. Increasing the skolem depth increases the number of variables in Alloy’s runs.

We know one reason why skolemization is useful: it helps us read the visualizer and understand our instances!

Q: Why else is this useful?
A: Alloy only knows how to deal with relations and constraints over relations.

Suppose we have

some x: A | x in R

Suppose we don’t want to skolemize; we don’t want to create a relation for the quantification. How can we get rid of the quantifiers without skolemization?

If we have multiple atoms (for example, A$0, A$1, A$2), we can evaluate the expressions for each atom then or them together.

A$0 in R or A$2 in R or A$3 in R

Q: What happens when we have multiple successive quantifiers? What’s the blowup for this method of converting quantification to pure relations and constraints?
A: It’s exponential in the number of successive quantifiers. In addition, Alloy expected it’s constraints in a specific format. This is often more expensive than skolemization, because under-the-hood this process is fairly expensive.

Without skolemization, Alloy uses this method, which doesn’t increase the number of variables, but can increase the number of constraints.

Q: Does Alloy consider skolemization relations when doing symmetry breaking?
A: Yes, there’s symmetries in both the instance and in the skolemization relation. So, minimal skolem depth is good if you want to see fewer instances. If performance is a concern, though, increasing the skolem depth can often improve that.

If we make some pred nonempty:

pred nonempty[r: univ->univ] {
    some r
}

When we run this, the visualizer labels all edge as $nonempty_r. This is called higher-order quantification; which is when you quantify over sets of things. This is in contrast to first-order quantification, which is along the lines of “for all cities, …”. It turns out that there are things that you can represent using higher-order quantification that you can’t do with first order quantification alone.

Alloy will refuse to do higher-order quantification without skolemization, though, because usually doing so would require constraints on the order of 2^(#Univ).

In graph theory, there’s a term called a “clique” (#tbt high school). A clique is a subgraph that is maximally-connected, with an edge from each node in the clique to every other node in the clique. A k-clique is one with k nodes.

pred hasClique {
    some clique: set City |
        all c: clique | (clique - c) in c.roads
    
    #clique > 2
}

This is pretty complicated quantification: we’re telling Alloy to look for some set, then say something is true for all things in the set.

Running this command gives a graph of 6 cities, with a subgraph of 3 nodes that are all connected, labeled with $hasClique_clique.

Ok, now we’re going to break Alloy.

Let’s write a pred that mandates that there are no clique with more than 2 nodes.

Q: What quantifier should we use here? Can we use some here again?
A: No, some won’t work, because we want to make sure there are no bigger cliques, which mandates that we quantifier over all cliques.

pred only2Clique {
    all clique: set City |
        (all c: clique | clique - c in c.roads)
            implies #clique = 2
}

This works logically, so let’s run it!

Oh no! Error! “Analysis cannot be performed since it requires higher-order quantification that could not be skolemized.”

Alloy can’t handle for-alls here, because of the complexity - it would have to deal with 2^36 constraints in this case.

This is crashing specifically because you’re using all for a set. Can we get around this using not (some ... ). No, Alloy is smart enough to detect that you really mean a some.

So how can we write this predicate in a way that Alloy will allow? We can downshift and rewrite the predicate to only quantify all over single things rather than sets. This may not be pretty, but it works!

pred workingOnly2Clique {
    all c1, c2: City |
        (c1 in c2.roads and c2 in c1.roads)
        implies
        no c3: City | {
            c3 in c1.roads
            c3 in c2.roads
            c1 in c2.roads
            c1 in c3.roads
        }
}