An airplane flies a sequence of legs. Each leg must be followed by an appropriate next leg. A NextLegTable contains the appropriate (Leg -> Leg) pairs.
So, each pair of legs in a flight must be in the NextLegTable. I implemented this constraint in the below Fact.
Here is a description of my implementation: Each pair of legs (leg and leg'), such that indexOf(leg) + 1 = indexOf(leg'), must be in the NextLegTable. Apparently that approach is wrong, as I get "no instances."
What is the right approach? Given a leg, how do I find its next leg in the sequence?
sig Flight {
legs: seq Leg
}
sig Leg {}
one sig NextLegTable {
nextLeg: Leg -> Leg
}
fact Flight_legs_In_NexLegTable {
all f: Flight |
all leg, leg': Leg {
leg in f.legs.elems
leg' in f.legs.elems
plus[f.legs.idxOf[leg], 1] = f.legs.idxOf[leg']
(leg -> leg') in NextLegTable.nextLeg
}
}
pred Show (f: Flight) {#f.legs > 1}
run Show
These are the two lines to look at:
plus[f.legs.idxOf[leg], 1] = f.legs.idxOf[leg']
(leg -> leg') in NextLegTable.nextLeg
This solution is currently attempting to find an instance where all Legs of a Flight that follow another Leg are also shown in the NextLegTable sig. However, you have not told it to actually put these mappings into the table. You need to say that when a Leg follows another Leg, put Leg -> Leg' into NextLegTable:
(plus[f.legs.idxOf[leg], 1] = f.legs.idxOf[leg']) =>
(leg -> leg') in NextLegTable.nextLeg
This tells the program to place the mappings into the table whenever the first condition is met.
Related
I am modeling a Google Docs-like inline commenting system in Alloy. Specifically, I'm modeling the UI interactions that the user can do. The constraints, in English, are that:
users can have any number of comments
users can have any number of comments composing at once
comments can be either composing or editing, not both
(eventually) users can expand one comment at a time to view it—text is truncated otherwise.
so far I have only modeled adding a new comment—or so I think! When I run the model below, I expect to see that Alloy cannot find any counterexamples, but it finds one where a comment is in both the composed and composing relations at a single time.
I think there may be something off in my mental model of how Alloy works in this case. I can't see how a comment would possibly be added to the composed relation here: I think init would set all the drafts to have empty relations there, and that the actions in Traces would preclude a comment ever being added to composed. BUT! I'm clearly wrong, since Alloy finds an example where composed is non-empty. Does anyone know what's actually happening here?
module inlineComments
open util/ordering [Time] as TimeOrdering
sig Time {}
sig Draft {
composing: set Comment -> Time,
composed: set Comment -> Time,
-- expanded: lone Comment -> Time,
}
sig Comment {}
-- What can we do with these?
pred newComment (t, t': Time, c: Comment, d: Draft) {
-- comment is not already known to us
c not in d.composing.t
c not in d.composed.t
-- start composing the comment
d.composing.t' = d.composing.t + c
}
pred init (t: Time) {
all d: Draft, c: Comment | c not in d.composing.t
all d: Draft, c: Comment | c not in d.composed.t
}
fact Traces {
init[TimeOrdering/first]
all t: Time - TimeOrdering/last |
let t' = TimeOrdering/next[t] |
some d: Draft, c: Comment |
newComment [t, t', c, d]
}
-- Is the world how we expect it to be?
assert validState {
-- comments cannot be composing and composed at the same time
all t: Time, d: Draft | d.composing.t & d.composed.t = none
}
check validState for 3 but 1 Draft
This is known as the "frame problem": you've specified that new comments can be put into composing, but not that nothing else happens! You have to make it explicit that the only way the system may change is via newComment. Here's one way you could do that:
fact Traces {
init[TimeOrdering/first]
all t: Time - TimeOrdering/last |
let t' = TimeOrdering/next[t] |
some d: Draft, c: Comment {
newComment [t, t', c, d]
d.composed.t = d.composed.t' -- d.composed doesn't change
}
}
Note this only works because you've exlicitly scoped to a maximum of 1 draft. If you test with two drafts, you also have to show that none of the drafts change in any way. If that will always be the case that there's only one Draft, you can write one sig Draft to enforce that.
[Update] Thanks to #Loïc Gammaitoni for providing the answer. Based on Loïc's insight, this is the model I created to test for equivalence of the two approaches:
sig Key {}
sig Room {
keys: set Key
}
pred DisjointKeySet_v1 {
keys in Room lone -> Key
}
// See page 274 of Software Abstractions.
// It says that this: sig S {f: disj e}
// is equivalent to the constraint:
// all a,b: S | a != b => no a.f & b.f
pred DisjointKeySet_v2 {
all r, r': Room | r != r' => no r.keys & r'.keys
}
assert Equivalent {
DisjointKeySet_v1 iff DisjointKeySet_v2
}
check Equivalent
Each guest at a hotel is given a set of keys:
sig Key {}
sig Room {
keys: set Key
}
We must constrain the sets of keys so that no two guests get an identical key. Here's one approach to implementing that constraint:
fact DisjointKeySet {
keys in Room lone -> Key
}
A second approach to implementing the constraint involves modifying the declaration of keys:
sig Room {
keys: disj set Key
}
Are the two approaches equivalent?
I believe they are.
To be certain, I want the Alloy Analyzer to compare the two approaches and search for counterexamples.
How to do this? How to write the proper assert?
Below is an assert that I created. I think that it properly checks for equivalence of the two approaches. Do you agree? My assert seems rather round about. Is there a more direct way of showing equivalence?
sig Key {}
sig Room {
keys: disj set Key
}
// If each room has a disjoint set of keys,
// then each key may be used with only
// one room
assert Equivalent {
no k: Key, disj r, r': Room |
(k in r.keys) and (k in r'.keys)
}
check Equivalent
The keyword disj is in this context a shorthand for all a, b: Room| a!=b implies no a.keys & b.keys (c.f. Appendix B of Software Abstraction)
To answer your question, you could thus check the assertion:
assert equivalence {
(keys in Room lone -> Key) <=> (all a, b: Room| a!=b implies no a.keys & b.keys)
}
Results will suggest that both approaches are equivalent.
I am trying to learn about how ordering works in Alloy. I have a time signature which I have used to instantiate the ordering module. I want the predicate addPage to add a page to the book at time t' where t' = t.next. (Basically add a page to the Book on the next time) However it is not working as expected and instead Time2 has lesser number of pages than Time1. Can someone explain to me why this is happening? Thanks.
open util/ordering[Page] as P0
open util/ordering[Time] as T0
sig Page {}
sig Time {}
sig Book
{
pages: Page -> Time
}
pred addPage(b:Book, p:Page, t: Time)
{
t != T0/last implies
{
let t' = t.next |
b.pages.t' = b.pages.t + p
}
}
run addPage {} for 3
The problem are the extra curly braces in the run statement.
I think Alloy executes an empty predicate in this case.
Try:
run addPage for 3
instead. You will see a visualization where the selected instances for b, t and p are marked.
You're trying to change state which can only be simulated in constraint logic.
Please notice that the expression in addPage is basically ineffective /run your model without it/ and that there's only one Book atom in the solution.
Here's a model you can start with and gradually refine.
open util/ordering[Time]
sig Page {}
sig Time {}
sig Book {
pages : Page lone -> Time // each Time atom is mapped to at most one Page atom
}
pred addPage(b0, b1 : Book, pg : Page, t0, t1 : Time) {
one pg and // one page at a time (it's likely redundant)
not pg in b0.pages.Time and // it's a 'new' page
b0.pages + pg->t1 = b1.pages and // 'new state' of b0
t1 = t0.next // pg is 'added' with the next time stamp
}
run addPage for 3 but 2 Book
I used the optional 'and' operators, placed t1 = t0.next at the end of the constraint, positioned b1.pages /representing the 'new state'/ on the right and used quotes in the comments to emphasize that there's no real state change and sequence of operation in the sense imperative programming works.
I'm trying to notice a change in atom relations to other atoms. Using the below signatures :
sig Word, Definition{}
sig Dictionary {
def: Word -> lone Definition
}
I then use a predicate to show what happens when you add a new relation to a Dictionary by having another Dictionarywhich is the same but with one more relation.
pred addRelation [d,d':Dictionary,w:Word,f:Definition] {
d'.word = d.word + w -> f
}
To see if the number of Word atoms used by the first Dictionary increase I can show only the instances where this occurs using:
#d'.def.Definition > #d.def.Definition
However, is there a way to see if the number of Definition atoms used by the second Dictionary atom has increased? I've been using trial and error in the Alloy Evaluator to try and find a value for this but have come up short.
Thanks!
Like this?
sig Word, Definition{}
sig Dictionary {
def: Word -> lone Definition
}
pred addRelation [d,d':Dictionary,w:Word,f:Definition] {
d'.def = d.def + w -> f
#d'.def[Word] > #d.def[Word]
}
run addRelation
I am trying to express certain mathematical properties of relations in Alloy, but I am not sure if I have the right approach yet, as I am just a beginner. Would appreciate any insights from the community of experts out there!
Specifying the fact that domain of a relation as singleton. e.g. Is the following a reasonable and correct way to do that?
pred SingletonDomain(r: univ->univ) {
one ~r
}
sig S2 {}
sig S1 {
child: set S2
}
fact {
SingletonDomain [child]
}
or should it be something like the following
pred SingletonDomain (r: univ->univ) {
#S2.~r = 1
}
This is not very modular since the use of S2 is very specific to the particular signature.
Specifying the fact that a relation is a total order. At the moment I am using the following, basically I am simulating xor
pred total[r: univ->univ] {
all disj e, e': Event | (e.r = e' and e'.r != e) or (e.r != e' and e'.r = e)
}
Thanks
To specify the fact that the domain of a given relation is a singleton, your first attempt is really close to do the trick. The only problem is that one ~r enforces the inverse of the r relation (and thus the r relation itself) to be composed of a single tuple. This is not what you want to express.
What you want to express is that all elements in the range of the r relation have the same (so only one) image through its inverse relation '.
You should thus write the following predicate :
pred SingletonDomain(r: univ->univ) {
one univ.~r
}
For your second question, your predicate doesn't handle cases were e -> e' -> e '' -> e. To handle those, you can use transitive closure.