Writing algorithms with the TLA toolbox

The following exercises use the TLA toolbox for writing and proving PlusCal algorithms. A local installation of the toolbox is available under ~herbrete/public/TLA/toolbox. Add this path to your environment variable PATH (in your local configuration file) and update your environment (applying the source command on the updated configuration file). The TLA toolbox is launched by entering the command: toolbox in a terminal. A PlusCal user's manuals are available at ~herbrete/public/TLA/c-manual.pdf and ~herbrete/public/TLA/pluscal.pdf

Aside from the resources provided above, books, tutorials and tools can be downloaded from the TLA webpage.

Introduction to the PlusCal language

PlusCal is an algorithmic language. It differs from programming languages since:

The TLA toolbox offers three main features:

Launch the TLA toolbox and create a new specification using menu File > Open Spec > Add New Spec... Enter filename euclid.tla (with full access path) and confirm the creation of the new specification. An empty module euclid has been created. Copy and paste Euclid's algorithm below inside the module.

EXTENDS Naturals, TLC

CONSTANT MAXINT  (* Maximal integer *)

(* PlusCal options (-termination) *)
(* --algorithm EuclidAlg { variables u \in 1..MAXINT; (* 1st integer *) v \in 1..MAXINT; (* 2nd integer *) { print <<u, v>>; while (u /= 0) { if (u < v) { u := v || v := u }; u := u - v } } } *)
In this algorithm:

Use menu File > Parse Module to check that your algorithm is syntactically correct. Notice that some errors may not be detected by this check. Then, add the following two lignes between the closing comment *) and the end of the module:

\* BEGIN TRANSLATION
\* END TRANSLATION

Convert the PlusCal algorithm into a TLA+ specification using menu File > Translate PlusCal Algorithm. A TLA+ expression equivalent to the PlusCal algorithm has been output in between the two lignes that have just been added. The errors that are not detected during module parsing are detected during the translation. When the PlusCal algorithm is syntactically correct, the status bar at the bottom of the window is green (otherwise, it is yellow or red, and error messages are displayed).

Model-checking PlusCal algorithms

Add two new variables u_init and v_init which are initialized to the values of u and v respectively. Then, add the following statements after the while statement:

print <<u_init, v_init, "have gcd", v>>;
assert v = gcd(u_init, v_init);

Define gcd(x,y) between the closing comment *) and \* BEGIN TRANSLATION by copying and pasting the following TLA+ specification:

gcd(x,y) == CHOOSE i \in 1..x:
                   /\ x % i = 0
                   /\ y % i = 0
                   /\ \A j \in 1..x: /\ x % j = 0
                                     /\ y % j = 0
                                     => i >= j

  1. Translate the definition of gcd(x,y) in natural language
  2. The algorithm is one of the two inputs required for verification. What is the other input? How is it specified in this example?
  3. Create a model using menu TLC Model Checker > New Model... or open an existing model using TLC Model Checker > Open Model... In panel Model Overview, choose a value for MAXINT by double-clicking on MAXINT <-.Then click on the green arrow to launch model-checking. What is the result of model-checking?
  4. What can you conclude on your algorithm?
  5. Determine, by successive experimentations, the maximal value of MAXINT for which model-checking can be achieved by TLC checker using reasonable time and space.

An incorrect binary search algorithm

Consider the PlusCal algorithm below that performs a binary search of the value x in the array t. The algorithm is purposely incorrect. The goal is to understand the errors issued by the TLC model-checker and to fix the algorithm using the error traces given by TLC.

EXTENDS Naturals, TLC

CONSTANT N      (* Size of arrays *)
CONSTANT MAXINT (* Max integer value *)

(* PlusCal options (-termination) *)

(*
--algorithm Binsearch {
variables t \in [ 1..N -> 0..MAXINT ];  (* Array of N integers in 0..MAXINT *)
          x \in 0..MAXINT;              (* Value to find *)
          found = FALSE;
          l = 1;                        (* All elements to the left of l are < x *)
          r = N;                        (* All elements to the right of r are > x *)
          p = 1;                        (* Pivot *)

(* Main *)
{     
    print <<t, x>>;
        
    while ((l <= r) /\ (~ found)) {
        p := (l + r) \div 2;
          
        if (t[p] = x)
            found := TRUE
        else if (t[p] < x)
            l := p-1
        else (* t[p] > x *)
            r := p+1
    };
        
    assert( found <=> (\E j \in 1..N : t[j] = x) )      
}

}
*)

\* BEGIN TRANSLATION
\* END TRANSLATION
  1. What are the loop invariant and variant that must hold for the correctness and termination of the algorithm above?
  2. Create a new module, copy and paste the algorithm above, then create a new model and check the Termination property and the assertion for small instances of the model (e.g. N<-2 and MAXINT<-2). Analyze the error trace provided by the TLA toolbox. Is it a finite or an infinite path? Is it a counter-example to the termination of the algorithm or to the assertion? Explain why the error trace violates the invariant and/or the variant of loop and fix the algorithm.
  3. Check again the Termination property and the assertion. Analyse the new error trace: is it a finite path ot an infinite path? Is it a counter-example to the termination of the algorithm or to the assertion? Fix the algorithm and justify which part of the specification was violated.
  4. Check that the algorithm now terminates and that it is now correct.