SE 504
Development of a program to compute Fibonacci numbers

The Fibonacci function is often defined as follows:

f.0 = 0     f.1 = 1
f.k = f.(k-2) + f.(k-1)   (k>1)

Viewing this function as defining the sequence

<f.0, f.1, f.2, f.3, ... >

gives rise to the so-called Fibonacci sequence

<0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... >

in which, beginning at the third element, each element is the sum of the preceding two.

Let's develop a program that, given as input a positive integer N, calculates f.N. Here is a specification:


  |[ con N : int;  { N > 0 }
     var m : int;

     compute_f.N

     { Q: m = f.N }
  ]|

It seems likely that we will need to use repetition to solve this problem. Applying the replace a constant by a variable heuristic, we can rewrite the postcondition Q as the slightly stronger Q':

Q' : m = f.n   &   n = N

Applying the remove a conjunct heuristic, we take the first conjunct of Q' as the loop invariant and the second as the negation of its guard. We obtain the following refinement of the specification:


  |[ con N : int;  { N > 0 }
     var m : int;
     var n : int;

     n, m := ?, ?;
     { P : m = f.n }
     do n != N  --->  n, m := ?, ?;
     od
     { Q' : m = f.n   &   n = N }
     { Q : m = f.N }
  ]|

First we consider how to initialize n and m so as to "truthify" the proposed loop invariant. That is, we wish to find expressions to replace the question marks so that the Hoare Triple

{ true } n,m := ?,? {P}
holds. (The post-condition of initialization is the loop invariant; the pre-condition of this program is true. Technically, the condition N > 0 is a pre-condition, but because it involves only constants we view it as a "global invariant".) The obvious possibilities for initialization are n, m := 0, 0 (reflecting the fact that f.0 = 0) and n, m := 1,1 (reflecting the fact that f.1 = 1). We choose the latter.

Either way, this suggests that n should be increased within the body of the loop so as to eventually reach value N. The simplest way to do that is by incrementing n, so that's what we propose. These refinements lead to the program

  |[ con N : int;  { N > 0 }
     var m : int;
     var n : int;

     n, m := 1, 1;
     { P : m = f.n }
     do n != N  --->  n, m := n+1, ?;
     od
     { Q' : m = f.n   &   n = N }
     { Q : m = f.N }
  ]|

It remains to determine how to update m inside the body of the loop. We attempt to calculate the appropriate expression E by proving item (ii) among the five proof obligations for a loop:

(ii) {P & B} n, m := n+1, E {P}

Assume P and B.

     wp.(n,m := n+1,E).P

  =    < wp assignment law >

     P(n,m := n+1,E)

  =    < defn of P; textual sub. >

     E = f.(n+1)
At this point, our only manipulative opportunity arises from the third equation in the definition of f. However, for that equation to apply requires that n+1 > 1, i.e., n > 0. As we initialized n to 1 and thereafter all changes to n are due to incrementing it, it is clear that n > 0 will be true. Hence, we incorporate this condition into the loop invariant so that we can use it here as an assumption. Starting the proof over again with the stronger version of P (namely, P = P0 & P1, where P0 : m = f.n and P1 : n > 0), we get

(ii) {P & B} n, m := n+1, E {P}

Assume P (i.e., m = f.n and n > 0) and B.

     wp.(n,m := n+1,E).P

  =    < wp assignment law >

     P(n,m := n+1,E)

  =    < defn of P; textual sub. >

     E = f.(n+1)  &  n+1 > 0

  =    < assumption n > 0 implies 2nd conjunct; (Gries 3.39) >

     E = f.(n+1)

  =    < assumption n > 0 implies n+1 > 1,
         justifying use of 3rd equation in defn of f >

     E = f.(n-1) + f.n

  =    < assumption m = f.n >

     E = f.(n-1) + m
At this point, we observe that it would be nice to have a program variable, say r, guaranteed to have value f.(n-1) at this point in execution, because then we could finish the derivation of E:
  =    < assumption r = f.(n-1) >

     E = r + m
As we are free to introduce such a variable, we do so! That is, we strengthen the loop invariant by adding P2 : r = f.(n-1) as a new conjunct. Of course, doing this brings with it the responsibility to introduce code to initialize r so as to truthify P2 and to update r during each loop iteration so as to preserve the truth of P2. The refined program is as follows:

  |[ con N : int;  { N > 0 }
     var m, r : int;
     var n : int;

     n, m, r := 1, 1, ?;
     { P : P0 & P1 & P2, where
       P0 : m = f.n, P1 : n > 0, and P2 : r = f.(n-1) }
     do n != N  --->  n, m, r := n+1, r+m, ?;
     od
     { Q' : m = f.n   &   n = N }
     { Q : m = f.N }
  ]|
Let's calculate the proper initialization to r, by deriving an expression F satisfying {true} n,m,r := 1,1,F {P}.

By the Hoare Triple Assignment Law (and (Gries 3.73)), this is equivalent to [P(n,m,r := 1,1,F)].

     P(n,m,r := 1,1,F)

  =    < defn of P; text. sub. >

     1 = f.1  &  1 > 0  &  F = f.(1-1)

  =    < defn of f; arithmetic, (Gries 3.39) >

     F = f.0

  =    < defn of f >

     F = 0 
Now to determine how to modify r during each iteration. As our previous work has demonstrated that execution of the loop body preserves the truth of P0 and P1, we use as a postcondition only P2.
     {P & B} n,m,r := n+1, r+m, G {P2}

Assume P and B.

     wp.(n,m,r := n+1,r+m,G).P2

  =    < defn of P2; text. sub >

     G = f.(n+1-1)

  =    < arithmetic >

     G = f.n

  =    < assumption m = f.n >

     G = m
We obtain the following program:

  |[ con N : int;  { N > 0 }
     var m, r : int;
     var n : int;

     n, m, r := 1, 1, 0;
     { P : P0 & P1 & P2, where
       P0 : m = f.n, P1 : n > 0, and P2 : r = f.(n-1) }
     do n != N  --->  n, m, r := n+1, r+m, m;
     od
     { Q' : m = f.n   &   n = N }
     { Q : m = f.N }
  ]|
All that remains is to provide a bound function. Here, the obvious choice is t : N - n. Technically, in order to prove item (iv), we need to include the condition n <= N within the loop invariant. Clearly, this condition is truthified by initializing n to 1 (given that N > 0 is a global invariant) and preserved by incrementing n when it is known that n < N, as is guaranteed by P & B.