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!

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 Instances 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:

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.