Académique Documents
Professionnel Documents
Culture Documents
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.
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
sig Object {}
Defines a set named Object represents all objects abstract sig Object{} abstract sig has no elements except those belonging to its extensions.
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 }
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 }
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)}
^ : 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
++: 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 }
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 dont 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 | FORMULA You 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 = numFloors floors[0].down = Off floors[numFloors-1].up = Off 0 <= lift.current && lift.current < numFloors all 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
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 = numFloors floors[0].down = Off floors[numFloors-1].up = Off 0 <= lift.current && lift.current < numFloors all i: floors.univ - lift.current | floors[i].door = Shut }
Initial state
pred InitialButton[b: Button] { b = Off }
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.univ all j: s.floors.univ-i | s'.floors[j] = s.floors[j] PressDown[s'.floors[i], s.floors[i]] }
PressDestButton
pred PressDestButton[i: Int, l, l': Lift] { i in l.buttons.univ l'.current = l.current all 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] }
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 up
pred 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.floors s'.lift.current = s'.lift.current+1 } TestMoveLiftUp: run { some s, s': State | moveLiftUp[s, s'] }