Gilbreath Card Trick: Difference between revisions

No edit summary
No edit summary
Line 5: Line 5:
* you reverse one of the sequences
* you reverse one of the sequences
* you perform a (not necessarily perfect) riffle shuffle of the two sequences
* you perform a (not necessarily perfect) riffle shuffle of the two sequences
* the resulting sequence will itself consist of a sequence of four quartets with one card from each suite.
* the resulting final sequence is guaranteed to consist of a sequence of four quartets with one card from each suite.


I attempted to model this problem in B and wanted to use model checking to validate the property. As I wanted to measure the time spent modeling I used a stopwatch. It took 13 minutes (starting from an empty B specification) to produce a first model that could be model checked by ProB. Full validation was finished after 19 minutes from the start. The model checking itself generated 150,183 states and 179,158 transitions and took 2 minutes and 17 seconds on a Mac Book Air (1.8 GHz i7).
I attempted to model this problem in B and wanted to use model checking to validate the property on the final sequence. As I wanted to measure the time spent modeling I used a stopwatch. It took 13 minutes (starting from an empty B specification) to produce a first model that could be model checked by ProB. Full validation was finished after 19 minutes from the start. The model checking itself generated 150,183 states and 179,158 transitions and took 2 minutes and 17 seconds on a Mac Book Air (1.8 GHz i7).
I am interested in seeing how much time is required to solve this in other formalisms and with other model checking tools (e.g., Promela with Spin, CSP with FDR, TLA+ with TLC).
I am interested in seeing how much time is required to solve this in other formalisms and with other model checking tools (e.g., Promela with Spin, CSP with FDR, TLA+ with TLC).



Revision as of 11:47, 22 May 2013

My belief is that B is a very expressive language, which can be very convenient for modelling many problems. Hence, when in the Dagstuhl library I stumbled upon a nice short article by Tony Hoare and Natarajan Shankar in memory of Amir Pnueli, I decided to model the problem and time how long it would take me to solve the problem using ProB. The article unravels a card trick by Gilbreath. The card trick has several phases:

  • first you arrange 16 cards into a sequence of quartets with one card from each suit (Spade, Club, Heart, Diamond) in the same order. The example in the article is as follows: ⟨5♠⟩,⟨3♡⟩,⟨Q♣⟩,⟨8♢⟩, ⟨K♠⟩,⟨2♡⟩,⟨7♣⟩,⟨4♢⟩, ⟨8♠⟩,⟨J♡⟩,⟨9♣⟩,⟨A♢⟩
  • you split the cards into two sequences. The example in the article is: ⟨5♠⟩,⟨3♡⟩,⟨Q♣⟩,⟨8♢⟩,⟨K♠⟩ and ⟨2♡⟩,⟨7♣⟩,⟨4♢⟩,⟨8♠⟩,⟨J♡⟩,⟨9♣⟩,⟨A♢⟩ .
  • you reverse one of the sequences
  • you perform a (not necessarily perfect) riffle shuffle of the two sequences
  • the resulting final sequence is guaranteed to consist of a sequence of four quartets with one card from each suite.

I attempted to model this problem in B and wanted to use model checking to validate the property on the final sequence. As I wanted to measure the time spent modeling I used a stopwatch. It took 13 minutes (starting from an empty B specification) to produce a first model that could be model checked by ProB. Full validation was finished after 19 minutes from the start. The model checking itself generated 150,183 states and 179,158 transitions and took 2 minutes and 17 seconds on a Mac Book Air (1.8 GHz i7). I am interested in seeing how much time is required to solve this in other formalisms and with other model checking tools (e.g., Promela with Spin, CSP with FDR, TLA+ with TLC).

Here is the specification

MACHINE CardTrick
/* Translation of Example in
 "Unraveling a Card Trick" by Tony Hoare and Natarajan Shankar
  in LNCS 6200, pp. 195-201, 2010. 
  DOI: 10.1007/978-3-642-13754-9_10
  http://www.springerlink.com/content/gn18673357154448/
  */
SETS
 SUIT={spade,club,heart,diamond}
DEFINITIONS
  all == [spade,club,heart,diamond]; /* an arbitrary permutation of the suits */

  /* check that in dst we can partition the deck into quartets where every suit occurs once: */
  ok(dst) == #(a,b,c,d).(dst = a^b^c^d & size(a)=4 & size(b)=4 & size(c)=4 & size(d)=4 &
              a : perm(SUIT) & b:perm(SUIT) & c:perm(SUIT) & d:perm(SUIT))
CONSTANTS
 initial
PROPERTIES
 initial = all^all^all^all /* we fix the sequence; i.e., we perform symmetry reduction by hand; it should be possible to achieve this by ProB's symmetry reduction itself using a deferred set */
VARIABLES x,y,dest,reversed
INVARIANT
 x:seq(SUIT) & y:seq(SUIT) & dest:seq(SUIT) & reversed:BOOL &
 ((x=<> & y=<>) => ok(dest)) /* the property we are interested in: after the riffle shuffle the sequence consists of four quartets, each containing every suit */
INITIALISATION
    x,y : (x^y = initial)  /* split the initial sequence into two: x and y */
 || dest := <> || reversed := FALSE
OPERATIONS

  /* reverse one of the two decks */
  Reverse = PRE reversed=FALSE THEN CHOICE x := rev(x) OR y := rev(y) END || reversed := TRUE END;

  /* perform the riffle shuffle: transfer one card from either x or y to dest */
  Shuffle1 = PRE x/=<> & reversed=TRUE THEN dest := dest<-last(x) || x:= front(x) END;
  Shuffle2 = PRE y/=<> & reversed=TRUE THEN dest := dest<-last(y) || y:= front(y) END
END

Some observations:

  • in the above model I perform symmetry reduction by hand, by forcing a particular order of the cards initially
  • a version of the model which does not do this can be found below; it requires symmetry reduction to be enabled for efficient model checking
  • I have tried to translate this example to TLA+ and use TLC (using our new B-TLC translator), but TLC cannot deal with the initialisation x,y : (x^y = initial) .
  • A (longer) Why3 encoding can be found here http://proval.lri.fr/gallery/unraveling_a_card_trick.en.html.

A version requiring symmetry reduction

MACHINE CardTrickSym /* a version to be used with ProB's symmetry reduction */
/* see comments in machine CardTrick for more details about the modeling effort */
/* Translation of Example in "Unraveling a Card Trick" by Tony Hoare and Natarajan Shankar in LNCS 6200, pp. 195-201, 2010. 
  DOI: 10.1007/978-3-642-13754-9_10
  http://www.springerlink.com/content/gn18673357154448/
  */
/* Model checking with hash symmetry taking 161.9 seconds to traverse
   150,183 states and 179,181 transitions (on a MacBook Air 1.8Ghz i7) */
SETS
 SUIT /* ={spade,club,heart,diamond} */
DEFINITIONS
  /* check that in dst we can partition the deck into quartets where every suit occurs once */
  ok(dst) == #(a,b,c,d).(dst = a^b^c^d & size(a)=4 & size(b)=4 & size(c)=4 & size(d)=4 &
              a : perm(SUIT) & b:perm(SUIT) & c:perm(SUIT) & d:perm(SUIT))
CONSTANTS
  all
PROPERTIES
  card(SUIT)=4 &
  all : perm(SUIT) /* a sequence of all suits in any order */
VARIABLES x,y,dest,reversed
INVARIANT
 x:seq(SUIT) & y:seq(SUIT) & dest:seq(SUIT) & reversed:BOOL &
 ((x=<> & y=<>) => ok(dest))
INITIALISATION x,y : (x^y = all^all^all^all ) || dest := <> || reversed := FALSE
OPERATIONS
  Reverse = PRE reversed=FALSE THEN CHOICE x := rev(x) OR y := rev(y) END || reversed := TRUE END;
  Shuffle1 = PRE x/=<> & reversed=TRUE THEN dest := dest<-last(x) || x:= front(x) END;
  Shuffle2 = PRE y/=<> & reversed=TRUE THEN dest := dest<-last(y) || y:= front(y) END
END