SE 504 Formal Methods and Models
Spring 2022
HW #6: Strengthening the Invariant, Assignment to Arrays
Due: 4:00pm, Monday, April 11

1. Calculate (and simplify as much as possible).
(a) wp.(b.j := k).(b.j ≠ b.k)
(b) wp.(b.(b.j) := b.k).(b.j = b.k)


2. Complete the development of the program below and show a proof of (ii) from the loop checklist. (The four other items on the list are clearly true.) Of course, E refers to an unknown expression. You will need to augment the loop invariant with a third conjunct in a way similar to what was done in solving the Prefix Sums problem. Don't forget that B is a rigid variable and therefore cannot be mentioned in any executable command.

|[ con N : int;  {N≥0}
   var b : array[0..N] of int; {b = B}
   var k : int;
   k := 0;
   {loop invariant I: (∀i | 0≤i<k : b.i = B.i * B.(i+1)) ∧ 0≤k≤N ∧ b.n = B.n}
   {bound function t: N - k}
   do k ≠ N ⟶ k,b := k+1,b(k:E)
   od
   {Q': (∀i | 0≤i<k : b.i = B.i * B.(i+1)) ∧ k=N ∧ b.N = B.N}
   {Q: (∀i | 0≤i<N : b.i = B.i * B.(i+1)) ∧ b.N = B.N }
]| 


3. Complete the development of the program below and show a proof of (ii) from the loop checklist. (The four other items on the list are clearly true, with the exception of item (i) with respect to any fresh variables that you might introduce.) Of course, E refers to an unknown expression. You will need not only to augment the loop invariant as in the previous problem but also to strengthen it by introducing a fresh variable. You should find that using a multiple assignment command is very convenient. Don't forget that B is a rigid variable and therefore cannot be mentioned in any executable command.

|[ con N : int;  {N>0}
   var b : array[0..N) of int; {b = B}
   var k : int;
   k := 1;
   {loop invariant I: (∀i | 1≤i<k : b.i = B.i max B.(i-1)) ∧ 1≤k≤N ∧ b.0 = B.0}
   {bound function t: N - k}
   do k ≠ N ⟶ k,b := k+1,b(k:E)
   od
   {Q': (∀i | 1≤i<k : b.i = B.i max B.(i-1)) ∧ k=N ∧ b.0 = B.0 }
   {Q: (∀i | 1≤i<N : b.i = B.i max B.(i-1)) ∧ b.0 = B.0 }
]| 


4. Prove the correctness of the following program, which sorts an array. (Do you recognize which sorting algorithm it is based upon?) Because it is obvious that items (iii), (iv), and (v) on the loop checklist are satisfied, all you need to show are items (i) and (ii). (Regarding (ii), all you need to consider is the line of code that swaps two array elements, as is explained below.)

|[ con N : int  { N≥0 }
   ;var b : array[0..N) of T  { b=B }
   ;var k, m : int

   ;k := 0
   {I: inOrder(b,0,k) ∧ boundary(b,k) ∧ 0≤k≤N ∧ perm(b,B)}
   {t: N-k}
   do k ≠ N ⟶ 
      {I ∧ k≠N}
      m := locOfMin(b,k,N);
      {I ∧ k≠N ∧ k≤m<N ∧ (∀j | k≤j<N : b.m ≤ b.j) }
      ;swap(b,k,m);
      {I(k:=k+1)}
      ;k := k+1
      {I}
   od
   {Q: inOrder(b,0,N) ∧ perm(b,B)}
]|
where
inOrder(f,p,q) : (∀i,j | p≤i≤j<q : f.i ≤ f.j)
boundary(f,p) : (∀i,j | 0≤i<p ∧ p≤j<#f : f.i ≤ f.j)
perm(f,g) : arrays f and g are permutations of one another

Notes/Hints:

  1. The more traditional notation for function application is being used here (e.g., inOrder(f,p,q)) so that there is no chance that you will confuse inOrder.f.p.q (which says to apply inOrder to the three arguments f, p, and q) with inOrder.(f.p).q (which says to apply inOrder to the two arguments f.p and q.
  2. The loop body is annotated with "intermediate assertions"; all that you must show in order to prove (ii) on the loop checklist is the Hoare Triple involving the command that calls the swap() subprogram, the precondition of which reflects the fact that the value returned by the call locOfMin(b,k,N) is a location containing the minimum value within b[k..N).

    The command swap(a,i,j) is, in effect, the assignment a := a(i,j : a.j,a.i) and therefore (in accord with the wp assignment law) has the following semantics:

    wp.(swap(a,i,j).Q = Q(a := a(i,j : a.j,a.i))

    where (provided that i=j implies E=F)

    a(i,j : E,F).r  = { E if i=r
    F if j=r
    a.r otherwise

    Using the if function, this can be written as follows:

    a(i,j : E,F).r  =  if(i=r, E, if(j=r, F, a.r))
  3. In carrying out the proof, you will need to refer to the array b(k,m : b.m,b.k) several times. Rather than writing out that entire expression each time, give it a shorter name, such as b', state its relevant properties, and then use those properties when appropriate. Which properties? Well, b'.k = b.m, b'.m = b.k, and, for all j equal to neither k nor m, b'.j = b.j.

    Thus, if b'.i occurs within a quantification in which i is a dummy that varies over a range that includes neither k nor m, you can cite the Irrelevant Array Element Axiom to justify replacing that occurrence of b'.i by b.i.

  4. Your proof should begin (after having indicated the assumption) with the expression
    wp.swap(b,k,m).(I(k:=k+1))
    which, making use of the definition of I, the semantics of the swap() method, the definition of b', and textual substitution, translates into
    inOrder(b',k+1) ∧ boundary(b',k+1) ∧ 0≤k+1≤N ∧ perm(b',B)
    As pointed out below, you can ignore the conjunct involving perm(). Rather than deal with all three remaining conjuncts simultaneously, it is recommended that you deal with them separately, which is justified by the PostCondition Conjunctivity theorem, which says that {P} S {Q1 ∧ Q2}  ≡  {P} S {Q1} ∧ {P} S {Q2}
  5. A useful theorem is
    boundary(b,k) ∧ k≤r<#b  ⟹  (∀i | 0≤i<k : b.i ≤ b.r)
  6. By virtue of the fact that every change made to array b[] is to swap two of its elements, the condition perm(b,B) is clearly an invariant of the program. Hence, do not bother to prove it. Nor will assuming it aid in proving any other conjunct of the loop invariant. Hence, you can just ignore it altogether.