elevator
TRANSCRIPT
Modeling & verification of a simple elevator system using Alloy
• To describe formally the components of a system and relationships between them
• Check properties about the model• Exclude ill-formed examples of Models.• Two kind of problems might arise
– bugs in the model itself. We will discuss the problems of overconstraining and underconstraining your model, and how to find and eliminate such bugs.
– errors in the subject you are modeling. These are what you are after, and what modeling is all about. We will show you how to make and check assertions about your system, and how to track down why a given assertion failed to hold.
Why Alloy• Conceptual simplicity and minimalism
– easy to learn– WYSIWYG: no special semantics
• high-level notation– Constraints -- can build up incrementally– Relations flexible and powerful– Much more briefly and clearly expressed than most model checking
notations
Properties of Alloy• finite scope check - once you go to actually analyze the model, you must specify a
scope (size) for your model. The analysis is sound (it never returns false positives) but incomplete (since it only checks things up to a certain scope). However, it is complete up to scope; it never misses a counterexample which is smaller than the specified scope. Small scope checks are still extremely valuable for finding errors.
• infinite model - The models you write in Alloy do not reflect the fact that the analysis is finite. That is, you describe the compontents of a system and how they interact, but do not specify how many components there can be (as is done in traditional "model checking").
• declarative - a declarative modeler answers the question "how would I recognize that X has happened", as opposed to an "operational" or "imperative" modeler who asks "how can I accomplish X".
• automatic analysis - unlike some other declarative specification languages, Alloy can be automatically analyzed. You can automatically generate examples of your system and counterexamples to claims made about that system.
Alloy model structures• Structures are expressed as a set of tuples
– individual elements are treated as singleton sets
• The Alloy universe consists of atoms and relations, although everything that you can get your hands on and describe in the language is a relation. Atoms only exist behind the scenes.
• An atom is a primary entity which is – indivisible: it cannot be broken down into smaller parts, – immutable: it's properties don't change over time, and
• A relation is a structure which relates atoms. Each relation is a set of ordered tuples (vectors of atoms). Each tuple indicates that those atoms are related in a certain way (as dictated by the relation itself). The "arity" of the relation is the number of atoms in each tuple.
Alloy declarations• First line module declaration
– module chapter4/filesystem• sig Object {}
– Defines a set named Object represents all objects– abstract sig Object{} abstract sig has no elements
except those belonging to its extensions. • sig File extends Object {}
– A set can be introduced as a subset of another set
• sig A {}• sig B extends A {}• sig C extends A {}
• Means– B in A– C in A– no B & C
Signatures
• abstract sig A {}• sig B extends A {}• sig C extends A {}• B in A• C in A• no B & C • A = (B + C)
Declaring relations
• Relations are declared as fields of signatures• sig Object{}
sig Dir extends Object {entries: set Object,parent: lone Dir
}
Set Multiplicities
• set : any number.• one: exactly one.• lone: zero or one.• some: one or more.
– sig Man extends Person { Wife: lone Woman}– sig Woman extends Person {Husband: lone Man}– sig Object{}
sig Dir extends Object {entries: set Object,parent: lone Dir}
Facts and Assertions• fact:
– Constraints that are assumed always to hold (used to restrict space of possible counterexamples)
– A Model can have any number of factssig A {}fact { all z: A | F }
• assert:– Constraints that are intended to follow from the
facts of the model– Can be checked using the check command
assert, check
• fact F {…}• assert A {…}• check A• means
– fact: assume constraint F holds– assert: believe that A follows from F– check: find an instance that satisfies F and not A
Quantifiers• all x: e | F• some x: e | F• no x: e | F• lone x: e | F• one x: e | F
– fact EntriesSameName {all e1, e2 : entries | e1.name = e2.name => e1 = e2}
– fact nametoaddress { all n:Name| lone d: Address| d in n.address}– fact {no p: Person | p in p.^(mother + father) }– fact FourPassengersPerCar { all c:Car | #{c.contents } <= 4 }– fact OneVehiclePerPassenger { all p:Passenger | one v:Vehicle | p in
v.contents }
Constants and Operators
• The language of relations has its own constants and operators
• Constants– none = empty set– univ = universal set– iden = identity
Constants and Operators• Name = {(N0), (N1)}
Addr = {(D0), (D1)}• none={}
univ = {(N0), (N1), (D0), (D1)} iden = {(N0,N0), (N1,N1), (D0,D0), (D1,D1)}
• Operators fall into Two Categories– Set– Relational
Set Operators
• + : union– sig Vehicle {} {Vehicle = Car + Truck}
• & : intersection– fact DisjSubtrees { all t1, t2:Tree | (t1.^children) & (t2.^children) = none }
• - : difference– fact dirinothers { all d: Directory - Root | some contents.d }
• in : subset– Sibling = (brother + sister)– sister in sibling
• = : equality
Relational operators• . : dot (Join)
– {(N0), (A0)} . {(A0), (D0)} = {(N0), (D0)}
• -> : arrow (product)– s -> t is their cartesian product– r: s -> t says r maps atoms in s to atoms in t
• ^ : transitive closure– fact DisjSubtrees { all t1, t2:Tree | (t1.^children) & (t2.^children) = none }– fact {no p: Person | p in p.^(mother + father) }
• * : reflexive-transitive closure– fact {Object in Root.*contents}
• ~ : transpose– Takes its mirror image s.~r = r.s (image of s navigating backwards through rel r)
Relational Operators• ~ : transpose (continued)
– fact dirinothers{ all d: Directory - Root | some d.~contents }– fact dirinothers { all d: Directory - Root | some contents.d }
• [] : box (join)– Semantically identical to join, takes arguments in different order. Expressions:
e1[e2] = e2.e1
• <: :domain restriction– Contains those tuples of r that start with an element in s.
• :> : range restriction– Contains the tuples of r that end with an element in s
• ++: Override– Override p ++ q (just like union), except that tuples of q can replace tuples of p
rather than augmenting them.
Relational Operators• Alias = {(N0), (N1)}• Addr = {(A0)}• address = {(N0, N1), (N1, N2), (N2, A0)}
• Domain restriction• Alias <: address = {(N0, N1), (N1, N2)}
• Range restriction:• address :> Addr = {(N2, A0)}
• workAddress = {(N0, N1), (N1, A0)}• address ++ workAddress = {(N0, N1), (N1, A0), (N2, A0)}
Logical operators
• ! : negation• && : conjunction (and)• || : disjunction (OR)• => : implication• else : alternative• <=> : bi-implication (IFF)
Logic Cardinalities
• = equals• < less than• > greater than• =< less than or equal to• >= greater than or equal
to
• #r number of tuples in r• 0,1,... integer literal• + plus• - minus
•all b: Bag | #b.marbles =< 3 all bags have 3 or less marbles• fact FourPassengersPerCar { all c:Car | #{c.contents} <= 4 }
Functions and Predicates
• A function is a named expression• Zero or more declarations for arguments• fun grandpas [p: Person]: set Person {
p.(mother + father).father}
• fun colorSequence: Color -> Color { Color <: iden + Red->Green + Green->Yellow + Yellow->Red}
Predicates and functions
• A predicate is a named constraint• Zero or more declarations for arguments• Can be used to represent an operation• Only holds when invoked (unlike fact)
– sig Name, Addr {}sig Book { addr: Name -> Addr }pred add (b, b': Book, n: Name, a: Addr) {
b'.addr = b.addr + n->a}
– pred Above(m, n: Man) {m.floor = n.ceiling}Man m is “above” Man n if m's floor is n's ceiling
pred, run
• fact F {…}• pred P () {…}• run P• means
– fact: assume constraint F holds– pred: define constraint P– run: find an instance that satisfies P and F
elevator policy
challenge› specify a policy for scheduling a lift› all requests eventually served› don’t skip request from inside lift› no fixed configuration of floors, lifts, buttons
A State is described by• Giving number of floors• Listing details of each floor states• The lift itself
A floor has• One door (open or shut)• Up call button • Down call button
– On/off• Down button on the ground floor is always off• Up button on the top floor is always off
• The lift itself has– Current floor– Destination button for each floor
enum Button { On, Off }enum Door { Open, Shut }
sig Floor { door: one Door, up: one Button, down: one Button
}
one sig Lift { current: Int, buttons: seq Button
}
enum keyword to declare singleton subsigs.For example, enum X { A, B, C }means abstract sig X { } one sig A, B, C extends X { }
"seq" is new it is used for declaring a field as a sequence of atoms.
In the following example, for each person p, "p.books" is a sequence of Book: sig Book { } sig Person { books: seq Book }
The actual type of a sequence of Book is "Int->Book".So if s is a sequence of Book, then the first element is s[0] and you can get the set of all elements by writing "univ.s" You can also use "seq" in quantifications, like this: some s: seq Book | FORMULAYou can also use "seq" in function argument declaration, like this: fun getAllElements [s: seq Book] : set Book { univ.s }
sig State { numFloors: Int, floors: seq Floor, lift: Lift } {#floors = numFloorsfloors[0].down = Offfloors[numFloors-1].up = Off0 <= lift.current && lift.current < numFloorsall i: floors.univ - lift.current | floors[i].door = Shut
}
Initially, the buttons are all off, and the lift is on the ground floor (floor 0) with its door shut
enum Button { On, Off }
enum Door { Open, Shut }
sig Floor { door: one Door, up: one Button, down: one Button }
one sig Lift { current: Int, buttons: seq Button }
sig State { numFloors: Int, floors: seq Floor, lift: Lift } {#floors = numFloorsfloors[0].down = Offfloors[numFloors-1].up = Off0 <= lift.current && lift.current < numFloorsall i: floors.univ - lift.current | floors[i].door = Shut
}
pred InitialButton[b: Button] {b = Off
}
pred InitialDoor[d: Door] {d = Shut
}
pred InitialFloor[f: Floor] {InitialDoor[f.door]InitialButton[f.up]InitialButton[f.down]
}
pred InitialLift[l: Lift] {l.current = 0all i: l.buttons.univ |InitialButton[l.buttons[i]]
}
pred InitialState[s: State] {InitialLift[s.lift]all i: s.floors.univ |InitialFloor[s.floors[i]]
}
Initial state
Test initial states
InitialButtonExists:run {some b: Button | InitialButton[b]}
InitialDoorExists:run {some d: Door | InitialDoor[d]}
InitialFloorExists:run {some f: Floor | InitialFloor[f]}
InitialLiftExists:run {some l: Lift | InitialLift[l]}
InitialStateExists:run {some s: State | s.numFloors = 3 && InitialState[s]}
PressUp & PressDownpred PressUp[f, f': Floor] {
f'.door = f.door && f'.up = On && f'.down = f.down}pred PressUp[i: Int,s, s': State] {
i in s.floors.univall j: s.floors.univ-i | s'.floors[j] = s.floors[j]PressUp[s'.floors[i], s.floors[i]]
}
TestPressUp:run { some s, s': State | PressUp[0,s,s']}
pred PressDown[f, f': Floor] {f'.door = f.door && f'.up = f.up && f'.down = On
}pred PressDown[i: Int,s, s': State] {
i in s.floors.univall j: s.floors.univ-i | s'.floors[j] = s.floors[j]PressDown[s'.floors[i], s.floors[i]]
}
TestPressDown:run { some s, s': State | PressDown[1,s,s']}
PressDestButtonpred PressDestButton[i: Int, l, l': Lift] {
i in l.buttons.univl'.current = l.currentall j: l.buttons.univ-i | l'.buttons[j] = l.buttons[j]i != l.current => l'.buttons[i] = On else l'.buttons[i] = l.buttons[i]
}pred PressDestButton[i: Int,s, s': State] {
i in s.floors.univ(all j: s.floors.univ-i |s'.floors[j].up = s.floors[j].up && s'.floors[j].down = s.floors[j].down)PressDestButton[i, s.lift, s'.lift]
}
TestPressDestButton:run { some i: Int, s, s': State | PressDestButton[i,s,s'] }
openDoor & closeDoorpred OpenDoor[f, f': Floor] {
f'.door = Open && f'.up = f.up && f'.down = f.down}pred OpenDoor[i: Int, s, s': State] {
i in s.floors.univ(all j: s.floors.univ |s'.floors[j].up = s.floors[j].up && s'.floors[j].down = s.floors[j].down)OpenDoor[s.floors[i], s'.floors[i]]
}TestOpenDoor:run { some i: Int, s, s': State | OpenDoor[i,s,s'] }
pred CloseDoor[f, f': Floor] {f'.door = Shut && f'.up = f.up && f'.down = f.down
}pred CloseDoor[i: Int, s, s': State] {
i in s.floors.univ(all j: s.floors.univ |s'.floors[j].up = s.floors[j].up && s'.floors[j].down = s.floors[j].down)CloseDoor[s.floors[i], s'.floors[i]]
}TestCloseDoor:run { some i: Int, s, s': State | CloseDoor[i,s,s'] }
Move uppred moveLiftUp[s, s': State] {
s.floors[s.lift.current].door = Shut(some i: s.floors.univ | i > s.lift.current &&(s.lift.buttons[i] = On || s.floors[i].up = On || s.floors[i].down = On))s'.floors = s.floorss'.lift.current = s'.lift.current+1
}
TestMoveLiftUp:run { some s, s': State | moveLiftUp[s, s'] }
Move Lift Downpred moveLiftDown[s, s': State] {
s.floors[s.lift.current].door = Shut(some i: s.floors.univ | i < s.lift.current &&(s.lift.buttons[i] = On || s.floors[i].up = On || s.floors[i].down = On))s'.floors = s.floorss'.lift.current = s'.lift.current-1
}TestMoveLiftDown:run { some s, s': State | moveLiftDown[s, s'] }