Академический Документы
Профессиональный Документы
Культура Документы
Jayalal Sarma
August 29, 2018
Abstract
This document serves as a class notes for the first week of lectures of the course
CS2700 : Programming and Data Structures course. The document borrows heavily
from notes on program correctness written by James Aspnes. The notes are being
written for consumption of the students in class and it relates to many questions
that were asked in class. The public document will be put out separately at a later
point of time. Hence the students are also requested not to publicize the notes
beyond the students within the class.
Contents
1 Introduction 1
1 Introduction
Proving programs to be correct is an important aspect of being an engineer and a sci-
entist (from the name of our branch, we happen to be aspiring to be both !). In fact,
computer science and engineering has proliferated into day-to-day life so much that the
word ”important” is an understatement. Proving that the systems (could be hardware
or software) to be correct is critical since they are out there in every aspect of life now.
A technical example that we discussed (in class) is the intel FDIV bug1 . Long back,
in the year 1994, the processor manufacturers named Intel shipped out it brand new
1
Read the link from the course page for knowing more details than what we describe here.
1
processor - The Intel Pentium Processor. Very quickly they invaded markets and it
started proliferating into various systems that were built. However, a few months later,
an error was discovered by Thomas Nicely, a professor of mathematics, on 19 October
1994 and reported to Intel five days later. Then on 30 October 1994, he wrote a fateful
email to ”a number of individuals and organizations” that set the wheels in motion. The
processor floating-point divide problem was caused by a subtle but specific circuit-design
error; the flaw was easily corrected with changes to masks in the next regular production
revision of the chip, in 1994. However, intel had to withdraw the processors, fix them,
or replace them. This caused a huge financial burden on the company, which however, it
survived successfully.
The bottomline of the story is about the importance of correctness. Although the
above example is about hardware, you can imagine a similar scenario if it happens to the
JEE seat allocation software that gave you seats. It is important that we use provably
correct programs for doing the SEAT allocations.
As you might have seen in CS1100. There are different stages for developing the
program for solving a problem. The statement of the problem has two important parts.
Input Specification: How should we interpret the input give to us as. Note that
ultimately it is a string. For example, the input variable n will hold a natural
number. This has to be expressed mathematically. Example : A predicate as
P (n) : n = a where a ∈ N. An example input is often called input instance - a
terminology which we would widely use in this course.
x=x+1
2
tation also implements, our correctness arguments hold waters. In this notes, we assume
semantics of the statements like assignment, if-then-else, while and for loops etc.
1. P holds in the initial state because of the input specification. That is input speci-
fication interpreted as a predicate implies P .
3. If P holds when the algorithm terminates, then the output of the algorithm is correct
(that is, implies the output requirement which is a mathematical statement).
The statement P does not change from being true during the run of the algorithm.
Hence it is usually called an invariant of the algorithm. However, there is a serious
problem about how one handles conditional statements and loops etc. Moreover, this
might look a bit too strict than what we actually require. We do not require that the
predicate which implies the output requirement can start holding to be true after some
parts of the algorithm is already run. However, in the above we insist that it should true
through out the program.
3
Definition 3.1 (Hoare Triplets) We attach to each statement of a program a precon-
dition (something that we demand is true before the statement executes) and a postcondi-
tion (something that will then be true after the statement executes). Such a precondition,
statement, and postcondition - together form a Hoare triple. That is,
{P }S{Q}
As for notation - the preconditions and postconditions are typically written inside curly
braces to distinguish them from program code.
Ok, so we have defined a Hoare triplet. Now how do we use it? The first idea is to use
the semantics of program execution. That is, consider a program segment P that contains
two statements S1 sqequentially followed by S2 . If a precondition P is true before the
execution of a statement S1 and it results in a postcondition Q. And if a pre-condition Q
is true before the exection of a statement S2 then the post-condition R is true after the
execution of the statement. Combining this, we can conclude that if a pre-condition P is
true before the execution of a program segment which has S1 sequentially followed by S2 ,
then the post-condition R is true. The following axiom captures this intuitive thought.
Definition 3.2 (Composition Axiom)
So if we knew Hoare triplet we know how to work through sequencing them through
repeated applications of composition axiom. Let us try on an example. Consider the
sequence of statements :
S1 : x = 2 ∗ x
S2 : x = x + 1
Let us arbitrarily try pre-conditions and post-conditions for the two statements inde-
pendently. Here is a result.
{x is a multiple of 4} x = 2 ∗ x {x is a multiple of 8}
Both of them form Hoare triplets independently. However, we cannot use the composition
axiom because the post-condition of the first statement does not match with the pre-
condition of the second statement. This means that we should choose the individual
pre-conditions and post-conditions more carefully with a compatibility conditions.
Here is a more powerful idea to solve the problem. Observe that in the above example,
the postcondition of S1 in the triplet we considered was x is a multiple of 8, which actually
implies the pre-condition that we have for the statement S2 . This leads to the following
conclusion : suppose {P }S{Q} represents a Hoare triplet. Instead of Q, we just want
a post-condition which is weaker. That is, say Q → R. Hence, it is immediate that
{P }S{R} is a Hoare triplet as well. We capture this with the following two axioms.
4
Definition 3.3 (Pre-strengthening Axiom)
{Q}S{R} ∧ (P → Q)
{Q}S{R}
{P }S{Q} ∧ (Q → R)
{P }S{R}
is a Hoare triplet.
Notice that designing P and Q such that the three proofs can be done is a challege.
Just for clarity, can we take P and Q to be a trivial tautology (a statement which is
always true)? If we do, then the first and the third are trivial to prove. Howevet, note
that the challenge then will lie in proving the second statement - in fact it is not even
true if the output specification is non-trivial. Thus, P should be weak enough for input
specification to imply it, but it should be strong enough to imply Q after the execution
of the program. Similarly, Q should be strong enough to imply the output requirement
specification, but should be weak enough to be implied by P after the execution of the
program. These thoughts demonstrate the challeges in designing the P and Q for a
program.
5
(so that Q implies output requirement specification automatically). Now we will work
backwards statement by statement. Writing down the pre-conditions in such a way that
each statement along with the corresponding predicates written form a Hoare triplet.
Next step is to answer the question: suppose we have written the statement Q for a
program segment, and the last statement in the program was an assignment statement
x = t, how do we write a pre-condition such that it forms a Hoare triplet along with Q
as the post-condition for this assignment statement. If we want Q to be true after the
assignment statement, and Q has x in it, then we should replace the x with t and insist
that this new statement(call it Q0 ) is true before the execution of the statement x = t.
Then, Q will be true after the execution of the program. That is {Q0 } x = t {Q} forms
a Hoare triplet. Capturing this as an axiom:
Definition 4.1 (Assignment Axiom) If P is a predicate:
{P [x|t]} x = t {P }
always forms a Hoare triplet, where P [x|t] denotes the predicate obtained by replacing the
occurance of x in the predicate P by t.
Let us see a simple example :
Example: Let the assignment statement be x = x + 1 and suppose we want the post-
condition to say
{x is an even number}
Thus by the assignement axiom:
{(x + 1) is an even number} x = x + 1 {x is an even number}
forms a Hoare triplet, by replacing x on the RHS predicate by t (which in this case, is
x + 1). By using the equivalence x + 1 is an even number if and only if x is an odd
number, by pre-strenthening axiom, we conclude that:
{x is an odd number} x = x + 1 {x is an even number}
forms a Hoare triplet.
Now, let us try out a different post-condition that we aim for. Say we want
P = {x is a power of 2}
for the same statement S : x = x + 1. That is,
P [x|t] = {x+1 is a power of 2}
and hence,
{x = 2k − 1 for k ∈ N} x = x + 1 {x is a power of 2}
forms a Hoare triplet by assignment axiom.
This example demonstrates that it is possible to write multiple pre-condition and
post-condition tuples that forms Hoare triplet for a given statement (and more generally
for a given program segment).
Now we know how to show one triplet is a Hoare triplet. We can use the composition
axiom and the pre-strengthening, post-weakening etc to show Hoare triplets involving a
sequence of assignment statements. Let us do one such example:
6
Example : Swap Program Consider the following lines of pseud-code program seg-
ment which is supposedly swapping the values in the variablex x and y using a temporary
variable named temp.
temp = x
x = y
y = temp
Let us denote the program segment by S which consists of three assignment statements
S1 , S2 , and S3 . In the beginning of the program segment say x = a and y = b are true,
where a and b are integers (say). In the end of the program we expect x = b and y = a.
In other words, we expect to prove that:
is a Hoare triplet. To prove this, let us work backwards from the final post-condition
that we want - namely, {x = b ∧ y = a}. By applying assignment axiom, we have that:
is a Hoare triplet. Further, if we use the pre-condition of this triplet as the required
post-condition of the statement x = y, we get, again by assignment axiom that:
is a Hoare triplet. And if we use the pre-condition of this triplet as the required post-
condition of the statement temp = x, we get, again by assignment axiom that:
is a Hoare triplet.
Now, from facts (1), (2) and (3), by using composition axiom, we can infer that :
is a Hoare triplet and it proves that the program segment correctly computes the swap
function.
Exercise 4.1 Prove that the following program segment also achieves the swap function
when the x and y hold integer values.
x = x + y;
y = x - y;
x = x - y;
Write down the complete proof using Hoare logic. Check if you are using the fact that the
values are integers.
7
5 The Next Step : If-the-Else Construct
Suppose our program contains an if-then-else construct. The general syntax is : the
program segment S can be thought of as :
if (B)
then
S1
else
S2
endif
where B is a Boolean condition (which can be thought of as a predicate having the program
variables and it returns True or False for a given substitution) and S1 and S2 are program
segments.
To show correctness, we go by the semantics of the if-then-else construct. To show
{P }S{Q} is a Hoare triplet, we need to establish that if the condition {P } is true before
the execution of the program segment S, then after the execution of the program segment
predicate Q will be true. S. Now the execution of the program depends on B. There
are two possibilities, either (P ∧ B) is true or (P ∧ ¬B) is true. In the former case, the
program segment would have executed S1 and hence to prove that {P }S{Q} is a Hoare
triplet, in this case, it suffices to prove that {P ∧ B}S{Q} is a Hoare triplet. In the latter
case, the program segment would have executed S2 and hence it suffices to prove that
{P ∧ ¬B}S2{Q} is a Hoare triplet.
Summing up, to establish that {P }S{Q} is a Hoare triplet, it is sufficient to prove
that {P ∧ B} S1 {Q} and {P ∧ ¬B} S2 {Q} Hoare triplets. Writing this idea formally
as an axiom:
Example: We take the following example where the program is supposed to be com-
puting the absolute value of a given integer x.
8
• {(x = a) ∧ (x > 0)} x = −x{x = |a|} forms a Hoare triplet. To prove this, by
assignment axiom we know that.
is a Hoare triplet (To apply the axiom, recall the notation, consider t as −x, and
P in the assignment axiom is {x = |a|}. And, P [x|t] will be {−x = |a|}.)
Now note that ((x = a) ∧ (x > 0)) → (−x = |a|) by the definition of absolute value.
Hence, by pre-strengthening axiom:
is Hoare triplet.
• {(x = a) ∧ (x > 0)} x = x {x = |a|}. Since the pre-condition implies the post-
condition, and the statement does not change the variable, it forms a Hoare triplet.
Hence by if-then-else axiom,{Pa } S {Qa } forms a Hoare triplet. Hence by univer-
sal generalization, ∀a ∈ Z, {Pa } S {Qa }, concluding that the program segment indeed
computes the absolute value if a is an integer.
while (B)
S1
endwhile
Now this requires fresh thinking. Suppose we would like to establish that if a pre-
condition P holds before the start of the loop, then a post-condition Q holds after the
exit from the loop. A natural thought is to insist that the predicate Q holds true through
out the run of the loop. However, this is too strong and counter intuitive. We might
have designed the loop such that at the time when the loop exists the predicate Q is true.
Hence the idea to insist that Q holds in every iteration of the loop is too strong to be
even useful.
Here is a solution to the puzzle. The idea is to identify a predicate R such that:
• R is true in the beginning of the first iteration of the loop. (We could show this by
simply proving that P → R).
{R ∧ B} S1 {R}
is a Hoare triplet.
9
• When the loop exits, Q must be true. We could establish this by proving mathe-
matically that (R ∧ ¬B) → Q.
Together, the above establishes that {P } S {Q} is a Hoare triplet. Note that the design
of the predicate R is the challenge in the above idea. R should be weak enough that it is
implied by P , and it should be strong enough to imply (along with ¬B) the truth of Q.
Since the predicate R remains true during the execution of the loop, we call it the loop
invariant for this loop.
Example: We consider the following example - which initializes all the elements of an
array A to 0. Assume that the array has n elements and are indexed from 0 to n − 1.
i=n;
while (i <> 0)
do
i = i-1;
A[i] = 0;
endwhile
Q : {∀i : 0 ≤ i ≤ n − 1, A[i] = 0}
The challenge, as described above is to find R for which we can prove the above three
properties. The design of R comes from the idea with which the program segment was
written (Yes, the onus of proving a program to be correct is with the designer !). The task
of correctness proof is indistinguishable from the program design stage itself. However,
we are only formalizing the idea that was generated at the time of the design.
In the above setting, the idea that is used is the following. At each iteration of the loop
we ensure the increasingly longer suffix part of the array are intialized to 0. Formalizing
this thought, here is a loop invariant.
P → R. Note that there is an assignment statement i = n before the while loop. Notice
that the following is a Hoare triplet by assignment axiom:
{∀j such that n ≤ j ≤ n−1, A[j] = 0} i = n {∀j such that i ≤ j ≤ n−1, A[j] = 0}
But the LHS of the triplet is a tautology, which is implied by P . The RHS is R.
{R ∧ B}S1{R} is a Hoare triplet: To prove this, note that there are two statements in
the body of the loop. We want R to be true in the end. By assignment axiom,
10
is a Hoare triplet, where R0 is obtained by replacing occurrence of A[i] in R with 0.
To clarify this, let us write R differently.
Thus,
R0 : ∀j such that i + 1 ≤ j ≤ n − 1, A[j] = 0
Now applying assignment axiom for the statement i = 0, we have that,
{R00 } i = i − 1 {R0 }
is also a Hoare triplet, where R00 is the predicated R0 with variable i replaced by
i − 1. That is,
which is same as R.
Thus, {R ∧ B} S {R} is a Hoare triplet by using composition axiom to the two
Hoare triplets
{R00 } i = i − 1 {R0 }
and
{R0 } A[i] = 0 {R}
Thus we have completed the three ingredients of proving that the loop is correct.
11