The difference between one
and some
in Alloy (and how they are implemented):
sig City { roads : set City }
pred someHub {
some c: City |
all c2: City | c->c2 in roads
}
pred oneHub {
one c: City |
all c2: City | c->c2 in roads
}
The only difference in the above predicates is whether we use one
or some
. We expect that the instances of oneHub
will be a proper subset of someHub
(because if there is one
, there is some
, but not vice versa)
However, they are implemented differently under the hood by Alloy. As a result, one
does not skolemize, so if you want to see the skolem relations in the visualizer, you will have to rewrite your predicate to use some
and all
, not one
Alloy takes relational logic and turns it into pure boolean formulas under the hood.
some c : City |
all c2: City |
c->c2 in roads
run for exactly 2 City
What’s an example instance that we would expect to see?
city1 -> city1, city1 -> city2
city1 -> city1, city1 -> city2, city2 -> city2
How do we turn this into a formula?
Substitute in the possibilities for the some
quantifier
(all c2: City | City1 -> c2 in roads) or (all c2: City | City2 -> c2 in roads)
Now to get rid of the all
, do the same thing, but join with and
instead of or
(city1 -> city1 in roads and city1 -> city2 in roads) or (city2 -> city1 in roads and city2 -> city2 in roads) Now, to simplify, set variables
c11 = city1 -> city1 in roads,
c12 = city1 -> city2 in roads, and so on
(c11 and c12) or (c21 and c22)`
Q: What does an instance of this boolean formula look like?
A: an assignment of truth values to each variable.
ex: c11 = T, c12 = T, c21 = T, c22 = T
This is basically what Alloy does. It translates your model into a boolean satisfiability problem. Then it tries to find variable assignments that make the formula true. If it finds an assignment, it can then reconstruct the instance.
We have boolean formulas with a bunch of ors at the top level, and only ands within each clause.
This is called Disjunctive Normal Form and it is possible to turn any boolean formula into DNF through distributivity and De Morgan’s Law.
Unfortunately, this can create exponentially large boolean formulas.
What is the opposite of DNF?
Conjunctive Normal Form: only ands at the top, and only ors inside each clause
Constructing CNF also results in exponential expansion of your boolean formulas.
Alloy puts boolean formulas into CNF in order to solve them.
Example: Constructing CNF from (c11 and c12) or (c21 and c22)
:
You can distribute or
over and
[c11 or (c21 and c22)] and [c12 or (c21 and c22)]
Then we do the distribution again:
(c11 or c21) and (c11 or c22) and (c12 or c21) and ...
Notice that we get a formula that is really really long. Converting from DNF to CNF is the worst case, and usually ends up with an exponentially long formula
SAT solvers are good at solving formulas in CNF. But we don’t want formulas that are exponentially long. So how can we make a shorter CNF formula?
What if we sacrifice equivalence?
Of course, we want to still be able to reconstruct a valid instance, so we can’t totally give up equivalence.
Introduce fresh variables for each subformula in the original formula:
(c11 and c12) or (c21 and c22)
becomes Arjun1 or Arjun2
Now we add constraints so that Arjun1
is only true if c11 and c12
is true (and similarly for Arjun2
)
clause: another word for disjunction of variables
How do we write A1 iff c11 and c212
in CNF?
(A1 -> c11 and c12) and (c11 and c12 -> A1)
In boolean logic, x -> y
is equivalent to not x or y
A1 -> c11 and c12)
becomes A1 -> c11 and A1 -> c12
, which becomes (not A1 or c11) and (not A1 or c12)
and (c11 and c12) -> A1
becomes not (c11 and c12) or A1
, which becomes not c11 or not c12 or A1
This gives us:
not A1 or c11
not A1 or c12
not c11 or not c12 or A1
These three constraints enforce that A1
is equivalent to c11 and c12
We can do the same for A2
to construct an entire CNF formula that is no longer exponential in length.
Our new formula is over a different set of variables, so it’s no longer equivalent to our original formula. However, we can reconstruct an instance of the original problem by getting rid of the A1
and A2
variables. (This is the difference in Alloy between “variables” and “primary variables”)
Q: When you run for up to some number (not exactly), does Alloy do this process for each number up to the bound?
A: No, it actually creates a unary relation for each city, and then keeps track of whether or not that city is used.
Q: How does Alloy get a new instance (next) without having to do the whole process again?
A: Add another clause to the end saying not c11 or not c12 or not c21 or not c22
, so that the next instance you get is different than the one it showed you first.