\documentclass[11pt]{article}
\usepackage{palatino}
\usepackage{latexsym}
\usepackage{verbatim}
\usepackage{alltt}
\usepackage{amsmath,proof,amsthm,amssymb,enumerate}
\usepackage{math-cmds}
% FIXME be more precise about parens in question 2
\newcommand{\question}[2]
{\bigskip \noindent
{\bf Question #1} (#2 points).}
\newcommand{\extracreditquestion}[1]
{\bigskip \noindent
{\bf Question #1} (EXTRA CREDIT).}
\newcommand{\definition}[2]
{\bigskip
\begin{tabular}{p{1.5in}p{4.0in}}
\textbf{#1} & #2 \\
\end{tabular}
}
\def\prop{\textsf{\,\,prop}}
\def\thm{\textsf{\,\,thm}}
\def\implies{\Rightarrow}
\title{Lecture Notes:\\
Hoare Logic}
\author{17-654/17-754: Analysis of Software Artifacts \\
Jonathan Aldrich ({\tt jonathan.aldrich@cs.cmu.edu})}
\date{Lecture 3}
\begin{document}
\newtheorem{theorem}{Theorem}
\newtheorem{lemma}[theorem]{Lemma}
\maketitle
\newcommand{\pfalse}{\mbox{false}}
\newcommand{\ptrue}{\mbox{true}}
\newcommand{\pand}{\mbox{and}}
\newcommand{\por}{\mbox{or}}
\newcommand{\pskip}{\mbox{skip}}
\newcommand{\pif}{\mbox{if}}
\newcommand{\pthen}{\mbox{then}}
\newcommand{\pelse}{\mbox{else}}
\newcommand{\pdo}{\mbox{do}}
\newcommand{\pwhile}{\mbox{while}}
%\newcommand{\p}{\mbox{}}
\newcommand{\mtrue}{\mathbf{true}}
\newcommand{\mfalse}{\mathbf{false}}
\section{Hoare Logic}
The goal of Hoare logic is to provide a formal system for reasoning
about program correctness. Hoare logic is based on the idea of a
specification as a contract between the implementation of a function
and its clients. The specification is made up of a precondition and a
postcondition. The precondition is a predicate describing the
condition the function relies on for correct operation; the client
must fulfill this condition. The postcondition is a predicate
describing the condition the function establishes after correctly
running; the client can rely on this condition being true after
the call to the function.
The implementation of a function is \textit{partially correct} with
respect to its specification if, assuming the precondition is true
just before the function executes, then if the function terminates,
the postcondition is true. The implementation is \textit{totally
correct} if, again assuming the precondition is true before function
executes, the function is guaranteed to terminate and when it does,
the postcondition is true. Thus total correctness is partial correctness
plus termination.
Note that if a client calls a function without fulfilling its
precondition, the function can behave in any way at all and still be
correct. Therefore, if it is intended that a function be robust to
errors, the precondition should include the possibility of erroneous
input and the postcondition should describe what should happen in case
of that input (e.g. a specific exception being thrown).
Hoare logic uses Hoare Triples to reason about program correctness. A
Hoare Triple is of the form $\{P\} ~S~ \{Q\}$, where $P$ is the
precondition, $Q$ is the postcondition, and $S$ is the statement(s)
that implement the function. The (total correctness) meaning of a
triple $\{P\} ~S~ \{Q\}$ is that if we start in a state where $P$ is true
and execute $S$, then $S$ will terminate in a state where $Q$ is true.
Consider the Hoare triple $\{x = 5\} ~x := x * 2~ \{ x > 0 \}$. This triple
is clearly correct, because if $x=5$ and we multiply $x$ by $2$, we
get $x=10$ which clearly implies that $x>0$. However, although
correct, this Hoare triple is not a precise as we might like.
Specifically, we could write a stronger postcondition, i.e. one that
implies $x>0$. For example, $x>5 ~\&\&~ x < 20$ is stronger because it is
more informative; it pins down the value of $x$ more precisely than
$x>0$. The strongest postcondition possible is $x=10$; this is the
most useful postcondition. Formally, if $\{P\} ~S~ \{Q\}$ and for all $Q’$
such that $\{P\} ~S~ \{Q’\}$, $Q \Rightarrow Q’$, then $Q$ is the strongest
postcondition of $S$ with respect to $P$.
We can reason in the opposite direction as well. If $\{P\} ~S~ \{Q\}$ and
for all $P’$ such that $\{P’\} ~S~ \{Q\}$, $P’ \Rightarrow P$, then $P$ is
the weakest precondition $wp(S,Q)$ of $S$ with respect to $Q$.
We can define a function yielding the weakest precondition with respect to
some postcondition for assignments, statement
sequences, and if statements, as follows:
\[
\begin{array}{ll}
wp(x := E, P) & = [E/x] P
\\[1ex]
wp(S;~ T, Q) & = wp(S, ~wp(T, Q))
\\[1ex]
wp(\mbox{if}~ B ~\mbox{then}~ S ~\mbox{else}~ T, Q) & = B \Rightarrow wp(S,Q) ~\&\&~ \lnot B \Rightarrow wp(T,Q)
\end{array}
\]
A function can also be defined for the strongest postcondition with respect
to some precondition, but this requires existential variables (to represent
the old value of the variable being assigned) and is beyond the scope of
these notes.
In order to verify the partial correctness of loops of the form
$\mbox{while}~ b ~\mbox{do}~ S$, we come up with an invariant $I$ such
that the following conditions hold:
\begin{itemize}
\item $P \Rightarrow I$ : The invariant is initially true.
\item $\{ Inv ~\&\&~ B \} ~S~ \{Inv\}$ : Each execution of the loop preserves the invariant.
\item $(Inv ~\&\&~ \lnot B) \Rightarrow Q$ : The invariant and the loop exit condition imply the postcondition.
\end{itemize}
We can verify full correctness by coming up with an integer-valued
variant function $v$ that fulfils the following conditions:
\begin{itemize}
\item $Inv ~\&\&~ B \Rightarrow v > 0$ : If we are entering the loop
body (i.e. the loop condition $B$ evaluates to true) and the invariant
holds, then v must be strictly positive.
\item $\{ Inv ~\&\&~ B ~\&\&~ v = V\} ~S~ \{v < V\}$ : The value of the variant function decreases each time the loop body executes (here V is a constant).
\end{itemize}
\section{Proofs with Hoare Logic}
Consider the \textsc{While} program used in the previous lecture notes:
\[
\begin{array}{l}
r := 1;\\
i := 0;\\
\pwhile~ i0$.
Thus our precondition will be $m \ge 0 ~\&~\& n > 0$.
We need to choose a loop invariant. A good hueristic for choosing a
loop invariant is often to modify the postcondition of the loop to
make it depend on the loop index instead of some other variable.
Since the loop index runs from $i$ to $m$, we can guess that we should
replace $m$ with $i$ in the postcondition $r=n^m$. This gives us a
first guess that the loop invariant should include $r=n^i$.
This loop invariant is not strong enough (doesn't have enough
information), however, because the loop invariant conjoined with the
loop exit condition should imply the postcondition. The loop exit
condition is $i \ge m$, but we need to know that $i = m$. We can get
this if we add $i \le m$ to the loop invariant. In addition, for
proving the loop body correct, it is convenient to add $0 \le i$ and
$n > 0$ to the loop invariant as well. Thus our full loop invariant
will be $r=n^i ~\&\&~ 0 \le i \le m ~\&\&~ n > 0$.
In order to prove full correctness, we need to state a variant function
for the loop that can be used to show that the loop will terminate.
In this case $m-i$ is a natural choice, because it is positive at
each entry to the loop and decreases with each loop iteration.
Our next task is to use weakest preconditions to generate proof
obligations that will verify the correctness of the specification.
We will first ensure that the invariant is initially true when
the loop is reached, by propagating that invariant past the first
two statements in the program:
\[
\begin{array}{l}
\{m \ge 0 ~\&~\&~ n > 0\}\\
r := 1;\\
i := 0;\\
\{ r=n^i ~\&\&~ 0 \le i \le m ~\&\&~ n > 0 \}
\end{array}
\]
We propagate the loop invariant past $i :=0$ to get
$r=n^0 ~\&\&~ 0 \le 0 \le m ~\&\&~ n > 0$. We propagate this
past $r :=1$ to get $1=n^0 ~\&\&~ 0 \le 0 \le m ~\&\&~ n > 0$.
Thus our proof obligation is to show that:
\[
\begin{array}{l}
m \ge 0 ~\&~\&~ n > 0\\
\Rightarrow 1=n^0 ~\&\&~ 0 \le 0 \le m ~\&\&~ n > 0
\end{array}
\]
We prove this with the following logic:
\[
\begin{array}{ll}
m \ge 0 ~\&~\&~ n > 0 & \mbox{by assumption}\\
1=n^0 & \mbox{because $n^0=1$ for all $n>0$ and we know $n>0$}\\
0 \le 0 & \mbox{by definition of $\le$}\\
0 \le m & \mbox{because $m \ge 0$ by assumption}\\
n > 0 & \mbox{by the assumption above}\\
1=n^0 ~\&\&~ 0 \le 0 \le m ~\&\&~ n > 0 & \mbox{by conjunction of the above}\\
\end{array}
\]
We now apply weakest preconditions to the body of the loop. We will
first prove the invariant is maintained, then prove the variant function
decreases. To show the invariant is preserved, we have:
\[
\begin{array}{l}
\{ r=n^i ~\&\&~ 0 \le i \le m ~\&\&~ n > 0 ~\&\&~ i < m\}\\
r := r*n;\\
i := i+1;\\
\{ r=n^i ~\&\&~ 0 \le i \le m ~\&\&~ n > 0 \}
\end{array}
\]
We propagate the invariant past $i:=i+1$ to get
$r=n^{i+1} ~\&\&~ 0 \le i+1 \le m ~\&\&~ n > 0$. We propagate this
past $r:=r*n$ to get:
$r*n=n^{i+1} ~\&\&~ 0 \le i+1 \le m ~\&\&~ n > 0$. Our proof
obligation is therefore:
\[
\begin{array}{l}
r=n^i ~\&\&~ 0 \le i \le m ~\&\&~ n > 0 ~\&\&~ i < m\\
\Rightarrow r*n=n^{i+1} ~\&\&~ 0 \le i+1 \le m ~\&\&~ n > 0
\end{array}
\]
We can prove this as follows:
\[
\begin{array}{ll}
r=n^i ~\&\&~ 0 \le i \le m ~\&\&~ n > 0 ~\&\&~ i < m & \mbox{by assumption}\\
r*n=n^i*n & \mbox{multiplying by $n$}\\
r*n=n^{i+1} & \mbox{definition of exponentiation}\\
0 \le i+1 & \mbox{because $0 \le i$}\\
i+1 < m+1 & \mbox{by adding 1 to inequality}\\
i+1 \le m & \mbox{by definition of $\le$}\\
n>0 & \mbox{by assumption}\\
r*n=n^{i+1} ~\&\&~ 0 \le i+1 \le m ~\&\&~ n > 0& \mbox{by conjunction of the above}\\
\end{array}
\]
We have a proof obligation to show that the variant function is
positive when we enter the loop. The obligation is to show that the loop
invariant and the entry condition imply this:
\[
\begin{array}{l}
r=n^i ~\&\&~ 0 \le i \le m ~\&\&~ n > 0 ~\&\&~ i < m\\
\Rightarrow m-i>0
\end{array}
\]
The proof is trivial:
\[
\begin{array}{ll}
r=n^i ~\&\&~ 0 \le i \le m ~\&\&~ n > 0 ~\&\&~ i < m & \mbox{by assumption}\\
i < m & \mbox{by assumption}\\
m-i > 0 & \mbox{subtracting $i$ from both sides}
\end{array}
\]
We also need to show that the variant function decreases. We generate
the proof obligation using weakest preconditions:
\[
\begin{array}{l}
\{ r=n^i ~\&\&~ 0 \le i \le m ~\&\&~ n > 0 ~\&\&~ i < m ~\&\&~ m-i = V \}\\
r := r*n;\\
i := i+1;\\
\{ m-i < V\}
\end{array}
\]
We propagate the condition past $i:=i+1$ to get $m-(i+1) < V$. Propagating
past the next statement has no effect. Our proof obligation is therefore:
\[
\begin{array}{l}
r=n^i ~\&\&~ 0 \le i \le m ~\&\&~ n > 0 ~\&\&~ i < m ~\&\&~ m-i = V\\
\Rightarrow m-(i+1) < V
\end{array}
\]
Again, the proof is easy:
\[
\begin{array}{ll}
r=n^i ~\&\&~ 0 \le i \le m ~\&\&~ n > 0 ~\&\&~ i < m ~\&\&~ m-i = V & \mbox{by assumption}\\
m-i = V & \mbox{by assumption}\\
m-i-1 < V & \mbox{by definition of $<$}\\
m-(i+1) < V & \mbox{by arithmetic rules}\\
\end{array}
\]
Last, we need to prove that the postcondition holds when we exit the
loop. We have already hinted at why this will be so when we chose the
loop invariant. However, we can state the proof obligation formally:
\[
\begin{array}{l}
r=n^i ~\&\&~ 0 \le i \le m ~\&\&~ n > 0 ~\&\&~ i \ge m\\
\Rightarrow r=n^m
\end{array}
\]
We can prove it as follows:
\[
\begin{array}{ll}
r=n^i ~\&\&~ 0 \le i \le m ~\&\&~ n > 0 ~\&\&~ i \ge m & \mbox{by assumption}\\
i = m & \mbox{because $i \le m$ and $i \ge m$}\\
r=n^m & \mbox{substituting $m$ for $i$ in assumption}\\
\end{array}
\]
\end{document}