home page - news - syllabus - schedule - assignments - tutorials - tests - java - references - Mike Jacobson |
Proving the Correctness of a Simple Program |
Recall that the lecture notes on proving correctness of algorithms included the exercise of proving the correctness of the following algorithm.
Precondition: | n is a positive integer |
Postcondition: | n is unchanged and sum is the sum of the first n positive integers. |
Algorithm: |
i := 1 sum := 1 while (i < n) do i := i+1 sum := sum + i end while |
The process that one can follow to develop this proof, and the assertions that should be used as intermediate assertions (including loop invariants), and the loop variant for the loop in this algorithm, are described below.
Getting Started
Notice, first, that there is only one rule that can be applied — this is a sequence of two (or more) statements
S1; S2
You can actually apply this rule in numerous ways, because the program is a sequence of (at least) three statements. For example, one might choose to consider S1 to be just the first statement, or you (taking the other extreme) one might choose S1 to include everything but the last statement.
We will try to use the organization of the program itself to make a decision about this: Notice that it includes a pair of statements that initialize variables used later on, followed by a loop that does most of the work. We will consider S1 to be the “initialization” stage (again, consisting of the first two statements), so that S2 is everything else (that is, the loop).
Applying the Rule
At this point, along with our program, we have the given precondition
p: n is a positive integer
and the given postcondition, which is essentially as follows.
q: n is unchanged, and sum is the sum of the first n positive integers
In order to proceed we must discover an “intermediate assertion,” r, and then establish each of the following:
Now, if you are dealing with somebody else’s program (and you don’t know why it works) and the program is poorly documented (as is the case — deliberately — for this example) then it will not necessarily be clear what this intermediate assertion is! Of course, it is really not very reasonable to expect you (some unfortunate person who has been given the algorithm) to be able to “prove it to be correct” anyway!
This is all supposed to be applied in one of the following two other situations:
You already know why the algorithms works — and you are using the tools we are discussing to write a proof down (in a somewhat standard, and somewhat formal way). In this case, you should know what “r” is.
You are trying to discover the reasons why the algorithm works, but the algorithm you have been given is properly documented (or a proof of its correctness has also been supplied in some other form). In this case the assertion”r” is written down somewhere (it should be part of the inline documentation) — unless your program is only one or two lines long, and it is obvious what the assertion should be.
Now, as mentioned above, this example program is not sufficiently documented! A few of the intermediate assertions needed to establish correctness should be included as documentation here, and are not. Subsequent examples, presented after the first exercise (or two) about correctness, will generally be better documented than this algorithm is.
On the other hand, this particular assertion would probably not be included in the better documented version of the algorithm anyway, because somebody who is a bit more familiar with all this (one hopes, like most students in this course after another week or two of study and practice) will be able to figure out what the needed assertion is, on her or his own!
The assertion that we will need to use, to continue, is as follows.
r: n is a positive integer (whose value has not been changed), i is equal to 1, and sum is also equal to 1 (so that sum is equal to the sum of the first i positive integers).
This is more “verbose” than the assertions we will generally use — one probably wouldn’t include the parenthetical remark at the end, in practice.
Establishing Partial Correctness of the First Subprogram
At this point, we have the same precondition as before,
p: n is a positive integer
The postcondition that we should use, when considering the first condition, is the assertion that we identified as our “intermediate assertion” during the previous step:
r: n is a positive integer (whose value has not been changed), i is equal to 1, and sum is also equal to 1 (so that sum is equal to the sum of the first i positive integers).
Once again, our program is a sequence of two programs, so we can consider it as
S1, 1; S1, 2
In this case there is no choice about what to call S1, 1, because our whole (sub)program only includes two statements — S1 1 is the first of these statements, and S1, 2 is the second.
Establishing the partial correctness of this program is simple enough for it to be left as an exercise.
Establishing Partial Correctness of the Second Subprogram
At this point we should using the “intermediate assertion” from the first step as our precondition:
r: n is a positive integer (whose value has not been changed), i is equal to 1, and sum is also equal to 1 (so that sum is equal to the sum of the first i positive integers).
Since this subprogram is the last part of the entire program, we must use the postcondition that we were given as the postcondition for this subprogram as well:
q: n is unchanged, and sum is the sum of the first n positive integers
This time our program has a different structure — it is a loop with the form
while (c) do S2 end while
where c is the test “i < n” and S2 is a sequence of two statements.
As the lecture notes indicate we must continue by finding a “loop invariant” — a statement that holds the beginning of every execution of the loop body.
This is an appropriate place to repeat something that has already stated above:
This program is not sufficiently documented.
This is deliberate — it has been done in hopes that it help students to see why documentation is important — but the following should also be pointed out:
Students will be required to document their code on assignments!
In particular: Loop invariants (and loop variants) will be required as in-line documentation for all loops included in code written for the first assignment!
This standard might be relaxed — somewhat — for later assignments. However, all assertions that are not likely to be obvious to others, that are needed to prove correctness, should be included in documentation later on, as well. This will frequently include loop invariants and loop variants.
For this example, the following can be used.
Loop Invariant:
- n is a positive integer (whose value has not been changed by this algorithm)
- i is a positive integer such that 1 ≤ i < n
- sum is the sum of the first i positive integers
We will call this loop invariant “sbefore” in the rest of this discussion.
We will also need to identify a condition that is satisfied at the end of each execution of the loop body. (This does not necessarily need to be included in documentation, but you might choose to do so if you think that it woud be helpful.) In this case, the following should be used.
Satisfied at the End of Each Execution of the Loop Body:
- n is a positive integer (whose value has not been changed by this algorithm)
- i is a positive integer such that 1 ≤ i ≤ n
- sum is the sum of the first i positive integers
Please read this carefully and note that it is not the same as the loop invariant! The relationship between i and n, given in the second condition, has changed slightly.
We will call this condition “safter” in the rest of this example.
Consulting the lecture notes, we see that it is now necessary to do each of the following things in order to prove that sbefore really is a loop invariant:
Prove that
(r ∧ (c)) ⇒ sbefore
— which establishes that the loop invariant holds the first time the loop body is executed, if it is ever executed at all
Prove that
{ sbefore } S2 { safter }
— that is, establish the partial correctness of the loop body for the assertions that have now been identified
Prove that
(safter ∧ (c)) ⇒ sbefore
— which establishes that if the expected conditions holds at the end of the kth execution of the loop body, for a positive integer k, and the loop test is satisfied, then the precondition holds once again just before the loop is executed for the k+1st time
Now, since c is the test “i < n, the statements that one needs to prove in steps (i) and (iii) above are (or, at least, should be) obvious. The second is another “partial correctness” problem which will be considered shortly.
Consulting our notes once again, we see that we need to do three things to establish partial correctness of this loop:
Prove that
(r ∧ ¬(c)) ⇒ q
— which establishes that the postcondition is satisfied at the end, if the loop body never gets executed at all
Prove that sbefore is a loop invariant for this loop — which we’ve just finished doing, above
Prove that
(safter ∧ ¬(c)) ⇒ q
— which establishes that if the loop is executed for a kth time, for a positive number k, and the loop test is not satisfied (so that the loop halts after that), then the postcondition is satisfied at this point, as well.
Notice that since n is a positive integer, n ≥ 1. Since c is the test “i < n,” it should be clear that n is equal to one if the loop body never gets executed at all and, more in all other cases, that i = n immediately after the final execution of the loop body. A comparison of spost to q should be sufficient for you to see that the statements one needs to verify in steps 1 and 3, above, are true (and fairly obvious).
Establishing Partial Correctness of the Loop Body
Once again, the loop body is a sequence of two statements.
The following “intermediate assertion” might not be obvious to students who are seeing all this material for the first time — but it is a good exercise (to make sure that you understand the proof rule for partial correctness of an assignment statement) to confirm that it can be used.
We'll refer to this assesrtion as Smid below.Satisfied after Execution of the First Statement in the Loop Body:
- n is a positive integer (whose value has not been changed by this algorithm)
- i is a positive integer such that 1 ≤ i ≤ n
- sum is the sum of the first i−1 positive integers
Exercise: Complete the proof of partial correctness of the loop body using the information provided above.
Getting Started
To establish termination one begins in much the same way as we did when considering partial correctness: Break the program down into two pieces.
Consulting the lecture notes we see that we need to prove each of the following, where p and r are the assertions that were considered during partial correctness.
Each of these are considered, below.
Proving Termination of the First Subprogram
Notice that this is a sequence of programs, once again, so that the same proof rule must be applied. After that, notice that there is virtually nothing more that one must do — except to recall (and mention) that assignment statements (that do not call methods that haven’t been verified themselves, yet) always terminate.
Proving Termination of the Second Subprogram
Consulting the lecture notes, one sees that it is necessary to establish a loop variant for this loop. We mentioned and discarded a few examples before considerig the function
fL(n, i) = n − i
Three properties were to be established. These are mentioned, and verified, below.
This is an integer-valued function of the program’s inputs and other variables. This should be obvious.
Each execution of the loop body decreases the value of this function by at least one. This is easy to show: Notice that the value of the variable n is not modified, while i is incremented — so that, in fact, the value of this function is decreased by exactly one every time the loop body is executed.
If the value of the function is zero or negative then the loop test is not satisfied. This is pretty clear, too, since the loop test is “i < n”
As the notes state, we may now conclude that the loop halts — and, furthermore that the initial value of the loop variant, n−1, is an upper bound on the number of times that the loop body is executed when the loop is.
Partial Correctness: note throughout that the variable n is never modified in the code, so the clause about n not being modified can be assumed in all assertions.
Next, we prove that {Smid} sum = sum +i {Safter}. Again, by the substitution rule we can prove Smid &rArr {sum+i is the sum of the first i positive integers} (note that we exclude the other two clauses of Smid and Safter because the are the same in both assertions). Smid states that sum is the sum of the first i-1 positive integers; thus sum+i is the sum of the first i positive integers as required.
Termination: We need to prove that fL(n,i) = n-i is a loop variant for the while loop, assuming that the preconditions {r} of the loop hold. We argue as follows:
Last updated:
http://www.cpsc.ucalgary.ca/~jacobs/cpsc331/F10/handouts/lecture03-example.html |