CS1950Y Lecture 14: Topic
February 26, 2018
Announcements
There’s no lab this week! Lab will resume next week, but we’re giving you this one off.
Case Studies!
- There will be signups really soon - we’ll post a form.
- What you’ll be doing: You won’t really be presenting. Instead you’re showing Tim that you did your due diligence in reading through the model and thinking about it. He’ll be asking about things like design decisions the spec authors made, the purpose of particular constraints, and so on.
Boolean Logic, continued
Yesterday, we got to syntax. Alloy can generate expression trees for us, but they don’t really have any meaning. Today, we’ll get to semantics and add meaning to these syntactic objects.
In the context of Boolean logic, what is the meaning of an instance? We’ll have some amount of variables, and for each of these variables, we need to know if it’s true or false.
sig Instance {
-- This is enough to figure out the values of all the variables without dealing with wider relations in Alloy.
trueVars : set Var
}
-- Just like we needed to add `size` as a field on `Formula`, we'll do the same for the set of satisfying instances.
-- Normally, this would be done inductively, so we'll use the same technique as last time here.
abstract sig Formula {
size: Int,
eval: set Instance
}
-- Next, we have to define how each type of formula is satisfied
sig Var extends Formula {} {
size = 1
-- This is a bit circular, but it works! We're saying that a variable is satisfied in an instance if it's true in that
-- instance.
eval = { i : Instance | this in i.trueVars }
}
sig Not extends Formula { child: Formula } {
size = ...
-- Since `not x` should be true in every instance where `x` is false, we can take the set of all instances and subtract
-- out ones where our child is true.
eval = Instance - child.@eval
}
sig And extends ... {
-- ...
-- `and` is true whenever both of its children are true, so we can use set intersection on their instances.
eval = left.@eval & right.@eval
-- Note: if you're uncomfortable with the @ operator, that's totally fine. Instead of using sig facts, you can use
-- explicit quantification and avoid it.
}
sig Or extends ... {
-- ...
eval = left.@eval + right.@eval
}
Hey, look! We’ve just defined the semantics of Boolean logic in Alloy. Just like the structure of the formulas is inductive, the semantics are inductive themselves. We’ve modeled this induction in Alloy with constraints on our sigs, and now it’ll give us instances.
Note: just like we made size
an attribute when looking at instances last time, we’ll want to do the same for eval
and trueVars
. Otherwise, there are way too many arrows to interpret.
If we run GiveMeAnInterestingFormula
now, we can look at the Instance
s Alloy produces and make sure they match the semantics of the generated formula.
Modeling Alloy in Alloy
Now that we’ve got Boolean logic, we’ll model a small subset of Alloy. Remember predicate calculus from the early Alloy assignments. This logic is also referred to as first-order logic, and you might’ve seen it called that in other classes. We’ve got constructs like these:
some x : A | ...
all x : A | ...
x1 -> ... -> xn in R
(this is specifically that some tuple is in some relation - we’re not modeling the whole set of operators like->
andin
)and
,or
,not
We can view this as an extension of Boolean logic, and build off of our existing model. First, we’ll get rid of size
, since that won’t be the interesting thing here.
Again, think about syntax. What needs changing?
Before, variables took true
and false
values. That’s not the case anymore. Now, they’re atoms, and we have relations between atoms as well.
sig RelName {
arity: Int
}
-- Var isn't a formula anymore, because variable values are atoms, not true or false.
sig Var {}
-- Something like x1 -> ... -> xn in R
sig Atomic extends Formula {
-- Thinking only in terms of syntax, what does an atomic formula have?
-- On the right side of the `in`, we have some relation.
rel: RelName,
-- And on the left, we have a tuple of variables
-- We have to be careful not to use `set Var` here, because the ordering is important.
tuple: seq Var
}
-- For And, Or, and Not, we don't need to change anything! A Formula is still something that evaluates to true or false, so
-- they can all still have Formula children.
-- For today, we'll make quantifiers simpler and ignore types.
-- We can sort of work around this, since Alloy types are sets. For example, `all s : Student | ...` is equivalent to
-- `all s | (s in Student) implies ...`. Since `implies` is itself a defined operator, the final syntax is
-- `all s | (not (s in Student)) or ...` (`p implies q` is equivalent to `(not p) or q`)
-- some v such that child(v)
sig Exists extends Formula {
v: Var,
child: Formula
}
-- for all v, child(v)
sig Forall extends Formula {
v: Var,
child: Formula
}
We also need to update allSubformulas
to disambiguate child
, because now Not
, Exists
, and Forall
all have a child
. This is something to be careful about when defining recursive models. Usually, when you add a sig, there’ll be some functions or predicates that need to be updated with this sig’s parent-child relations.
Our model has a bug, which we’ll fix next time. We don’t constrain that Atomic
formulas actually have the correct arity in their tuples.