[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [tlaplus] Re: Simpler method of making multiple changes to a function in a single step


Really cool stuff thanks. I suspect that model checking will be a bottleneck as this piece is part of a larger algorithm so the more efficient version may well prove critical. I've definitely learned new techniques so thanks.


On Thursday, January 17, 2019 at 9:21:07 AM UTC+1, Stephan Merz wrote:
Oh, I see, you wish to assign resources to a unique node and to use all resources: that makes sense. (Note that in the first conjunct, the disjunct "m=n" is superfluous.)

By the way, another way to define this operator is to start by computing the number of resources that each node obtains in a balanced allocation. Which one you prefer is essentially a matter of taste. TLC may be able to evaluate the predicate below a little faster, but it must still enumerate all functions in [N -> SUBSET R].

IsBalanced(reg) ==
  LET k == Cardinality(R) \div Cardinality(N)
  IN  /\ \A n \in N : Cardinality(reg[n]) \in {k, k+1}
      /\ \A m,n \in N : m=n \/ reg[m] \cap reg[n] = {}
      /\ \A r \in R : \E n \in N : r \in reg[n]

Pushing this idea further, one can define the set of balanced allocations as

BalancedAllocations ==
  LET k == Cardinality(R) \div Cardinality(N)
      kSets == { S \in SUBSET R : Cardinality(S) \in {k, k+1} }
      Disjoint(reg) == \A m,n \in N : m=n \/ reg[m] \cap reg[n] = {}
      Surjective(reg) == (UNION { reg[n] : n \in N }) = R
  IN  { reg \in [N -> kSets] : Disjoint(reg) /\ Surjective(reg) }

and this definition definitely speeds up evaluation because now TLC only enumerates functions of type [N -> kSets], of which there are much fewer. Again, as long as model checking is not a real bottleneck, I'd prefer the definition that you gave because it is easier to understand.


On 16 Jan 2019, at 22:32, Jack Vanlightly <vanl...@xxxxxxxxx> wrote:

I think I have it. I have updated IsBalanced and BalancedAllocations to as follows:

IsBalanced(reg) == 
  /\ \A m,n \in N : \/ m = n
                    \/ Cardinality(reg[m]) - Cardinality(reg[n]) \in {-1,0,1}
  /\ \A m,n \in N : \/ m = n
                    \/ reg[m] \intersect reg[n] = {}
  /\ \A r \in R : \E n \in N : r \in reg[n]

BalancedAllocations == 
    { reg \in [N -> SUBSET R] : IsBalanced(reg) }

It will generate a register such as: [n1 |-> {"r1"}, n2 |-> {"r2"}, n3 |-> {"r3", "r4"}]

The first line of IsBalanced ensures resources are well balanced,
The second ensures that resources are not assigned twice.
The third ensures that all resources are assigned.

It is much shorter and clearer than my first attempt, thank you very much for the help. (If you see any further improvements do let me know!)


On Wednesday, January 16, 2019 at 5:35:42 PM UTC+1, Jack Vanlightly wrote:

I have a set of nodes N, a set of resources R and a function "register" that maps N to R. The algorithm is a resource allocation algorithm that must assign the resources of R evenly across the nodes N.

So if N is { "n1", "n2", "n3"} and R is {"r1", "r2", "r3", "r4" } then once allocation has taken place a valid value for register would be:
[n1 |-> <<"r4", "r1">>, n2 |-> <<"r2">>, n3 |-> <<"r3">>]

I want to set the values of register in a single step and I have managed it, though the formula seems overly complex and I wonder if there is a simpler way of doing that would help me also gain more insight into TLA+.

I have isolated the allocation logic into a toy spec as follows:

EXTENDS Integers, FiniteSets, Sequences, TLC
VARIABLE register

Init ==
  register = [n \in N |-> << >>]

HasMinResources(counts, nd) ==
  \A x \in N : counts[nd] <= counts[x] 

Allocate ==
   LET newRegister == [n \in N |-> << >>]
       counts == [n \in N |-> 0]
       al[pendingRes \in SUBSET R, assignCount \in [N -> Nat]] ==
            LET n == CHOOSE nd \in N : HasMinResources(assignCount, nd)
                r == LET int == R \intersect pendingRes
                      IN CHOOSE x \in int : TRUE 
                IF Cardinality(pendingRes) = 0 THEN newRegister
                    LET remaining == pendingRes \ { r }
                        newAssignCount == [assignCount EXCEPT ![n] = @ + 1]
                    IN [al[remaining, newAssignCount] EXCEPT ![n] = Append(@, r)]
   IN al[R, counts]
Rebalance ==
  /\ register' = Allocate
  /\ PrintT(register')

(* ignore the spec, I just wanted to run the Rebalance action once *)
Spec == Init /\ [][Rebalance]_register

- I made Allocate recursive as that is the only way I could figure out making all the changes to register in a single step.
- I did the intersect so that I could use CHOOSE. Else it complained that is was an unbounded CHOOSE so I figured if I did an intersect with R then it would be interpretted as bounded.

Any insights or suggestions would be great.


You received this message because you are subscribed to the Google Groups "tlaplus" group.
To unsubscribe from this group and stop receiving emails from it, send an email to tlaplus+u...@googlegroups.com.
To post to this group, send email to tla...@xxxxxxxxxxxxxxxx.
Visit this group at https://groups.google.com/group/tlaplus.
For more options, visit https://groups.google.com/d/optout.