Today, we'll look at more examples of how Alloy uses relationional logic to describe interesting systems.
Workday example
Let's use Alloy to model our favorite part of the Wellesley experience: registering for classes on Workday!
First, we'll define some people. Recall that we use the abstract
keyword to indicate that we only want actual atoms of a sub-sig, not the sig itself.
sig TimeSlot {}
abstract sig Person {}
sig Student extends Person {}
sig Admin extends Person {
office: Room
}
sig CourseID {}
sig Room {}
sig Faculty extends Person {
advisees: set Student,
office: Room
}
Now, we can model a course:
sig Course {
instructor : Faculty,
when : TimeSlot,
where : Room,
tutors: set Student,
staff: set Person,
roster : set Student,
}
Sig facts
Alloy has a special syntax to indicate fact
along with a sig, like below. In the "sig-fact" syntax, everything in the second block is scoped to be over all c: Course
.
sig Course {
instructor : Faculty,
tutors: set Student,
staff: set Person,
roster : set Student,
when : TimeSlot,
where : Room,
} {
staff = instructor + tutors
no (tutors & roster)
}
Because we are scoped to talking about all courses for the sig, the first line is equivalent to the following, if defined outside of the sig definition:
fact {
all c: Course | no (c.tutors & c.roster)
}
The above says: "no course can have overlap between its tutors and its students".
Note that this is different from the following, which would say: "no person can be both a tutor for any course and a student for any course":
fact {
no (c.tutors & c.roster)
}
Workday constraints using relations
Now, let's set up some facts that relate different courses.
fact noTimeTraveling {
all disj c1, c2 : Course |
c1.when = c2.when => {
c1.instructor != c2.instructor
c1.where != c2.where
no c1.roster & c2.roster
}
}
Relations between sigs and fields
We can use relations on the full sig and a field together in interesting ways.
For example, some instances have a problem where the course is supossed to take place in some faculty member's office. Let's model for that to not be allowed:
fact differentRooms {
no Course.where & (Admin.office + Faculty.office)
}
Does this prevent faculty and admin from sharing offices?
(Think, then click!)
No since we don't say anything about the intersection of Admin.office
and Faculty.office
, or do anything to say two admins can't be in the same office. But, we'll leave that as unconstrained for now.
Joins
The join, or .
, syntax is very general!
We can conceptualize joins are taking two database tables, finding all the rows were the columns in the middle of the join are the same, and keeping just those rows with the "matched" columns removed.
Said another way, a join a.b
on two binary relations, a
and b
:
- Looks at the second atom,
a2
, of every pair a1->a2
in a
.
- Finds every pair
b1->b2
in b
where the first atom b1
matches a2
.
- Adds
a1->b2
to the output relation.
Numbers
Alloy can also reason about numbers. For example, we can say that the total number of rooms must be great than 3.
fact roomCount {
#Room > 3
}
However, using integers to count can often be slower in the solver than expressing constraints on number with relations directly. Only use counting if you actually care about the specific integer relationships.
Note that Alloy can model integers both with and without overflow allowed. This is something you can change in the settings (search Help for Overflow). In general, we'll keep overflow on if we want to model a system where overflow can really happen (like a 4-bit counter chip, as seen in a CS240 lab), and otherwise turn overflow off (for cases like this Workday example, where the number of rooms is a mathematical, counting integer we aren't storing somewhere small).
See the end of these notes for the full Workday file.
More on relations (graph example)
Let's look at a very simple Alloy example to see how powerfun Alloy's use of relations and the join operator is.
sig Node {
edges : set Node
}
run { #Node = 3}
Does this model a directed or undirected graph?
(Think, then click!)
Directed, since the order of atoms in a relation matters.
We can use the Evaluator to examine the value of different relations in a given instance of this model.
The instance we get first is:
[N1] -> [N0] -> [N3], (N1 also has a self loop)
The following results are all from the Evaluator in the Alloy Analyzer.
If we ask for just the name of the entire sig:
Node
We get:
The set of all nodes ({N0, N1, N2}
).
edges
We get:
The set of all edges ({N0->N2, N1->N0, N1->N1}
).
N.edges
We get:
The set of all nodes with some incoming edge ({N0, N1, N2}
).
edges.N
=
We get:
The set of all nodes with some outgoing edge ({N0, N1}
).
edges.edges
We get:
The set of two-hop paths ({N1->N0, N1->N1, N1->N2}
).
That is, the set of paths we can take in the graph of length 2, starting at any node.
Next time, we'll see how we (and Alloy) can take this idea of paths and reachability even further.
abstract sig Person {}
sig Room {}
sig Faculty extends Person {
office: Room,
advisees: set Student,
}
sig Admin extends Person {
office: Room
}
sig Student extends Person {
}
sig TimeSlot {}
sig Course {
instructor : Faculty,
when: TimeSlot,
where: Room,
roster: set Student,
tutors: set Student,
staff: set Person,
} {
no (roster & tutors)
staff = instructor + tutors
}
fact noTimeTraveling {
all disj c1, c2 : Course | {
c1.when = c2.when =>
{
c1.instructor != c2.instructor
c1.where != c2.where
no (c1.roster & c2.roster)
}
}
}
fact differentRooms {
no (Course.where & (Admin.office + Faculty.office))
}
fact numerics {
#Room > 3
}
run {
some Room
} for 5
Today, we'll look at more examples of how Alloy uses relationional logic to describe interesting systems.
Workday example
Let's use Alloy to model our favorite part of the Wellesley experience: registering for classes on Workday!
First, we'll define some people. Recall that we use the
abstract
keyword to indicate that we only want actual atoms of a sub-sig, not the sig itself.Now, we can model a course:
Sig facts
Alloy has a special syntax to indicate
fact
along with a sig, like below. In the "sig-fact" syntax, everything in the second block is scoped to be overall c: Course
.Because we are scoped to talking about all courses for the sig, the first line is equivalent to the following, if defined outside of the sig definition:
The above says: "no course can have overlap between its tutors and its students".
Note that this is different from the following, which would say: "no person can be both a tutor for any course and a student for any course":
Workday constraints using relations
Now, let's set up some facts that relate different courses.
Relations between sigs and fields
We can use relations on the full sig and a field together in interesting ways.
For example, some instances have a problem where the course is supossed to take place in some faculty member's office. Let's model for that to not be allowed:
Does this prevent faculty and admin from sharing offices?
(Think, then click!)
No since we don't say anything about the intersection of
Admin.office
andFaculty.office
, or do anything to say two admins can't be in the same office. But, we'll leave that as unconstrained for now.Joins
The join, or
.
, syntax is very general!We can conceptualize joins are taking two database tables, finding all the rows were the columns in the middle of the join are the same, and keeping just those rows with the "matched" columns removed.
Said another way, a join
a.b
on two binary relations,a
andb
:a2
, of every paira1->a2
ina
.b1->b2
inb
where the first atomb1
matchesa2
.a1->b2
to the output relation.Numbers
Alloy can also reason about numbers. For example, we can say that the total number of rooms must be great than 3.
However, using integers to count can often be slower in the solver than expressing constraints on number with relations directly. Only use counting if you actually care about the specific integer relationships.
Note that Alloy can model integers both with and without overflow allowed. This is something you can change in the settings (search Help for Overflow). In general, we'll keep overflow on if we want to model a system where overflow can really happen (like a 4-bit counter chip, as seen in a CS240 lab), and otherwise turn overflow off (for cases like this Workday example, where the number of rooms is a mathematical, counting integer we aren't storing somewhere small).
See the end of these notes for the full Workday file.
More on relations (graph example)
Let's look at a very simple Alloy example to see how powerfun Alloy's use of relations and the join operator is.
Does this model a directed or undirected graph?
(Think, then click!)
Directed, since the order of atoms in a relation matters.
We can use the Evaluator to examine the value of different relations in a given instance of this model.
The instance we get first is:
[N1] -> [N0] -> [N3], (N1 also has a self loop)
The following results are all from the Evaluator in the Alloy Analyzer.
If we ask for just the name of the entire sig:
Node
We get:
The set of all nodes (
{N0, N1, N2}
).edges
We get:
The set of all edges (
{N0->N2, N1->N0, N1->N1}
).N.edges
We get:
The set of all nodes with some incoming edge (
{N0, N1, N2}
).edges.N
=We get:
The set of all nodes with some outgoing edge (
{N0, N1}
).edges.edges
We get:
The set of two-hop paths (
{N1->N0, N1->N1, N1->N2}
).That is, the set of paths we can take in the graph of length 2, starting at any node.
Next time, we'll see how we (and Alloy) can take this idea of paths and reachability even further.