% to get hardcopy of this lecture, you need the following files (plus the
% normal latex and tex base directories):
%     lecture.tex:     latex source file
%     macros.tex:      figure macros
%     psfig.tex:       postscript figure (psfig) macro definition
%     net.idraw:	figure, created with idraw
%     pocs-header.sty: Principles of Computer Systems lecture latex header
%
% run the following programs:
% latex lecture
%   -> Creates lecture2.dvi, plus latex intermediate files
% latex lecture
%   -> Run a second time to get cross-references right
% dvi2ps lecture |  egrep -v '^showpage$' > lecture.ps
%   -> Merges the figures with the text, can also pipe to lpr
%
\documentstyle[12pt,pocs-header]{article}
\Scribe{Carl G. Heinzl}
\Lecturer{Bill Weihl}
\LectureNumber{2}
\LectureDate{September 16, 1991}
\include{macros} % used to include figures (if any) in this document
\begin{document}
\MakeScribeTop

\section{Administrivia}

\begin{table}[h]
\centering
\begin{tabular}{|c|l|} \hline
\multicolumn{1}{|c|}{Handout} &
\multicolumn{1}{c|}{Title} \\ \hline

5 & Problem Set \#1, due 9/23/91 \\
(unnumbered) & Problem Set \#1 and Solutions from Fall 1990 \\
6 & A Hash Table Implementation \\
7 & Hints for Computer System Design, by Butler W. Lampson \\
8 & Spec Hints, by Butler W. Lampson \\
9 & Atomic Semantics of Spec, by Butler W. Lampson \\
\hline
\end{tabular}
\end{table}
Additionally, ``scribe'' signup sheet was circulated.

\section{Specifications for Sequential Systems}

For the next few weeks, we will ignore concurrency and will study 
the specification, verification, and performance of sequential systems.

\section{Data Abstraction}

Data abstraction allows us to encapsulate multiple operations as well
as state.  Normally, we would like to perform the abstraction
at as high a level as possible to remove unnecessary details from
the users of the system (i.e., they need not be concerned with
the details of the implementation, only the manner in which the
data structures change according to the specification).
Without data abstraction it would be quite difficult to
understand systems as they become larger and their data
structures grow in complexity.

\subsection{The Simple Memory Module}

As an example, consider the simple memory module (handout \#4)
that keeps track of a data value for each of some domain of 
addresses.  In Spec we have several different {\it levels}\/ with 
which to write.  Modules are essentially a building block for name
and procedure encapsulation.  Modules may have the following:

\begin{itemize}
\item Types
\item Variables (contain the state of the module)
\item Operations (procedures and functions)
\end{itemize}

Modules may be {\it parameterized}, which implies that the
module may be instantiated with different values for
the different type variables (e.g., A and D that represent the
address and data types in the simple memory module).
(NOTE: the simple memory module in handout \#4 may not be accurate 
with respect to the syntax for SPEC).
Types may provide mappings between data types, for example, in
the simple memory model we have a type ``M'' that maps
addresses to data, i.e., when we apply a type {\it M}\/ to
a type {\em A, or address}\/ we get a return of type 
{\it D, or data}.  Declarations of variables, unless
explicitly specified take on the (implicit) type 
by capitalizing them, e.g., {\it little}\/ m takes on the type of 
{\em capital}\/ M.  This is simply a convention of SPEC.
For further information read handout \#3 on SPEC.
The state of the simple memory module is simply one of these
functions, i.e., at any point in time you can think of the
state as being a function of addresses to data.

There four procedures in this example:

\begin{itemize}
\item Initialize (initializes the state of the variable {\it m} to
be a function that maps every address to the value passed to as an
argument).  initialize).
\item Read (simply returns the current state of the memory for
the given address {\it a}).
\item Write (writes value of {\it d} to address {\it a}).  
\item Swap
\end{itemize}

It is required that initialize be called {\it before} any of the 
other routines.  SPEC does not have a formal method of stating
such requirements, so we simply list it as a comment.  Note that
a write can be expressed by
\begin{verbatim}
    m := m{a->d}
\end{verbatim}
which is a function constructor that takes in one funtion
{\it m} and sets it to another function that has the same
values as the original {\it m} except with the value of
{\it a} mapped to the value d.  Swap is essentially a
read then write routine.

This a simple example of data abstraction.  For {\it procedures}\/ we
use relations between input and output in which the input and output
may include state.  What is the abstract behavior, or what do the
users {\it depend}\/ on?  Users care about the responses to a sequence
of invocations of the various routines (e.g., initialize followed by
some sequence of reads, writes, and swaps).  Initialize and write
simply return without passing any information back to the user, while
read and swap pass back data to the user.

One way of thinking about data abstraction abstractly is
that it is simply a set of traces (a trace is a set of
invocation/response pairs).  These traces describe
the behavior of the interface between the user and
the module. The user has no knowledge about how
these results are generated and cannot access the
internal state of the module.  A sample set of
traces might be:

\begin{center}
\vspace{.15in}
\begin{tabular}{c|c|c|l}
Input & Legal Sequence & Illegal Sequence & Comment\\
\hline
Init(d)	& Ret	& Ret 	& Initialize all values to d\\
R(a1)	& d	& x	& Read location a1\\
W(a2, x)& Ret	& Ret	& Write x to location a2\\
R(a2)	& x	& d	& Read location a2\\
\end{tabular}
\end{center}

So, read returns either the last value written to that address
or the initial value.  This is the {\bf ONLY} set of responses
that satisfy the spec for this sequences of invocations.
The second sequence is illegal because it returns $x$ for the 
first read, although the location should have the value $d$, and 
because it fails to return $x$ after it has been written to $a2$.  
Thus, the

This SPEC is deterministic in that, given a set of invocations,
there is a single set of responses that is acceptable.  This
{\bf is not} true in the general case, there may be multiple
responses.  So, the SPEC can be thought of as a relation
of invocation sequences to response sequence.  In real life,
this will be executed as one invocation at a time but abstractly
it is possible to model it as giving the module a set of
invocations and looking at the sequence of return values.

So, if we consider a {\bf Module} as just a set of traces of
input sequences to output sequences, it is very abstract.  This
is useful when we think about {\it correctness}.  That is
our abstract view of our module and is exactly what the 
specification denotes, which is also the information provided
to a client (i.e., what the client can depend on).  The text
of the implementation can be abstracted in the same way to
a set of traces that the implementation will generate by
looking at the boundary between the module and its clients.

\section{Definition of Correctness}

If we think of the SPEC as denoting a set of traces and the
implementation as denoting another set of traces, then the
definition of correctness is to have the implementation
be (in general) a {\bf subset} of the traces that the SPEC 
allows.  It is a subset since there is no way for a client
to tell if the implementation permits or incorporates {\bf ALL} of 
the non-determinism that may be present in the SPEC.

So, there is a {\bf DOMAIN} of input values and a {\bf RANGE}
of output values.  Not every possible output value {\bf must}
be generated, but something {\bf must} be done for every
possible value of the input.  This range of output values
{\it may}\/ include non-termination or an incorrect answer.

\subsection{Partial and Total Correctness}

This definition raised the question of termination.  For example, if
$R_i$ was $\emptyset$, it would trivially satisfy the subset relation,
although it would not terminate and return a value where the
implementation could.  We will be primarily concerned with {\it
partial correctness}, as opposed to {\it total correctness}.  Partial
correctness requires that whenever the procedure terminates, it
returns a correct value; total correctness further requires that the
implementation terminates for the same of inputs in which the
specification terminates.

\section{A Write-Back Cache}

In handout \#4 pages 2 -- 4 we see simply another implementation of
the Simple Memory Specification, this time using a write-back caching
algorithm.  This implementation basically says:

\begin{itemize}
\item if the data corresponding to the requested memory address is 
in the cache, return that value
\item otherwise return the value value stored in the main memory
\end{itemize}

Initialize has a different interface since it also now takes a 
size parameter to determine the size of the cache.  Strictly 
speaking this is not an implementation of the simple memory module, 
any longer, but we will overlook this difference for now.

Note that in the initialize routine will cause {\it HAVOC}\/ if the
size passed in for the cache is less than or equal to 0.  This
is something that is appropriate for writing specifications rather
than implementation.  {\bf HAVOC} implies that literally anything
can happen.  This is one of the methods that requirements or
preconditions on the caller are specified (e.g., if a binary
search algorithm is performed on an array, the array must
be sorted, otherwise the binary search won't work).  We don't
want to check the array to make sure it's sorted, because this
would throw away the advantage of using a binary search
algorithm since the check would take linear time.

\paragraph{State Machines}

Because it would be impossible to enumerate all sequences, proofs are
done inductively.  The {\it state machine} is a useful abstraction.
For the memory example, suppose that A, the set of memory locations,
is \{1, 2, 3, 4\} and D, the set of possible values, is \{a, b, c\}
and we have a two-location cache.  The state of memory prior to
initialization could be undefined (i.e., it may not even have a member
of the set of allowable values).  Following is one of the possible
transitions that the INIT(c) invocation could produce.  Note that the
current state of the memory is irrelevant when calling the INIT()
procedure.

We're just assuming undefined values here, any 
input value would produce the same output value for a given 
constant passed to the INIT() routine.  

\begin{center}
\begin{tabular}{|l|l|l|c|l|l|l|}
\cline{1-3} \cline{5-7}
A & D & C & & A & D & C	\\
\cline{1-3} \cline{5-7}
1 & / & / & & 1 & c & /	\\
\cline{1-3} \cline{5-7}
2 & / & / & $\longrightarrow$ & 2 & c & c \\
\cline{1-3} \cline{5-7}
3 & / & / & INIT(c) & 3 & c & / \\
\cline{1-3} \cline{5-7}
4 & / & / & & 4 & c & c \\
\cline{1-3} \cline{5-7}
\end{tabular}
\end{center}
\vspace{.15in}

In this example the init also wrote the values into the
cache locations for memory locations 2 and 4.  Note that there
are multiple initial states, depending on which cache
address are defined after the initialization takes place.
Without the cache, this (the invocation of INIT(c)) would have 
been a deterministic invocation.  Assuming (as we do here) that 
we have two cache locations, as long as exactly two cache locations 
are defined, the INIT() routine will have run sucessfully.  

A following write (e.g. {\bf WRITE(1,a), RET}) could produce
the following transition:

\begin{center}
\begin{tabular}{|l|l|l|c|l|l|l|}
\cline{1-3} \cline{5-7}
A & D & C & & A & D & C	\\
\cline{1-3} \cline{5-7}
1 & c & / & & 1 & c & a	\\
\cline{1-3} \cline{5-7}
2 & c &  c & $\longrightarrow$ & 2 & c & c \\
\cline{1-3} \cline{5-7}
3 & c & / & INIT(c) & 3 & c & / \\
\cline{1-3} \cline{5-7}
4 & c &  c & & 4 & c & / \\
\cline{1-3} \cline{5-7}
\end{tabular}
\end{center}
\vspace{.15in}

Note that we can throw out ANY location that is currently in the
cache.  In this example we could have thrown out location 2 instead
of location 4.  We will be asked to implement a procedure and
data abstraction and then describe the input/output
relation for the procedure and the state transition for the
data abstraction.  We will use words or simple math to describe the
state relationships.

\section{Proving Correctness}

The goal was to show the correctness of this releationship, i.e., all 
the traces produced by the implementation are are allowed by the 
specification.  We may use data abstraction to proceed from proving 
the correctness of individual procedures to entire modules.  
Tools we use are to prove correctness are:

\begin{itemize}
\item Abstraction functions.
\item Representation invariants.
\end{itemize}

\subsection{Abstraction Functions}
             
The {\it Abstraction Function}\/ tells us how to interpret states
of the implementation and if they are states of the specification,
essentially providing a mapping between them.  Think about the
implementation as simulating the specification.  The 
{\it Representation Invariant}\/ is a predicate on the state of
the implementation that should be true for every state that
is reachable by applying the transitions.  Handout \#4 (on the
bottom of page \# 3) has an abstraction for the SingleCache
implementation.  It takes the state of SingleCache, a
cache value and the integer csize, and returns the state of
the SimpleMemory, which is just a function M mapping A to D.

We want the transitions of the implementation to map to
transitions of the specification.  The abstraction function is 
a many to one function with many states in the implementation 
mapping to a single state in the specification.  To do this, apply 
the abstraction function to the initial and final states of the
transition and see if that transition is allowed by the
specification.  The second property that we want is
that initial states map to initial states.  We want this
so that we can successfully argue inductively that if we
take a trace of the state machine that we can map it through
the abstraction function and those transitions will also
be allowed by the state machine of the specification.
We can then inductively prove that any finite sequence
of the implementation is also a trace of the specification.
So, in summary, we want:

\begin{itemize}
\item Initial states map to initial states.
\item For every transition in the Implementation (from S1 to S2);
if we apply the abstraction function to S1 then apply the transition
in the specification, the resulting state is the image that we 
get by applying the abstraction function to S2.
\end{itemize}

\subsection{Representation Invariants}

For the SingleCache module, all we need is the abstraction
function given in handout \#4.  This is enough to prove with
a reasonably careful argument that this is true for all the
transitions in the implementation as well as the initial
state. 

Moving onto another example, see handout \#6, {\it A Hash 
Table Implementation}. This example is a hash table implementation
of the simple memory module.  Read simply performs a hash on
the incoming address and then calls FindEntry to determine if
the requested address is present in the hased bucket.  Write
first deletes the entry (if present) then adds the new value
to the appropriate hash bucket.

There is more than one way to determine the {\it AF} of this
implementation.  One such way is in the handout in which
all of the pairs stored in all of the hash table buckets.
This list of pairs is a representation of a function, then
if we have a pair for a given address then the data value is
stored in that pair, otherwise we return the initial value
(which is stored elsewhere).  This relies on the assumption
that these is at most one pair for a given address.

Another way to define the {\it AF} is to make use of the
hash function itself.  So, to determine the data for a
given address, we could look in the appropriate bucket.
The {\it AF} in the handout takes {\bf ALL} of the pairs,
regardless of whether they're in the correct bucket.

Now, using the {\it AF} in the handout, we now want to
prove the implementation correct.  Suppose we have a state
in which a pair for address 1 appears in the wrong bucket
(bucket \# 1 instead of \# 2).  The {\it AF} maps that
to a valid state of the specification, but the problem is
that READ(1) will now return the wrong answer because it
will look into the wrong (actually correct) bucket.  We
cannot therefore prove it correct if we attempt to allow
such states.  In reality, this implementation doesn't 
store pairs in the wrong buckets, so even though we
can construct states in which the addresses are in the
wrong buckets, this will not occur.  We can rely on
this property while doing the proof.  Before trying to prove
these properties about the abstraction function, we want
to state and prove the {\it representation invariant},
a predicate on the states of this implementation that's
always true when we're not in the middle of one of these
procedures.  The rep invariant may be violated if (one
or more of) these procedures have to do several updates,
but for now we're considering these to be essentially
atomic procedures.

We want to prove the rep invariant first.  It is only a property
of the implementation and says nothing about the specification
or how the implementation relates to the specification, but
it will allow us to prove the {\it AF}.

In order to figure out what rep invariant we need to make this proof
go through, look through each of the transitions that we want to
reason about and try to do the proof and discover why it fails, add
properties (constraints) to the state, and then go on.  We will end up
with a list of properties of the state that must be true.  Another way
is to start with intuition and knowledge about how the implementation
works and write down some set of properties that seem to be
fundamental to the whole implementation then plug and chug (i.e., try
the transitions to see if they work).  In summary, for the hashed
memory module, we need to know:

\begin{enumerate}
\item The hash function produces values in the correct range.
\item Any pair containing ``a'' is stored in the bucket at the 
index given by the hash function.
\item There is only one entry for a given address in that bucket.
\end{enumerate}

Given these, the proof on the abstraction function can be
completed.  The rep invariant itself must be subjected to
a similar inductive proof.  For example, take a rep invariant 
R(I) we must show that

\begin{enumerate}
\item {\bf Base} Show that all initial states satisfy the invariant.
\item {\bf Induction} Show that all transitions preserve the invariant.
\end{enumerate}

For example, first show that R(I) is true for all initial states, i.e. after 
initialize, then secondly, show that if RI(S) is true before we 
start a transition that takes S to S', then we want to know that 
RI(S') is true.  Now, inductively any set of transitions will
take us to a state in which the rep invariant is satisfied.

\end{document}
