Why are multiple Alloy declarations not equivalent to nested Alloy declarations? - alloy

Page 293 of Software Abstractions says that these two are equivalent:
all x: X, y: Y | F
all x: X | all y: Y | F
but these two are not equivalent:
one x: X, y: Y | F
one x: X | one y: Y | F
Why are the latter two not equivalent?
I learn best with concrete examples, so let’s take a concrete example.
Last week I took a cruise to Alaska. At dinner I was seated with five other people. One of the couples (Jason and Denise) were celebrating their 25th (silver) wedding anniversary.
I created an Alloy model of the people at the dining table. The model specifies that there is one m: man, w: woman couple for which m's wife is w, w's husband is m, and m and w are celebrating a silver anniversary:
one m: Man, w: Woman |
m.wife = w and
w.husband = m and
m.anniversary = Silver and
w.anniversary = Silver
The instances that the Alloy Analyzer generated are what I expected.
Then I modified the expression to use nested expressions:
one m: Man |
one w: Woman |
m.wife = w and
w.husband = m and
m.anniversary = Silver and
w.anniversary = Silver
The Alloy Analyzer generated the same instances!
Next, I wrote an assertion, asserting that the two versions are equivalent:
Version1 iff Version2
The Alloy Analyzer returned: No counterexamples found!
Below is my model. Why are the two versions equivalent? Is there a small tweak that I could make to the model to show a difference between the nested version and the non-nested version?
Alloy Model of Roger's Dining Table on the Cruise to Alaska
abstract sig Man {
wife: lone Woman,
anniversary: lone Anniversary
}
one sig Roger extends Man {}
one sig Jason extends Man {}
abstract sig Woman {
husband: lone Man,
anniversary: lone Anniversary
}
one sig Faye extends Woman {}
one sig Nina extends Woman {}
one sig Claudia extends Woman {}
one sig Denise extends Woman {}
abstract sig Anniversary {}
one sig Silver extends Anniversary {}
pred v1_One_couple_celebrating_25th_wedding_anniversary {
one m: Man, w: Woman | m.wife = w and w.husband = m and
m.anniversary = Silver and w.anniversary = Silver }
pred v2_One_couple_celebrating_25th_wedding_anniversary {
one m: Man |
one w: Woman |
m.wife = w and w.husband = m and
m.anniversary = Silver and w.anniversary = Silver }
assert Both_versions_are_identical {
v1_One_couple_celebrating_25th_wedding_anniversary
iff
v2_One_couple_celebrating_25th_wedding_anniversary
}
check Both_versions_are_identical

You may run these examples to see the difference:
sig Man {}
sig Woman {
// married is a relation Woman set -> set Man
married : set Man
}
// there is exactly one married couple
run { one w : Woman, m : Man | w->m in married } for 5
// there is exactly one woman that has exactly one husband;
// apart from that, several men may share a wife, and vice-versa
run { one w : Woman | one m : Man | w->m in married } for 5

The predicate is too constraining for there to be a difference.
The difference between the nested version and the non-nested version can be seen if the example is modified a bit: Allow each man to be married to a set of women:
sig Man {
wives: set Woman
}
Loosen the constraint to this: w in m.wives
Here is the new, non-nested version:
one m: Man, w: Woman |
w in m.wives and
w.husband = m and
w.anniversary = Silver
That says there is exactly one m: Man, w: Woman for which w is one of m's wives, w's husband is m, and m and w are celebrating a silver anniversary.
Here’s one of the instances that the Alloy Analyzer generates: Man1 has wives Woman1 and Woman2. Woman1 has husband Man1. Likewise, Woman2 has husband Man1. Woman1 is celebrating a 25th wedding anniversary with her husband (Man1).
Here is the nested version:
one m: Man |
one w: Woman |
w in m.wives and
w.husband = m and
w.anniversary = Silver
That says there is exactly one m: Man for which the constraint is true, where the constraint is this: there is one woman for which the woman is the man’s wife and they have been married 25 years. There can be another man for which the constraint is true.
Here’s one of the instances that the Alloy Analyzer generates: Man1 has wives Woman0 and Woman1. Man1 and Woman1 have been married 25 years. Man2 has wife Woman2. They have been married 25 years.
Below is the Alloy model.
Alloy Model of Men Married to Multiple Wives
sig Man {
wives: set Woman
}
sig Woman {
husband: lone Man,
anniversary: lone Anniversary
}
abstract sig Anniversary {}
one sig Silver extends Anniversary {}
pred v1_One_couple_celebrating_25th_wedding_anniversary {
one m: Man, w: Woman | w in m.wives and w.husband = m and
w.anniversary = Silver
}
pred v2_One_couple_celebrating_25th_wedding_anniversary {
one m: Man |
one w: Woman |
w in m.wives and w.husband = m and w.anniversary = Silver
}
assert Both_versions_are_identical {
v1_One_couple_celebrating_25th_wedding_anniversary
iff
v2_One_couple_celebrating_25th_wedding_anniversary
}
check Both_versions_are_identical

Related

Ways to model the farmer, goat, cabbage, wolf puzzle?

I am cataloging various ways of modeling the farmer, goat, cabbage, wolf problem.
Below are two ways to model the problem. Are there other, reasonable, ways to model it?
One model defines a set of River objects. Each River object represents a snapshot of the River and its two sides after the farmer has done a ferry.
sig River {
side1: set Item,
side2: set Item
}
Another model has one River object. The items on the two sides of the River vary over time.
one sig River {
side1: Item -> Time,
side2: Item -> Time
}
What are other, reasonable, ways to model the farmer, goat, cabbage, wolf problem?
Let's imagine the puzzle as an ordered series of situations where a situation contains location-item pairs.
open util/ordering[Situation]
abstract sig Location {}
one sig SideA, SideB extends Location {}
abstract sig Item {}
one sig Goat, Cabbage, Wolf, Farmer extends Item {}
sig Situation {
l_i: Location -> Item
}
Puzzle rules are easy to form then:
// in the initial situation everyone is on SideA
one s: Situation & first | all i: Item | s.l_i.i = SideA
// in the final situation everyone is on SideB
one s: Situation & last | all i: Item | s.l_i.i = SideB
// in all other situations the locations of the goat/wolf and the cabbage/goat must be diffent, except when the farmer is also there
all s: Situation - first - last | (s.l_i.Goat != s.l_i.Cabbage) or s.l_i.Goat = s.l_i.Farmer
all s: Situation - first - last | (s.l_i.Goat != s.l_i.Wolf) or s.l_i.Goat = s.l_i.Farmer
// further puzzle constraints ...
I'm curious if there's a more compact way to refer to the first item in an ordering than
one s: Situation & first

Grouping of people multiple times, where all the members meet each other in the same group at least once

I want to group people into smaller subgroups, and after shuffling groups multiple times for successive sessions, make all the people meet each other at least once.
In every session, people are divided into a constant number of groups. Everyone has to join one group in every session.
The group size should be closest to (the number of people)/(# of groups). There should not be a groups of too few people or too many people.
The sessions are continued until every pair of people meets each other at least once.
Preferably, the number of times the same pair meet each other should be minimized.
The following is the answer for this problem for 11 people (numbered 0-10) and 3 groups (3 columns). It requires 5 sessions.
Session 1: 3,6,8,10 0,1,7,9 2,4,5
Session 2: 3,5,7,8 0,1,2,10 4,6,9
Session 3: 0,1,6,8 2,3,4,9 5,7,10
Session 4: 0,3,5,9 1,4,8,10 2,6,7
Session 5: 1,3,5,6 2,8,9,10 0,4,7
Members of two groups of different size must meet each other (1v1, once)
The question above is similar, but I want rather make the people meet just in a group of a larger group, not 1-on-1.
The following is my approach that uses Alloy. This works for a small number of people (~15) and groups (~2), but it quickly causes computation time explosion when the size is increased. I need to calculate it for ~25 people and ~5 groups.
module Teaming
sig Person { groups: some Group }
sig Group { people: some Person }
sig Session { groups: some Group }
one sig Sessions { sessions: some Session }
sig GroupPerSession {}
-- Tree structures
fact {
all s: Session | s in Sessions.sessions
all g: Group | g in Session.groups
all s: Session | all p:Person | p in s.groups.people
people =~ groups
}
-- The total number of people
fact {
all s: Session | #s.groups.people = #Person
}
-- The number of groups per session
fact {
all s: Session | #s.groups = #GroupPerSession
}
-- The number of people in a group
fact {
all g: Group | (#g.people) >= div[#(Person), #(GroupPerSession)] and (#g.people) <= add[div[#Person,#GroupPerSession],1]
}
-- Mutually exclusive grouping in a session
fact separate {
all s: Session | all disj a,b: s.groups | no p: Person | p in a.people and p in b.people
}
-- Every pair of people meets somewhere
pred sameGroup {
all disj a,b: Person | some g: Group | a in g.people and b in g.people
}
-- The same people should not meet too many times
fact sameGroupNotTooMuch {
all disj a,b: Person | #{a.groups & b.groups} <= 3
}
run sameGroup for 6 Int, 5 Session, 15 Group, exactly 3 GroupPerSession, exactly 16 Person
run sameGroup for 6 Int, 6 Session, 24 Group, exactly 4 GroupPerSession, exactly 18 Person
run sameGroup for 6 Int, 7 Session, 35 Group, exactly 5 GroupPerSession, exactly 18 Person
I guess dynamic programming should work, though I cannot find anything specific. Any pointer for the improvement in Alloy code or other algorithms would be great.
Here's my quick shot at solving this problem.
Overall, the generation of instance seems faster, but still have difficulty to complete when trying to assign >20 people into >4 groups.
module Teaming
one sig Settings{
maxEncounter:Int,
minGroupSize:Int,
maxGroupSize:Int
}{
// Manually filling values there helps (1)reducing the integer bit-width needed (2) decrease the complexity (in terms of clauses)
maxEncounter=4
//minGroupSize=5
//maxGroupSize=5
minGroupSize=div[#Person, #Group]
maxGroupSize=add[minGroupSize,1]
}
sig Session{}{
Group.people[this]=Person // all person are assigned in group during a session
no disj g1,g2 :Group| g1.people[this] & g2.people [this] !=none // a person can't be in two disjoint groups of a same session
}
sig Group {
people: Session some -> some Person
}{
all s:Session| #people[s]<= Settings.maxGroupSize and #people[s]>=Settings.minGroupSize
}
sig Person {}
pred allMeet {
all disj a,b: Person | people. a & people.b != none->none
}
pred allMeetAndMinEncounter {
all disj a,b: Person | let x= (people. a & people.b) {
#x <=Settings.maxEncounter
x != none ->none
}
}
run allMeet for 6 Int, 9 Session, exactly 4 Group, exactly 20 Person
Highlight of the changes brought:
Removed quantifications when possible
Removed redundant constraints
Replaced the two binary relations groups and people by a ternary relation. This comes with several advantages:
It reduces the number of group atoms present in an instance to the total of group per session.
It enables the use of instance projection in the instance viewer. You'll now be able e.g. to view group assignment for each session separately.
I do not think Alloy is the right tool for optimisation. It is a specification tool that focuses on finding counter examples. However, it can of course be found to find solutions to puzzles like this. (I think there is a group that developed an extension to Alloy that minimises the found solutions.)
I took a stab though I am a beginner. Surprisingly I found a solution with 4 sessions 11 people and 3 groups.
s0 {2,9,10}, {5,6,7,8}, {0,1,3,4},
s1 {2,4,7,8}, {1,3,6,10}, {0,5,9},
s2 {1,2,3,5}, {0,7,8,10}, {4,6,9},
s3 {0,2,6}, {4,5,10}, {1,3,7,8,9}
Since Alloy is not an optimiser I used the binary way to find the minimum number of sessions, I found that you needed at least 7 sessions for 25/5.
This is my full model:
sig P, Group {}
sig Session {
participants : Group -> P
}
fact {
// tuning goes here (max sure <= scope )
# Group = 5
# P = 25
# Session = 7
// In every session, people are divided into a constant number
// of groups.
all s : Session | s.participants.P = Group
// The sessions are continued until every pair of people
// meets each other at least once.
// Preferably, the number of times the same pair meet
// each other should be minimized.
all disj a,b : P | some participants.a & participants.b
// Everyone has to join one group in every session.
all p : P, s : Session | one s.participants.p
// The group size should be closest to (the number
// of people)/(# of groups).
// There should not be a
// groups of too few people or too many people.
all g : Group, s : Session | (# s.participants[g]) >= 3
}
run {} for 5 Group, 25 P, 24 Session, 6 int
Specifying the int width for these number is crucial.
Finding a series of 8 sessions took 5 seconds, finding the 7 sessions took much longer, 34 seconds. Forcing a more equal group size by increasing the minimum is still running :-)
I think the tool does exactly what it is supposed to do: finding a solution. It is not that good in finding an optimal solution.

How do relation multiplicities work?

I'm using this simple model and the evaluator to study relation multiplicities. The evaluator dialogue demonstrates a situation that I find puzzling.
sig A {}
sig B {}
pred show {}
run show
Evaluator dialogue:
univ
{A$0, A$1, B$0, B$1, B$2}
B->A
{B$0->A$0, B$0->A$1, B$1->A$0, B$1->A$1, B$2->A$0, B$2->A$1}
B lone -> A
{B$0->A$0, B$0->A$1, B$1->A$0, B$1->A$1, B$2->A$0, B$2->A$1}
// Although the two relations seem to be identical,
B->A in B->A
true
// but
B->A in B lone -> A
false
What makes the two products different in answering 'B->A in B->A' and 'B->A in B lone -> A'?

How to align blocks in PlantUML class diagrams?

I am using PlantUML to make simple class diagrams and the tool is awesome, but I couldn't find any way to align classes with each other except putting them into packages or using relationships like Alice -left-* Bob. What I need is something like:
#startuml
class Bob
class Alice
class Dan
**Dan aligned Alice: horizontally**
'or using a grid?
**Bob at grid (2, 3)**
#enduml
Is there a way?
UPDATES Aug.08.2019
From Rotsiser's comment, by combining changing the length of lines with together keyword, it can align elements
#startuml
class A
A ..> B
C ---> B
D ...> B
together {
class E
class F
class G
}
E ----> B
#enduml
OUTDATED
You are able to align elements by changing the number of line's character, such as '-', '.', and so on.
#startuml
class A
A ..> B
C ---> B
D ...> B
E ----> B
F ----> B
G ----> B
#enduml
Using a -[hidden] relation can do the job :
#startuml
class Bob
class Alice
class Dan
class Foo
class Bar
class Foobar
Bob -[hidden] Alice
Bar -[hidden] Foobar
#enduml
No, there's no way to do that, sorry :( The idea behind PlantUML is that you should not care too much about the layout rendering.
Actually, early versions of PlantUML use to align classes, but it was an issue: When there were many unrelated classes, diagrams tended to be very large and very thin. So a patch was added to organize classes in a square.
How many classes do you want to have in your diagram? Sure it would be possible to disable the organizing patch for e.g. 3 to 5 classes. You could post a suggestion to the forum to see what other users think about it.
Here is a solution.
The documentation:
"It is also possible to change arrow direction by adding left, right, up or down keywords inside the arrow:"
#startuml
foo -left-> dummyLeft
foo -right-> dummyRight
foo -up-> dummyUp
foo -down-> dummyDown
#enduml
To your question:
#startuml
class Bob
class Alice
class Dan
Alice -left[hidden]-> Bob
Alice -right[hidden]-> Dan
#enduml
It may also be useful:
#startuml
class Bob
class Alice
class Dan
Bob -right-|> Alice
Alice -right-> Dan
interface Friend
Dan -up..> Friend
interface Person
Friend -left-> Person
interface Object
Person -down-> Object
interface Native
Object -right-> Native
#enduml
You don't need a hidden package, use the together keyword:
together {
class A
class B
}
A cleaner approach is to put them in a hidden package, which is more logical.
#startuml
skinparam shadowing false
skinparam package<<Layout>> {
borderColor Transparent
backgroundColor Transparent
fontColor Transparent
stereotypeFontColor Transparent
}
package x <<Layout>>{
class A
class B
}
A .. D
B .. C
C .. D
A1 .. D1
B1 .. C1
C1 .. D1
#end

How can one express that a relationship must not be cyclic?

Consider a upgrades relationship:
I need to make sure that upgrades cannot be circular. How can I do that in Alloy?
It is sufficient to enforce transitivity and antireflexivity.
fact {
no a: Item | a in a.upgrades
}
fact{
all a,b,c: Item |
a in b.upgrades and b in c.upgrades implies
a in c.upgrades
}
From your example, I infer that the upgrades relation is not intended to be transitive: in the example, a diamond sword upgrades a stone sword, and a stone sword upgrades a wooden sword, but the pair WoodSword -> DiamondSword is not in the upgrades relation.
So what you want to say is something like
fact upgrades_acyclic {
no x : univ | x in x.^upgrades
}
Some modelers prefer the more succinct formulation in terms of relations:
fact upgrades_acyclic { no ^upgrades & iden }

Resources