Vous êtes sur la page 1sur 40

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.

First line module declaration


module chapter4/filesystem

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.

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 facts
sig 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 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

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 = 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 InitialDoor[d: Door] { d = Shut }

pred InitialFloor[f: Floor] { InitialDoor[f.door] InitialButton[f.up] InitialButton[f.down] }

pred InitialLift[l: Lift] { l.current = 0 all i: l.buttons.univ | InitialButton[l.buttons[i]] }

pred InitialState[s: State] { InitialLift[s.lift] all i: s.floors.univ | InitialFloor[s.floors[i]] }

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 & PressDown


pred 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.univ all 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.univ all 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']}

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] }

TestPressDestButton: run { some i: Int, s, s': State | PressDestButton[i,s,s'] }

openDoor & closeDoor


pred 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 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'] }

Move Lift Down


pred 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.floors s'.lift.current = s'.lift.current-1 } TestMoveLiftDown: run { some s, s': State | moveLiftDown[s, s'] }

Vous aimerez peut-être aussi