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.
Related
I am trying to sum all the numbers in a set in Alloy.
For instance, in the signature abc, I want the value to be the sum of a.value + b.value + c.value, which is 4+1+3=8.
However, if I use "+", it gives me the union set and not the sum.
PS. I know there is the "plus" (as I used it in sig sumab), but this only allows me to sum two values.
Thanks
open util/integer
sig a{value: Int}
{
value = 4
}
sig b{value: Int}
{
value = 1
}
sig c{value: Int}
{
value = 3
}
sig abc{value: set Int}
{
value = a.value + b.value + c.value
}
sig sumab{
value : Int
}
{
value = plus[a.value, b.value]
}
pred add{}
run add for 4 int, exactly 1 sumab, exactly 1 a, exactly 1 b, exactly 1 c, exactly 1 abc
Note: I wrote this in pseudo-code, it may help to get to an answer:
fun plusN [setInt : set de Int] : Int { // function "plusN" should take a set of integers "setInt", return an integer
if #setInt = 2 //if only two numbers in set, sum them
then plus[max setInt , min setInt]
else // if more than 2, use recursion
plusN [max setInt , plusN[{setInt - max setInt}]]
}
Note 2. The function sum may seem to be a good idea, but if I sum 1+1+1=1, the result will be 1 intead of 3 as the only number in the set is 1.
Alloy comes with a built-in sum function, so just do sum[value].
As mentioned in this answer, Alloy has a sum keyword (different than the sum function) which can be used.
The general format is:
sum e: <set> | <expression involving e>
Variable sig values
Here's a simple example that sums the number of sales during a day for a restaurant's three meals. In particular, note the use of the sum keyword in fun sum_sales.
one sig Restaurant { total_sales: Int }
abstract sig Meal { sales: Int }
one sig Breakfast, Lunch, Dinner in Meal {}
// Each meal only falls in one category
fact { disj[Breakfast, Lunch, Dinner] }
// Every meal is in some category
fact { Breakfast + Lunch + Dinner = Meal }
// Keep the numbers small because the max alloy int is 7
fact { all m: Meal | m.sales <= 4 }
// Negative sales don't make sense
fact { all m: Meal | m.sales >= 0 }
fun sum_sales: Int { sum m: Meal | m.sales }
fact { Restaurant.total_sales = sum_sales }
This works even when all meals have the same number of sales (1 + 1 + 1 = 3), as shown in this check.
check { (all m: Meal | m.sales = 1) => Restaurant.total_sales = 3 }
Here are some other ways to play around with the example.
check {
{
// One meal with three sales
#{ m: Meal | m.sales = 3 } = 1
// Two meals with one sale
#{ m: Meal | m.sales = 1 } = 2
} => Restaurant.total_sales = 5
}
run { Restaurant.total_sales = 5 }
Constant sig values
Another way you might want to use this is to have the value associated with each type of Meal be constant, but allow the number of Meals to vary. You can model this with a relation mapping each meal type to a number of sales as follows.
one sig Restaurant { total_sales: Int }
abstract sig Meal {}
lone sig Breakfast, Lunch, Dinner in Meal {}
// Each meal only falls in one category
fact { disj[Breakfast, Lunch, Dinner] }
// Every meal is in some category
fact { Breakfast + Lunch + Dinner = Meal }
fun meal_sales: Meal -> Int {
Breakfast -> 2
+ Lunch -> 2
+ Dinner -> 3
}
fun sum_sales: Int { sum m: Meal | m.meal_sales }
fact { Restaurant.total_sales = sum_sales }
check { #Meal = 3 => Restaurant.total_sales = 6 }
run { Restaurant.total_sales = 5 }
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
I'm novice at Alloy (4.2) Sat Solver
I have a problem with the scope of Integers
I want to guarentee that the age of a given person is always more than 18 years
Exemple:
sig Customer {age : Int }
fact f { all c:Customer | c.age > 18 }
pred NewRule { all c:Customer | c.age = 15 }
run NewRule for exactly 1 Customer
The problem is that the Integers in Alloy take only values between [-8,+7]
If I change the fact & mak c.age >0
and change the pred & make c.age =7
so the system is concistant
thinks
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
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