Modeling sequence of events in Alloy - alloy

The following model represents a sequence of actions given a certain pre-defined order.
open util/ordering[Time]
abstract sig Action {pre: set Action}
one sig A, B, C, D extends Action {}
fact{
pre = A -> B + D -> B + D -> C
}
sig Time { queue: Action -> lone State}
abstract sig State {}
one sig Acted, Ok, Nok extends State{}
pred Queue [t, t': Time] {
some a: Action-(t.queue).State |
a.pre in (t.queue).Ok + (t.queue).Nok and t'.queue=t.queue+(a->Acted)
}
pred Reply [t, t': Time] {
some a: (t.queue).Acted |
some s: State-Acted | t'.queue=t.queue++(a->s)
}
fact {
no first.queue
last.queue=Action->Ok or last.queue = Action -> Nok
all t:Time-last | Queue[t,t.next] or Reply[t,t.next]
}
run {last.queue=Action->Ok and some t:Time-last | t.queue = Action->Nok} for 9
With the run I would like to have a sequence where the last queueing action goes OK but some action failed before. However I don't get any instance.
Can someone explain me what am I doing wrong?
Regards, Andre.

The problem comes from the fact that once an action has, at a given time, a state which is Nok then it can't be changed back to another state in a future time(as suggested by the two quantifier in the Queue and Reply predicates).
The analyzer thus can't find an instance where in the final Time, all the actions are in an Ok state and where at a given time an action is in a Nok State. (This is what you request in your run command)
Hope it helps

Related

Predicate not consistent

I have to realize an application which permits to reserve a seat in a store. There are no sintax error, but I don't understand why terminal reply me "predicate not consistent". Can u help me?
This is my code:
open util/integer
sig Email{}
sig CF{}
sig Time{
hour: one Int,
minute:one Int,
second: one Int
}
abstract sig RegisteredUser{
email: one Email
}
sig User extends RegisteredUser{
cf: one CF
}
sig SM extends RegisteredUser{
cf: one CF,
store: one Store
}
sig Location{}
sig Date{}
abstract sig Status {}
//only one of this can be true
one sig Waiting extends Status {}
one sig Expired extends Status{}
one sig Pending extends Status{}
sig Ticket{
owner:one User,
date: one Date,
time: one Time,
status : one Status,
}
sig Visit{
owner:one User,
date: one Date,
time: one Time,
duration:one Time,
status : one Status,
products: some Product,
category: some Category,
}
//tickets are assumed with a default duration time of 15 min and entrance one for time, so 4*8=32 visit per day
sig TicketQueue {
max_visit:one Int,
ticket: some Ticket,
manager: one SM
}
sig VisitQueue{
category: some Category,
visit: some Visit,
manager: one SM
}
sig Store{
max_simultaneous: one Int,
location: one Location,
visitqueue: one VisitQueue,
ticketqueue: one TicketQueue,
product_category: some Category
}
sig Category{
simultaneous_seats: one Int,
}
sig Product{
category: one Category
}
// Constraints
// Registration data for the system are unique(Unique username and Fiscal Code)
fact registrationDataUniqueness {
no disjoint u1, u2: RegisteredUser | u1.email = u2.email
no disjoint u1,u2: User | u1.cf = u2.cf
no disjoint s1,s2: SM | s1.cf = s2.cf
no disjoint s:SM, u:User | s.cf=u.cf
}
//Ticket and Visit can have only one of status' values defined before
fact requestConsistency {
all s: Status | (s = Waiting && s != Expired && s !=Pending ) || (s != Waiting && s = Expired && s != Pending) || (s !=Waiting && s != Expired && s = Pending)
}
//SM can manage only one Store
fact StoreUniqueness{
no disjoint s1,s2: SM |s1.store=s2.store
}
// the same ticket or visit cannot be of two or more different user
fact UserUniqueness{
no disjoint t1,t2: Ticket | t1.owner=t2.owner
no disjoint v1,v2: Visit | v1.owner=v2.owner
}
//different products can not be of the same category
fact CategoryUniqueness{
no disjoint p1,p2: Product | p1.category=p2.category
}
//different stores can't have the same ticket or visit queue
fact QueueUniqueness{
no disjoint s1,s2: Store | s1.ticketqueue=s2.ticketqueue
no disjoint s1,s2: Store | s1.visitqueue=s2.visitqueue
}
//different tickets cannot have the same time
fact TimeUniqueness{
no disjoint t1,t2: Ticket | t1.time = t2.time
}
//for semplicity,in the visit/ticket queue, we consider "active" only visit/ticket with Waiting status
fact StatusQueue{
all tq:TicketQueue | tq.ticket.status=Waiting
all vq:VisitQueue | vq.visit.status=Waiting
}
//one ticket/visit can not belong to two or more different queue
fact ReservationUniqueness{
no disjoint tq1,tq2:TicketQueue | tq1.ticket=tq2.ticket
no disjoint vq1,vq2:VisitQueue | vq1.visit=vq2.visit
}
//SM is unique for a specific store, and its related ticket/visit queue
fact SMUniqueness{
no disjoint s1,s2: Store | s1.ticketqueue.manager=s2.ticketqueue.manager
and s1.visitqueue.manager=s2.visitqueue.manager
and s1.ticketqueue.manager=s2.visitqueue.manager
and s1.visitqueue.manager=s2.ticketqueue.manager
}
//different tickets/visits associated with the same user have different time
fact UniqueTimeUser {
all u: User, t1, t2: Ticket, v1,v2:Visit | ((u in t1.owner) and (u in t2.owner) and (u in v1.owner) and (u in v2.owner)
and (t1 != t2) and (v1 !=v2))
implies (t1.time != t2.time)
and (v1.time != v2.time)
}
//different ticket and visit associated with the same user have different time
fact UniqueTimeUser2 {
all u: User, t: Ticket, v:Visit | ((u in t.owner) and (u in v.owner) )
implies (v.time != t.time)
}
//Maximum number of visit with the same time for category
fact MaxTime{
all v:Visit, c: Category |(( c in v.category)) implies #v.time< c.simultaneous_seats
}
//Considering meaningfull integer value
fact PossibleValues{
all c: Category | c.simultaneous_seats>1 and c.simultaneous_seats<5
all s: Store | s.max_simultaneous >0 and s.max_simultaneous<50
all t: Ticket | t.time.hour>7 and t.time.hour<21 and ((t.time.minute=15) || (t.time.minute=30) || (t.time.minute=45)) and t.time.second=0
all v: Visit | v.time.hour>7 and v.time.hour<21 and ((v.time.minute=15) || (v.time.minute=30) || (v.time.minute=45)) and v.time.second=0 and v.duration.hour=0 and v.duration.minute>0 and v.duration.minute=<30 and v.duration.second=0
all tq: TicketQueue | tq.max_visit=32
}
pred addTicket[ t :Ticket,ti,ti':Time, tq, tq':TicketQueue]{
//precondition
ti' not in Ticket.time
#tq.ticket< tq.max_visit //seats available
//postconditions
tq'.manager= tq.manager
tq'.ticket= tq.ticket+t
t in tq'.ticket
#tq'.ticket< tq.max_visit
all t': Ticket | t' in tq.ticket implies t' in tq'.ticket
}
pred show{
#User = 4
#Store = 2
#SM = 2
#Category = 3
#TicketQueue = 2
#VisitQueue= 2
#Category = 5
some v, v': Visit | v.time != v'.time
#Ticket = 5
#Visit=5
}
run show for 4
This is the complete response of Alloy terminal:
Executing "Run show for 4"
Solver=sat4j Bitwidth=4 MaxSeq=4 SkolemDepth=1 Symmetry=20
15098 vars. 840 primary vars. 17697 clauses. 28ms.
No instance found. Predicate may be inconsistent. 1ms.
Have you run unsat core (using the MiniSAT with Unsat Core solver)? That might give you a hint. Another thought: I would simplify the model first and then only add features as you need them. I'm not sure that you really need to split the times into hours, minutes and seconds. That adds a lot of solver complexity, perhaps needlessly.
General observations:
Some inconsistences in your show predicate:
#Category = 3
#Category = 5
Your scope is definitely too small. You can't expect 5 Visit elements if you have a general scope of 4.
I'd rewrite your show predicate and run command as follows:
pred show{
some v, v': Visit | v.time != v'.time
}
run show for 10 but 7 Int, exactly 4 User, exactly 2 Store, exactly 2 SM, exactly 3 Category, exactly 2 TicketQueue, exactly 2 VisitQueue, exactly 5 Ticket, exactly 5 Visit
Notice the 7 Int.
It means that your model will contain int type atoms that represent all the integers you can express with a bitwidth of 7 (interval is [-64,63]).
As Daniel wrote, i'd also advise to stay as abstract as possible when you model in Alloy and thus to prefer concepts over quantitative values.
I concur with the previous advice of simplifying and would add to use check statements to test as you go. I'm not an expert with Alloy, so I take it slow and test early and often.
One thing that sticks out to me immediately is that your "show" predicate seems to be specifying multiplicities for some signatures that are greater than those allowed by the run statement ("for 4"). I've never specified multiplicity in a predicate that way, so I don't know how Alloy handles that. Offhand, those seem like contradictory constraints.
Also, I figured this out the hard way, but Alloy counts instances (atoms) of extended signatures as instances (atoms) of the parent(s). So, be aware of that.

Defining a Fact in Alloy which constrains an Airplane's capacity

I'm trying to define an Alloy Fact, in the below specification, which would prevent adding a Person to an Airplane's set of passengers, unless there's enough capacity. Furthermore, I would like to add another Fact, which would disallow the removal of a Person from an Airplane's passenger set, unless the latter's cardinality exceeded zero.
Any help would be much appreciated.
Cheers,
Phiroc
PS Here's my first stab at the initial Fact:
a'.onboard <= a'.capacity implies
no p and add [a,a',p] and del [a',a'',p] implies a.onboard = a''.onboard
... According to Daniel Jackson, it's invalid because the condition before the implied keyword could potentially be false. Furthermore, it doesn't have the expected effect of preventing the number of passengers from exceeding the airplane capacity, when running the model. For instance, you sometimes get sets with cardinality 4, although capacity is 2, when you execute the model...
sig Person {}
sig Airplane {onboard: set Person, capacity: Int}
fact {
some a,a': Airplane | disj [a.onboard, a'.onboard]
}
pred show(a: Airplane) {
#a.onboard > 0
a.capacity = 2
}
run show for 10 but 2 Airplane
pred add (a, a': Airplane, p: Person) {
a'.onboard = a.onboard + p
}
pred del (a, a': Airplane, p: Person) {
a'.onboard = a.onboard - p
}
assert delUndoesAdd {
all a,a',a'': Airplane, p: Person |
no p and add [a,a',p] and del [a',a'',p] implies a.onboard = a''.onboard
}
assert addIdempotence {
all a,a',a'': Airplane, p: Person |
add [a,a',p] and add [a',a'',p] implies a'.onboard = a''.onboard
}
check delUndoesAdd for 10 but 3 Airplane
check addIdempotence for 3
I think I've found a solution.
sig Person {}
sig Airplane {onboard: set Person, capacity: Int}
fact {
some a,a': Airplane | disj [a.onboard, a'.onboard]
}
fact {
all a: Airplane | a.capacity = 3 and #a.onboard <= a.capacity
}
pred show(a: Airplane) {
}
run show for 10 but 2 Airplane
pred add (a, a': Airplane, p: Person) {
a'.onboard = a.onboard + p
}
pred del (a, a': Airplane, p: Person) {
a'.onboard = a.onboard - p
}
assert delUndoesAdd {
all a,a',a'': Airplane, p: Person |
no p and add [a,a',p] and del [a',a'',p] implies a.onboard = a''.onboard
}
assert addIdempotence {
all a,a',a'': Airplane, p: Person |
add [a,a',p] and add [a',a'',p] implies a'.onboard = a''.onboard
}
check delUndoesAdd for 10 but 3 Airplane
check addIdempotence for 3

Creating an object for each relation in Alloy

I have the following def. in Alloy:
sig A {b : set B}
sig B{}
sig Q {s: A , t: B}
I want to add a set of constraints such that for each relation b1:b there exists one and only one Q1:Q where Q1.s and Q1.t refers to the source and target of b1, respectively. For example, if I have an instance which contains A1 and B1 and b1 connects them (i.e., b1: A1->B1), then I also would like to have a Q1 where Q1.s=A1 and Q1.t=B1.
Obviously number (cardinality) of Q is equal to number (cardinality) of b relation.
I managed to write such a constraint as bellow:
t in s.b
all q1,q2:Q | q1.s=q2.s and q1.t=q2.t => q1=q2
all a1:A,b1:B | a1->b1 in b => some q:Q | q.s=a1 and q.t=b1
I am wondering if anyone has a bit more concise way to express my intentions in terms of an alloy fact. I am open to use Alloy util package if it makes life easier.
Thanks
sig A { b : set B }
sig B {}
sig Q { ab : A -> B }{ one ab }
fact { b = Q.ab and #Q = #b }
I would complete the #user1513683 answer by adding two relations s and t to make it the complete answer to the question:
sig A { b : set B }
sig B {}
sig Q { ab : A -> B , s:A, t:B}{ one ab and t=ab[s]}
fact { b = Q.ab and #Q = #b }

Alloy error signature

I've to run an example of the book "Logic in computer Science", Michael Huth and Mark Ryan. The example is on section 2.7.3, and it's the next one:
module PDS
open std/ord -- opens specification template for linear order
sig Component {
name: Name,
main: option Service,
export: set Service,
import: set Service,
version: Number
}{ no import & export }
sig PDS {
components: set Component,
schedule: components -> Service ->? components,
requires: components -> components
}{ components.import in components.export }
fact SoundPDSs {
all P : PDS |
all c : components, s : Service | --1
let c' = c.schedule[s] {
(some c' iff s in c.import) && (some c' => s in c'.export)
}
all c : components | c.requieres = c.schedule[Service] --2
}
sig Name, Number, Service {}
fun AddComponent(P, P': PDS, c: Component) {
not c in P.components
P'.components = P.components + c
} run AddComponent for 3 but 2 PDS
fun RemoveComponent(P, P' : PDS, c: Component) {
c in P.components
P'.components = P.components - c
} run RemoveComponents for 3 but 2 PDS
fun HighestVersionPolicy(P: PDS) {
all s : Service, c : components, c' : c.schedule[s],
c'' : components - c' {
s in c''.export && c''.name = c'.name => c''.version in c'version.^(Ord[Number].prev)
}
} run HighestVersionPolicy for 3 but 1 PDS
fun AGuideSimulation(P, P', P'' : PDS, c1, c2 : Component) {
AddComponent(P, P', c1) RemoveComponent(P, P'', c2)
HighestVersionPolicy(P) HigjestVersionPolicy(P') HighestVersionPolicy(P'')
} run AGuideSimulation for 3
assert AddingIsFunctionalForPDSs {
all P, P', P'' : PDS, c : Component {
AddComponent(P, P', c) && AddComponent(P, P'', c) => P' = P''
}
}
check AddingIsFunctionalForPDSs for 3
I've to run it on the MIT's alloy analizer (http://alloy.mit.edu/alloy/), and when I execute this code I have the following error:
Syntax error at line 7 column 15:
There are 1 possible tokens that can appear here:
}
I 've searched in some reference books, forums... and I don't found something useful. If someone have been working with this tool and knows how to solve this problem I would be very grateful.
Thanks in advance.
Your primary problem is that the second edition of the Huth / Ryan book appears to have been published in 2004 and to use (unsurprisingly) the syntax accepted by the Alloy Analyzer at that time, which is (also unsurprisingly) not quite the same as the syntax accepted by current versions of the Alloy Analyzer.
So to run this in a current version of the Analyzer, you are going to have to understand (a) what they are trying to say (b) the then-current Alloy syntax in which they are trying to say it, and (c) the current Alloy syntax, well enough to translate the model into current syntax. (Or else find someone who has done this already.) Fortunately, Huth and Ryan explain the Alloy syntax they use in some detail, so it's not a difficult exercise for someone familiar with Alloy 4 to translate the model into the Alloy 4 syntax.
Good luck!
[Postscript] On the theory that the goal of your assignment is to let you get familiar with the Alloy Analyzer, and not to drop you in the deep end by requiring a translation from the old Alloy syntax to the new Alloy syntax, I append a rough translation of the Huth / Ryan PDS model into Alloy 4 syntax, with some interspersed comments. (I say 'rough' because I haven't spent a lot of time on it, and I may have missed some nuances, especially in the predicate HighestVersionPolicy, where the authors get a little tricky.) If the goal of your assignment is precisely to force you to fight your way through the thickets of syntax with only your native ingenuity to use as a machete, then I apologize for messing up that experience.
At the top of the module, the main change is to the way the ordering library module is invoked.
module PDS
open util/ordering[Number]
In Component, the keyword 'option' is replaced by the current keyword 'lone', and I've transcribed some of Huth and Ryan's comments, to try to help myself understand what is going on better.
sig Component {
name: Name, // name of the component
main: lone Service, // component may have a 'main' service
export: set Service, // services the component exports
import: set Service, // services the component imports
version: Number // version number of the component
}{
no import & export // imports and exports are disjoint
// sets of services
}
In the sig for PDS, the main change is again the change to cardinality syntax: ->? becomes -> lone.
// Package Dependency System
// components is the set of Component in this PDS
// schedule assigns to each component in the PDS
// and any of its import services
// a component in the PDS that provides that service
// (see SoundPDSs, below)
// requires expresses the component dependencies
// entailed by the schedule
sig PDS {
components: set Component,
schedule: components -> Service -> lone components,
// any component / Service pair maps to at most
// one component
requires: components -> components
}{
// for every component in the system,
// the services it imports are supplied (exported)
// by some (other) component in the system
components.import in components.export
}
In the fact SoundPDSs, the authors use a with P construct which I don't remember ever seeing in versions of Alloy I've used. So I took it out, and reformulated the expressions a bit for clarity, since the authors explain that clarity is their main motive for using the with P construct. It will be well worth your while to be sure you understand Alloy's box notation, and why P.schedule[c][s] is another way of writing c.(P.schedule)[s] or s.(c.(P.schedule)).
fact SoundPDSs {
all P : PDS | {
all c : P.components, s : Service |
let c' = P.schedule[c][s] {
(some c' iff s in c.import)
// c and s require c' only iff c imports s
&&
(some c' => s in c'.export)
// c and s require c' only if c' exports s
}
all c : P.components | P.requires[c]= P.schedule[c][Service]
// c requires precisely those components
// that schedule says c depends on for some service
}
}
sig Name, Number, Service {}
The big change from here on out is that Huth and Ryan use fun for their definitions of various properties, where Alloy 4 uses pred -- the keyword fun is still legal, but it means a function (an expression which when evaluated returns a value, not a Boolean) not a predicate.
pred AddComponent(P, P': PDS, c: Component) {
not c in P.components
P'.components = P.components + c
} run AddComponent for 3 but 2 PDS
pred RemoveComponent(P, P' : PDS, c: Component) {
c in P.components
P'.components = P.components - c
} run RemoveComponent for 3 but 2 PDS
In HighestVersionPolicy, I've again introduced box notation to try to make the expression clearer. Note that prev is not defined here -- it's one of the relations imported by the import instruction (open ...) at the top of the module, from the library module for ordering.
pred HighestVersionPolicy(P: PDS) {
all s : Service,
c : P.components,
c' : P.schedule[c][s],
c'' : P.components - c' {
s in c''.export && c''.name = c'.name
=>
c''.version in ^prev[c'.version]
}
} run HighestVersionPolicy for 3 but 1 PDS
pred AGuideSimulation(P, P', P'' : PDS, c1, c2 : Component) {
AddComponent[P, P', c1]
RemoveComponent[P, P'', c2]
HighestVersionPolicy[P]
HighestVersionPolicy[P']
HighestVersionPolicy[P'']
} run AGuideSimulation for 3
assert AddingIsFunctionalForPDSs {
all P, P', P'' : PDS, c : Component {
AddComponent[P, P', c] && AddComponent[P, P'', c]
=> P' = P''
}
}
check AddingIsFunctionalForPDSs for 3
The version of their model given by Huth and Ryan on the web doesn't include the StructurallyEqual predicate they describe in their text; I added it to help make sure my translation of the model worked.
pred StructurallyEqual(P, P' : PDS) {
P.components = P'.components
P.schedule = P'.schedule
P.requires = P'.requires
}
run StructurallyEqual for 2
And similarly, they don't include the fix to AddingIsStructurallyFunctional -- the intent is presumably for the student to do it dynamically, in Alloy.
assert AddingIsStructurallyFunctionalForPDSs {
all P, P', P'' : PDS, c : Component {
AddComponent[P, P', c] && AddComponent[P, P'', c]
=>
StructurallyEqual[P',P'']
}
}
check AddingIsStructurallyFunctionalForPDSs for 3

Modelling TicTacToe in Alloy

How do I write a predicate to check for a Horizontal, vertical or diagonal wins for a tictactoe game in Alloy? I'm kind of struggling with the syntax below is my code:
open util/ordering [Time] as T
sig Time {}
abstract sig Game {
turn, winner, loser: Game
}
abstract sig Player {
opponent:Player
}
sig X extends Player {}
sig O extends Player {}
fact {
all t:Time| all p: Player | no p.opponent
all t: Time | all p: Player | all g:Game | one g.turn
all t:Time | all g:Game | one g.winner & g.loser
}
pred HorizontalWin {
}
I think your model might not be appropriate for this game. For example, I don't see a 3x3 grid in your model, so it is not clear how to express any property about the state of the game.
There are several other issues with your model. For example, the Game sig is abstract and it has no concrete subsigs, so instances of this model can never contain any games (thus turn, winner, and loser fields will always be empty as well). Also, you probably want to use the Time signature somewhere (either put some fields in it, or make other fields use it, e.g., turn: Player -> Time) and then add some facts about every two consecutive time steps to properly connect the game moves. Here is an idea:
open util/ordering [Move] as M
abstract sig Player {}
one sig X, O extends Player {}
abstract sig Cell {}
one sig C00, C01, C02, C10, C11, C12, C20, C21, C22 extends Cell {}
sig Board {
grid: Cell -> lone Player
}
sig Move {
player: Player,
pos: Cell,
board, board': Board // pre and post board
} {
// must choose an empty grid cell
no board.grid[pos]
// set the `pos` cell to `player`
board'.grid[pos] = player
// all other grid cells remain the same
all c: Cell - pos | board'.grid[c] = board.grid[c]
}
fact {
// empty board at the beginning
no M/first.board.grid
all m: Move {
some M/next[m] => {
// alternate players each move
M/next[m].player != m.player
// connect boards
M/next[m].board = m.board'
}
}
}
run {} for 9 but 10 Board

Resources