\documentclass[11pt]{article}
\input{macros-course-lectures}

\setlength{\textheight}{9in}
\setlength{\textwidth}{6in}
\setlength{\evensidemargin}{0in}
\setlength{\oddsidemargin}{0in}
\setlength{\topmargin}{0in}
\setlength{\headheight}{0in}
\setlength{\headsep}{0.3in}
\newcommand{\implies}{\longrightarrow}

\newcommand{\ms}[1]{%
        \relax\ifmmode
                \mathord{\mathcode`\-="702D\it #1\mathcode`\-="2200}%
        \else
                {\it #1}%
        \fi
}

\begin{document}

\title{\bf Notes on Computational Processes, Installment 3}
\author{Nancy Lynch\footnote{\copyright Nancy Lynch, 2000}}
\maketitle

%\renewcommand{\thepage}{\arabic{page}}

\section{Overview}

We've seen the basic idea of a computational process and how to model
it. \\
Computational processes include simple (nondeterministic) games,
ordinary sequential programs, abstract descriptions of system services. \\
\\
Can also include more complex systems. \\
\\
Examples: \\
Computer operating systems \\
World-Wide Web. \\
\\
However, such systems are too big to model as one big,
complicated state machine. \\
We need ways of breaking the descriptions into pieces. \\
Structure. \\
\\
What kind? \\
Two main methods are used: \\
Abstraction, composition. \\
\\
Abstraction: \\
\\
This refers to giving high-level descriptions of systems, separate
from the detailed implementations. \\
Can  use high-level data types, like relations, graphs, trees, \\
rather than (or in addition to) low-level types, like numbers,
Booleans, and strings. \\
Use high-level operations, e.g., taking transitive closure of
a complicated relation, coloring a graph. \\
Can reason about the system's behavior in terms of such high level
data types and operations. \\
\\
Then separately, implement the high-level data types and operations in
terms of available low-level data types and operations. \\
Now just have to convince ourselves that the implementation correctly
implements the high-level description, \\
but don't have to think anymore about correctness of the high-level
description. \\
Have broken down both the implementation and the reasoning about its
correctness. \\
\\
Composition: \\
\\
This refers to building a system up out of individual components. \\
Build the components separately, give specifications for what each one
does, reason separately about how each component implements its
specification. \\
\\
These kinds of system decomposition are very common in practical
programming. \\
What is not so common is very careful reasoning about the correctness
of the decomposed implementations. \\
Usually, reasoning about correctness of complex systems is done fairly
informally. \\
But ideally, we could be quite rigorous, e.g., doing really proofs
about system behavior. \\
\\
To do this, we need the right math to support it. \\
\\
What math? \\
\\
1. The basis is just what we've been studying, state machines to model 
computational processes. \\
\\
For system decomposition, need a little more: \\
\\
2. Need to capture the idea that some parts of a computational process
(system component) are intended to be {\em externally visible\/}, so
they can be used by people or by other programs. \\
On the other hand, other parts of a computational process are only
used for the implementation, and don't need to be seen from the
outside. \\
\\
3. Need to capture the idea that one state machine {\em implements\/}
another, so it can be substituted for it in larger systems. \\
This will be based on saying that any externally-visible behavior of
one machine can also be exhibited by the other machine. \\
\\
The notions of external behavior and implementation are fundamental to
the process of decomposing systems. \\
\\
In the rest of this lecture, I will: \\
1. Define externally-visible behavior of a state machine (and give
examples). \\
2. Define what it means for one state machine to ``implement'' another
one (and give examples). \\
3. Give a theorem that expresses one method for showing that a state
machine implements another one. \\
4. Say just a little, informally, about composition.

\section{Externally-visible behavior}

\subsection{GCD example}

GCD algorithm, using subtraction. \\
Started with the inputs in the state, ended up with the output in this
state, in state component $x$. \\
We knew it was the answer when $x=y$. \\
\\
Modify this so the inputs come into the algorithm in separate transitions,
and the GCD output is provided by the algorithm in an output
transition: \\
\\
$Q = \{ (x0,y0,x,y) \in N^+ \cup \{ \ms{null} \}$. \\
$Q_0$: 	Initially all null. \\
\\
$\delta$: \\
Has three kinds of steps: input steps, output steps, and internal
(computation) steps: \\
\\
Input steps: \\
Set up the computation. \\
Notice the ``if'' test---if an input arrives when another one is
active, it's ignored. \\
The intention is that the first input is supposed to be processed
first, before any new input is accommodated. \\
This state machine is written so that after it finishes one GCD
computation, another one can begin (with a new input). 
\begin{tabbing}
XXX\=XXX\= \kill
$\ms{request-GCD}(a,b)$ steps: \\
if $x0 = \ms{null}$ then \\
\> $x0 := a$ \\
\> $y0 := b$ \\
\> $x := a$ \\
\> $y := b$ 
\end{tabbing}
\\
Computation steps: \\
Each performs one iteration of the subtraction-based GCD algorithm. 
\begin{tabbing}
XXX\=XXX\= \kill
$\ms{compute}$: \\
Can occur if $x \neq y$ \\
Effect: \\
if $x > y$ then $x := x-y$ \\
else $y := y-x$ 
\end{tabbing}

Output steps: \\
This can happen when $x = y$, and returns the answer to the client. \\
Also zeroes out the variables so new inputs can be accommodated.
\begin{tabbing}
$\ms{return-GCD}(r)$: \\
Can occur if $x = y = r$. \\
Effect: 
$x0, y0, x, y := \ms{null}$ 
\end{tabbing}
\\
\\
How can we talk about externally-visible behavior of this machine? \\
\\
Make the labels $\ms{request-GCD}(a,b), a, b \in N^+$,
$\ms{compute}$, $\ms{return-GCD}(r), r \in N^+$ part of the formal model. \\
\\
Augment the basic definition of a state machine to allow ``labels''
for steps (see next subsection). \\
\\
Also, augment the definition of an execution to include the labels. \\
\\
Example: \\
An execution of the labeled state machine for GCD: \\
$(\ms{null},\ms{null},\ms{null},\ms{null})$, $\ms{request-GCD}(9,6)$,$(9,6,9,6)$, \\
$\ms{compute}$,$(9,6,6,3)$, $\ms{compute}$,$(9,6,3,3)$, \\
$\ms{return-GCD}(3)$, $(\ms{null},\ms{null},\ms{null},\ms{null})$. \\
\\
\\
External behavior: \\
\\
What should be considered to be the externally-visible behavior of
this process? \\
Can reasonably say that what the client sees is just the input he
submits and the return values he gets back. \\
Model this by extracting the input and output labels from an
execution. \\
Suppress internal labels and states. \\
What's left is called a ``trace''. \\
\\
Example: \\
From the execution above, we can extract the trace: \\
$\ms{request-GCD}(9,6)$, $\ms{return-GCD}(3)$. \\
\\
Notice that this trace only tells us the input and output---it says
nothing about how the process actually computed the GCD (by repeated
subtraction steps). \\
The machine might have used another algorithm. \\
\\
\\
What might we prove about executions of this labeled state machine? \\
E.g., look at the simple case of exactly one input: \\
\\
If an execution contains exactly one $\ms{request-GCD}$ label, then: \\
-- It contains at most one $\ms{return-GCD}$ label. \\
-- If it contains a $\ms{return-GCD}(r)$ label, then $r$ is the GCD of
the arguments in the $\ms{request-GCD}$ label. \\
\\
Could also prove an eventuality property, saying that the answer is
eventually produced. \\
But this is a little tricky: \\
We have to hypothesize a live execution in order to talk about
eventuality. \\
But liveness is a little tricky to define in the presence of inputs
and outputs. \\
I won't go into this here, in fact, won't mention liveness again
today. \\
\\
Notice that these properties only talk about externally-visible
behavior. \\
Many interesting properties of processes are formulated that way. \\
\\
\\
Example: \\
\\
Recall that, a couple of weeks ago, we presented an alternative
version of the GCD algorithm, where at each step, instead of
subtracting $x-y$ (or $y-x$) to get a new value for the next step, we
let the new value be the remainder on division of $x$ by $y$
(or $y$ by $x$). \\
That is, if $x \geq y$ we move to $(y, x \mod y)$ instead of
$(x-y,y)$, and if $y > x$ we move to $(x, y \mod x)$. \\
This stops when the remainder reaches $0$, and the other number is the
GCD. \\
\\
Can formulate this as a labeled state machine, similar to the
one for the subtraction-based GCD. \\
(Some technical issues about handling of $0$.) \\
%[[[Type of x and y must be N, since 0 can occur.]]]
\\
Although executions of this new labeled state machine are different from
those for the other one, the two machines have the same traces. \\
Thus, in a sense, they are ``equivalent'', as far as a client could
tell. \\
\\
(Could have different performance---we're just talking about
correctness here.)

\subsection{Labeled state machines---formal definitions}

Now we can generalize what we did for the GCD state machine, to give a
general definition of a labeled state machine, and its executions and
traces. \\
\\
A {\em labeled state machine} consists of: \\
\\
$(Q,Q_0,L,\delta)$, where: \\
\\
$Q$, $Q_0$ are sets of states and initial states, as before. \\
$L$ is a set of {\em labels\/}, partitioned into input, output and
internal labels. \\
$\delta \subseteq Q \times L \times Q$. \\
\\
Thus, every step is labeled with a ``label'' (element of $L$), which
may be an input, output or internal label. \\
\\
An {\em execution\/} is a (possibly infinite) sequence 
$q_0, \ell_1, q_1, \ell_2, q_2, \ldots$ such that: \\
$q_0 \in Q_0$, and \\
$\forall i \geq 0 (q_i,\ell_{i+1},q_{i+1}) \in \delta$. \\
\\
Thus, the execution includes the labels as well as the states. \\
\\
The {\em trace\/} of an execution is the subsequence of the execution
sequence consisting of all the external (input and output) actions. \\
\\
\\
A labeled state machine is not very different from an ordinary state
machine.  \\
It just adds the labels. \\
In particular, the methods we have already studied for proving things
about ordinary state machines---invariants and termination
functions---can also be used for labeled state machines. \\
\\
However, the labels will let us do other things, namely, to talk about
system structure.\\
\\
The rest of this section contains some more examples of labeled
state machines and their external behavior.

\subsection{Relation example}

Consider a state machine that keeps track of a ``dynamic'' binary
relation on a set $S$. \\
Assume $S$ is a fixed finite set, of fixed size $n$. \\
The relation starts out empty, and a client can add and remove pairs. \\
(Can think of adding and removing edges from a digraph, if you want to
be more concrete.) \\
\\
The client can also perform some queries: \\
Asking if a particular pair is (currently) \\
-- In the relation \\
-- In the reflexive transitive closure of the relation (that is,
connected by some walk \\
And the state machine is supposed to respond appropriately. \\
\\
This interaction can go on forever. \\
\\
Labeled state machine: \\
The state consists of the current relation, plus some bookkeeping info
about active operations. \\
\\
$Q$: \\
$\ms{rel}$, a binary relation on $S$ \\
$\ms{rtc}$,  a binary relation on $S$ \\
$\ms{reqtype} \in \{ ``add'', ``remove'', ``in-relation?'', ``in-closure?'' \} \cup
\{ \ms{null} \} $ \\
$\ms{reqpair} \in S \times S \cup \{ \ms{null} \}$ \\
$\ms{retval} \in \{ ``OK'' \} \cup Bool \cup \{ \ms{null} \}$ \\
\\
$Q_0$: \\
$\ms{rel}$, $\ms{rtc}$ initially contain no pairs (empty relation). \\
$\ms{reqtype}$, $\ms{reqpair}$, $\ms{retval}$ all $\ms{null}$ (no
active operation). \\
\\
$L$: \\
Input labels: \\
$\ms{request-add}(a,b)$, $a,b \in S$ \\
$\ms{request-remove}(a,b)$, $a,b \in S$ \\
$\ms{request-in-relation?}(a,b)$, $a,b \in S$ \\
$\ms{request-in-closure?}(a,b)$, $a,b \in S$ \\
\\
Output labels: \\
$\ms{return}(``OK'')$ \\
$\ms{return}(b)$, $b \in Boolean$ \\
\\
Internal labels: \\
$\ms{compute-add}$ \\
$\ms{compute-remove}$ \\
$\ms{compute-in-relation?}$ \\
$\ms{compute-in-closure?}$ \\
\\
$\delta$: \\
\\
Input, output and internal steps. \\
\\
Input steps: \\
Record the request type in the bookkeeping state components. \\
If there is an active request, the new request is ignored. 
\begin{tabbing}
XXX\=XXX\= \kill
$\ms{request-add}(a,b)$ \\
if $\ms{reqtype} = \ms{null}$ then \\
\> $\ms{reqtype} := ``add''$ \\
\> $\ms{reqpair} := (a,b)$ \\
\\
$\ms{request-remove}(a,b)$ \\
if $\ms{reqtype} = \ms{null}$ then \\
\> $\ms{reqtype} := ``remove''$ \\
\> $\ms{reqpair} := (a,b)$ \\
\\
$\ms{request-in-relation?}(a,b)$ \\
if $\ms{reqtype} = \ms{null}$ then \\
\> $\ms{reqtype} := ``in-relation?''$ \\
\> $\ms{reqpair} := (a,b)$ \\
\\
$\ms{request-in-closure?}(a,b)$ \\
if $\ms{reqtype} = \ms{null}$ then \\
\> $\ms{reqtype} := ``in-closure?''$ \\
\> $\ms{reqpair} := (a,b)$ \\
\end{tabbing}
The compute steps do the real work. \\
Processing the add step involves putting the new pair into
$\ms{rel}$, and recalculating $\ms{rtc}$. \\
Here, I'll do this in one big step, that is, this state machine works
at a high level of abstraction where an entire reflexive transitive
closure operation occurs in one step. \\
(I could also have given a more detailed state machine that did a
series of successive relation-squaring steps.) \\
Finally, set $\ms{retval} := ``OK''$. \\
\\
Remove is analogous. \\
\\
$\ms{request-in-relation?}$ requires just one compute step, which
looks up the answer and puts it into retval component. \\
\\
$\ms{request-in-closure?}$ analogous, looking up in rtc. \\
\\
\begin{tabbing}
XXX\=XXX\= \kill
$\ms{compute-add}$ \\
Can occur if $\ms{reqtype} = ``add''$ and $\ms{retval} = \ms{null}$ \\
Effect: \\
$\ms{rel} := \ms{rel} \cup \{ \ms{reqpair} \}$ \\
$\ms{rtc} :=$ reflexive transitive closure of $\ms{rel}$ \\
$\ms{retval} := ``OK''$ \\
\\
$\ms{compute-remove}$ \\
Can occur if $\ms{reqtype} = ``remove''$ and $\ms{retval} = \ms{null}$ \\
Effect: \\
$\ms{rel} := \ms{rel} - \{ \ms{reqpair} \}$ \\
$\ms{rtc} :=$ reflexive transitive closure of $\ms{rel}$ \\
$\ms{retval} := ``OK''$ \\
\\
$\ms{compute-in-relation?}$ \\
Can occur if $\ms{reqtype} = ``in-relation''$ and $\ms{retval} = \ms{null}$ \\
Effect: \\
if $\ms{reqpair} \in \ms{rel}$ then $\ms{retval} := \ms{true}$ \\
else $\ms{retval} := \ms{false}$ \\
\\
$\ms{compute-in-closure?}$ \\
Can occur if $\ms{reqtype} = ``in-closure''$ and $\ms{retval} = \ms{null}$ \\
Effect: \\
if $\ms{reqpair} \in \ms{rtc}$ then $\ms{retval} := \ms{true}$ \\
else $\ms{retval} := \ms{false}$ \\
\end{tabbing}
The output steps return the retval to the client and
clean out the bookkeeping state components, thus allowing another
operation to be handled: 
\begin{tabbing}
XXX\=XXX\= \kill
$\ms{return}(``OK'')$ \\
Can occur if $\ms{retval} = ``OK''$ \\
Effect: \\
$\ms{reqtype}, \ms{reqpair}, \ms{retval} := \ms{null}$ \\
\\
$\ms{return}(b)$, $b \in Boolean$ \\
Can occur if $\ms{retval} = b$ \\
Effect: \\
$\ms{reqtype}, \ms{reqpair}, \ms{retval} := \ms{null}$ 
\end{tabbing}
\\
Example trace: \\
Assume $S = \{ 1, 2, 3, 4 \}$. \\
$\ms{request-add}(1,2)$, $\ms{return}(``OK'')$, $\ms{request-add}(2,3)$, $\ms{return}(``OK'')$, \\
$\ms{request-add}(3,4)$, $\ms{return}(``OK'')$, \\
$\ms{request-in-closure?}(1,4)$, $\ms{return}(\ms{true})$, \\
$\ms{request-remove}(3,4)$, $\ms{return}(``OK'')$, \\
$\ms{request-in-closure?}(1,4)$, $\ms{return}(\ms{false})$. \\
\\
\\
What might we prove about this machine? \\
If an execution does not contain two consecutive input steps without
an intervening output step, then: \\
-- It does not contain two consecutive responses. \\
-- Responses correctly reflect the preceding add and remove operations. \\
\\
Of course, must state these more formally. \\
\\
Again, note that these properties only talk about externally-visible
behavior. \\
\\
Note that many other state machines have the same external behavior as
this one: \\
\\
1. Multi-step closure calculation \\ 
\\
2. Lazy evaluation of the closure, only when an in-closure? request is
made. \\
\\
3. Manipulate a lower-level representation for the relation, like a
matrix or a set of lists. \\
Come back to this in a few minutes, under ``abstraction''.

\subsection{Caching example}

Modify the caching service example to model interaction with two
clients, one at each cache. \\
\\
That is, add labels for inputs from clients: \\
$\ms{request-read}(x,1)$ is an input from client $1$ requesting to
read the value in address $x$ from his local cache ($\ms{cache1}$). \\
$\ms{request-read}(x,2)$ same, for client $2$. \\
$\ms{request-write}(x,v,1)$, $\ms{request-write}(x,v,2)$ are requests
by the two clients to write value $v$ in location $x$ in memory (not
cache). \\
\\
These steps just record the request (if there's no other active
request at the same location). \\
Requires bookkeeping info, as usual. \\
We allow operations to be active simultaneously at both locations. \\
\\
Output labels for corresponding returns: \\
$\ms{return-read}(v,1)$, $\ms{return-read}(v,2)$ \\
$\ms{return-write}(``OK'',1)$, $\ms{return-write}(``OK'',2)$. \\
\\
These steps just return determined responses. \\
\\
Internal labels: \\
$\ms{compute-read}$ does the actual read from a local cache, after
it's been requested and the value is non-null. \\
$\ms{compute-write}$ does the actual write to memory, after it's been
requested. \\
$\ms{copy}$ copies a value from memory to a cache. \\
$\ms{drop}$ drops a value from a cache. \\
\\
Copy and drop can happen any time, not just during the performance of
a read or write operation. \\
\\
This general description applies to both weak and strong caches. \\
The only difference is that, in the strong case, a perform-write step
cleans out that location in both caches. \\
\\
\\
More detailed model for weak caching system: \\
\\
$Q$: \\
$\ms{memory}$, a function from $\ms{addresses}$ to $\ms{values}$ \\
$\ms{cache1}, \ms{cache2}$, functions from $\ms{addresses}$ to
$\ms{values} \cup \{ \ms{null} \}$ \\
\\
$\ms{req1} \in \{ ``read'', ``write'' \} \cup \{ \ms{null} \}$ \\
$\ms{reqaddress1} \in \ms{addresses} \cup \{ \ms{null} \}$ \\
$\ms{reqval1} \in \ms{values} \cup \{ \ms{null} \}$ \\
$\ms{retval1} \in \ms{values} \cup \{ ``OK'', \ms{null} \}$ \\
\\
$\ms{req2} \in \{ ``read'', ``write'' \} \cup \{ \ms{null} \}$ \\
$\ms{reqaddress2} \in \ms{addresses} \cup \{ \ms{null} \}$ \\
$\ms{reqval2} \in \ms{values} \cup \{ \ms{null} \}$ \\
$\ms{retval2} \in \ms{values} \cup \{ ``OK'', \ms{null} \}$ \\
\\
$Q_0$: \\
$\ms{memory}$ arbitrary, caches all \ms{null}$, bookkeeping components
all $\ms{null}$ \\
\\
$L$: \\
Input: \\
$\ms{request-read}(x,i)$, $x \in \ms{addresses}$, $i \in \{ 1, 2 \}$ \\
$\ms{request-write}(x,v,i)$, $x \in \ms{addresses}$, $v \in
\ms{values}$, $i \in \{ 1, 2 \}$ \\
\\
Output: \\
$\ms{return-read}(v,i)$, $v \in \ms{values}$, $i \in \{ 1, 2 \}$ \\
$\ms{return-write}(``OK'',i)$, $i \in \{ 1, 2 \}$ \\
\\
Internal: \\
$\ms{compute-read}(x,i)$, $x \in \ms{addresses}$, $i \in \{ 1, 2 \}$ \\
$\ms{compute-write}(x,v,i)$, $x \in \ms{addresses}$, $v \in
\ms{values}$, $i \in \{ 1, 2 \}$ \\
$\ms{copy}(x,i)$, $x \in \ms{addresses}$, $i \in \{ 1, 2 \}$ \\
$\ms{drop}(x,i)$, $x \in \ms{addresses}$, $i \in \{ 1, 2 \}$ 
$\delta$ \\
\begin{tabbing}
XXX\=XXX\= \kill
Input: \\
$\ms{request-read}(x,1)$ \\
if $\ms{req1} = \ms{null}$ then \\
\> $\ms{req1} := ``read''$ \\
\> $\ms{reqaddress1} := x$ \\
\\
$\ms{request-write}(x,v,1)$ \\
if $\ms{req1} = \ms{null}$ then \\
\> $\ms{req1} := ``write''$ \\
\> $\ms{reqaddress1} := x$ \\
\> $\ms{reqval1} := v$ \\
\\
Inputs for client 2 analogous. \\
(Of course, I should have parameterized the state machine by the client
indices, but I won't redo this now.) \\
\\
Internal: \\
$\ms{compute-read}(x,1)$ \\
Can occur when $\ms{req1} = ``read''$, 
$\ms{reqaddress1} = x$, 
$\ms{cache1}(x) \neq \ms{null}$,\\
and $\ms{retval1} = \ms{null}$ \\
Effect: \\
$\ms{retval1} := \ms{cache1}(x)$ \\
\\
$\ms{compute-write}(x,v,1)$ \\
Can occur when $\ms{req1} = ``write''$, $\ms{reqaddress1} = x$,
$\ms{reqval1} = v$, and $\ms{retval1} = \ms{null}$ \\
Effect: \\
$\ms{memory}(x) := v$ \\
$\ms{retval1} := ``OK''$ \\
\\
$\ms{copy}(x,1)$ \\
Can occur anytime \\
Effect: \\
$\ms{cache1}(x) := \ms{memory}(x)$ \\
\\
$\ms{drop}(x,1)$ \\
Can occur anytime \\
$\ms{cache1}(x) := \ms{null}$ \\
\\
And same for 2 \\
\\
Output: \\
\\
$\ms{return-read}(v,1)$ \\
Can occur if $\ms{retval1} \in \ms{values}$ \\
Effect: \\
$\ms{req1}, \ms{reqaddress1}, \ms{reqval1}, \ms{retval1} := \ms{null}$ \\
\\
$\ms{return-write}(``OK'',1)$ \\
Can occur if $\ms{retval1} = ``OK''$ \\
Effect: \\
$\ms{req1}, \ms{reqaddress1}, \ms{reqval1}, \ms{retval1} := \ms{null}$ \\
\\
\\
For strong caching, same except for the compute-write:\\
\\
$\ms{compute-write}(x,v,1)$ \\
Can occur when $\ms{req1} = ``write''$, $\ms{reqaddress1} = x$,
$\ms{reqval1} = v$, and $\ms{retval1} = \ms{null}$ \\
Effect: \\
$\ms{memory}(x) := v$ \\
$\ms{cache1}(x), \ms{cache2}(x) := \ms{null}$ \\
$\ms{retval1} := ``OK''$ \\
\\
\end{tabbing}
Example trace for strong caching system: \\
\\
Assume that the value set is $N$. \\
$\ms{request-write}(x,3,1)$ (client $1$ requests to write value $3$ in
location $x$), \\
$\ms{request-read}(x,2)$ (client $2$ requests to read value of $x$ from
its cache), \\
$\ms{return-read}(3,2)$, \\
$\ms{return-write}(``OK'',1)$. \\
\\
This is OK because it arises, e.g., from an execution consisting of
steps with the following labels: \\
$\ms{request-write}(x,3,1)$, \\
$\ms{compute-write}(x,3,1)$, \\
$\ms{copy}(x,2)$, the write gets copied into cache2, \\
$\ms{request-read}(x,2)$, client $2$ requests to read, \\
$\ms{compute-read}(x,2)$, the read happens locally, from cache2, \\
$\ms{return-read}(3,2)$, \\
$\ms{return-write}(``OK'',1)$. \\
\\
Example trace for weak caching system, but not strong caching system:
\\
\\
$\ms{request-write}(x,3,1)$ (client $1$ requests to write value $3$ in
location $x$), \\
$\ms{return-write}(``OK'',1)$, \\
$\ms{request-read}(x,2)$, \\
$\ms{return-read}(0,2)$. \\
\\
This can arise from an execution where the memory starts with $0$ in
location $x$, \\
and this gets copied to cache2, \\
then client $1$ requests the write, which gets performed and returns,
but without cleaning the old value out of cache2. \\
Then the read gets performed from cache2, getting the old value. \\
\\
This trace cannot arise for strong cache, because the old value
would get cleaned out when the write occurs to memory. \\
\\
\\
Things to prove? \\
\\
Weak cache:  Fairly weak guarantees, e.g.: \\
Returns either the initial value or some value someone requested to
write. \\
(Could show some weak constraints on order of values returned.) \\
\\
Strong cache:  Some stronger guarantees, e.g.: \\
(Special case): \\
Suppose a write operation $W$ to location $x$ completes before a read
$R$ of $x$ begins. \\
Suppose there are no active writes to $x$ after $W$ completes. \\
Suppose $R$ returns. \\
Then $R$ returns the value written by write $W$. 

%\subsection{Other examples}
%
%Not all state machines interact by simple call-return inputs and
%outputs. \\
%\\
%Counter: \\
%No inputs, just outputs $1, 2, \ldots$. \\
%\\
%Like a clock. \\
%\\
%The state machine would be like the counter machine we saw earlier,
%only now the steps that move from one value to the next would be
%labeled by output labels giving the new number. \\
%\\
%\\
%Communication channel: \\
%Someone sends a message, later gets delivered someplace, or many
%places. \\
%\\
%The state machine might have a state that's a sequence of message
%strings. \\

\section{Implementation}

\subsection{Overview}

Now we have enough math machinery to describe what it means for one
computational process to ``implement'' another. \\
\\
Definition: \\
Suppose that $A$ and $B$ are two labeled state machines with the same
input and output labels. \\
Then we say that $A$ {\em implements\/} $B$ provided that 
every trace of $A$ is a trace of $B$. \\
\\
This says that anything that $A$ can do, $B$ can also do. \\
I'm not paying any attention to liveness (eventuality) here---just
correct results. \\
\\
The following three examples illustrate some of the many different
uses of this notion of implementation: \\
\\
GCD example:  Algorithm optimization. \\
\\
Relation example:  Abstract data types and their implementations. \\
\\
Cache example: Concurrent program implementing a global service
specification.

\subsection{GCD example}

This example illustrates an implementation relationship between two
algorithms, where one is an ``optimized'' (improved) version of the
other. \\
\\
Let $A$ be a labeled state machine for calculating GCD using remainder
on integer division, which I described roughly earlier. \\
Let $B$ be the machine for GCD using subtraction. \\
\\
They both compute the GCD. \\
More specifically: \\
If exactly one request happens, and if there is a return value,
then it is the correct GCD. \\
\\
One way to prove this is to break up the job into two pieces: \\
\\
1. Showing that $B$ correctly computes the GCD: \\
\\
We essentially already did that, using invariants. \\
Actually, we showed the invariants for the earlier version of the
algorithm without the labels, but the slightly different handling of
the input and output doesn't change anything important. \\
\\
2. Showing that $A$ implements $B$, that is, every trace of $A$ is a
trace of $B$. \\
\\
How? \\
\\
Consider any execution of $A$, and try to construct an execution
of $B$ having the same trace. \\
\\
So, fix an execution of $A$. \\
It consists of: \\
An input step, \\
then a series of steps, each of which moves from $(x,y)$ to a new pair,
consisting of the smaller of $x$ and $y$ and the remainder when the
larger is divided by the smaller, \\
then an output step. \\
\\
Now construct an execution of $B$ that ``mimics'' the given
execution of $A$: \\
Replace each division step by a series of subtraction steps, ending up
with the same new pair (in one order or the other). \\
Technicality: when the division-based algorithm winds up with a
remainder of $0$, the subtraction-based algorithm should stop one
short, with the two components equal (the two algorithms have
different stopping conditions): \\
\\
The idea is simply that the remainder can be found by repeated
subtraction. \\
\\
Example: $\ms{GCD}(184,30)$. \\
\\
The division algorithm $A$ moves from
$(188,30)$ directly to $(30,8)$, then to $(8,6)$, then $(6,2)$, then
$(2,0)$, then returns the $2$ \\
(stops when the remainder is $0$). \\
Replace the first division step by six subtraction steps,
$(188,30)$ to $(158,30)$ to $(128,30)$ to $(98,30)$ to $(68,30)$ to
$(38,30)$ to $(8,30)$. \\
Then replace the second division step by three subtraction steps,
$(8,30)$ to $(8,22)$ to $(8,14)$ to $(8,6)$. \\
Replace the third division step by one subtraction step, $(8,6)$ to
$(2,6)$. \\
Replace the final division step by two subtraction steps (it stops
short, because of the different stopping condition): \\
$(2,6)$ to $(2,4)$ to $(2,2)$. \\
Then return $2$. \\
\\
\\
Formally, this gives a recursive construction of an execution of $B$,
working step-by-step in the $A$ execution. \\
Can prove by induction on number of steps in the $A$ execution that
the traces are the same. \\
\\
\\
Theorem: \\
The remainder-based GCD labeled state machine implements the
subtraction-based GCD labeled state machine. 

\subsection{Relation example}

This example illustrates an implementation relationship between an
abstract object and a more detailed, ``lower-level'', implementation
object. \\
\\
Recall the relation reflexive transitive closure example. \\
Assume a basic set $S$ of size $n$. \\
To be definite, say that the set is exactly the set $\{ 1, \ldots, n
\}$. \\
\\
Let $B$ be the relation labeled state machine I described earlier,
with operations for adding and removing pairs, and determining if a
pair is in the relation, or is in the relation's reflexive, transitive
closure. \\
Its basic data values are relations, and the steps of the state
machine involve performing high-level relational operations like taking
closures. \\
\\
Now, an actual implementation of this could be written in
terms of a lower-level representation of the relation, e.g., as an
adjacency matrix. \\
\\
The tests would involve looking up particular elements in the
matrix. \\
\\
The transitive closure operation would involve successive squaring of
matrices, using matrix multiplication. \\
(Boolean matrix multiplication, with ands and ors instead of times and
plus.) \\
\\
Let $A$ be a labelled state machine of this kind. \\
The state consists of matrix representations of the relation and
of its reflexive, transitive closure. \\
Plus some bookkeeping information to keep track of what operations are
in progress. \\
\\
Details: \\
$Q$: \\
$\ms{mat}$, an $n \times n$ matrix \\
$\ms{rtc-mat}$,  an $n \times n$ matrix \\
$\ms{reqtype}, \ms{reqpair}, \ms{retval}$ exactly as before. \\
\\
$Q_0$: \\
$\ms{mat}$, $\ms{rtc-mat}$ initially contain no pairs (empty relation). \\
$\ms{reqtype}$, $\ms{reqpair}$, $\ms{retval}$ as before (null) \\
\\
$L$: \\
Exactly as before. \\
\\
$\delta$: \\
\begin{tabbing}
XXX\=XXX\= \kill
$\ms{request-add}(a,b)$ \\
$\ms{request-remove}(a,b)$ \\
$\ms{request-in-relation?}(a,b)$ \\
$\ms{request-in-closure?}(a,b)$ \\
\\
Exactly as before---just record the inputs. \\
\\
$\ms{compute-add}$ \\
Can occur if $\ms{reqtype} = ``add''$ and $\ms{retval} = \ms{null}$ \\
Effect: \\
$\ms{mat}(\ms{reqpair}) := 1$ \\
$\ms{rtc-mat} := I_n + \ms{mat} + \ms{mat}^2 + \cdots + \ms{mat}^n$ \\
$\ms{retval} := ``OK''$ \\
\\
$\ms{compute-remove}$ \\
Can occur if $\ms{reqtype} = ``remove''$ and $\ms{retval} = \ms{null}$ \\
Effect: \\
$\ms{mat}(\ms{reqpair}) := 0$ \\
$\ms{rtc-mat} := I_n + \ms{mat} + \ms{mat}^2 + \cdots + \ms{mat}^n$ \\
$\ms{retval} := ``OK''$ \\
\\
$\ms{compute-in-relation?}$ \\
Can occur if $\ms{reqtype} = ``in-relation''$ and $\ms{retval} = \ms{null}$ \\
Effect: \\
if $\ms{mat}(\ms{reqpair}) = 1 then $\ms{retval} := \ms{true}$ \\
else $\ms{retval} := \ms{false}$ \\
\\
$\ms{compute-in-closure?}$ \\
Can occur if $\ms{reqtype} = ``in-closure''$ and $\ms{retval} = \ms{null}$ \\
Effect: \\
if $\ms{rtc-mat}(\ms{reqpair}) = 1 then $\ms{retval} := \ms{true}$ \\
else $\ms{retval} := \ms{false}$ \\
\\
$\ms{return}(``OK'')$ \\
$\ms{return}(b)$, $b \in Boolean$ \\
Exactly as before.
\end{tabbing}
Now, assume we already (think we) understand $B$, and believe that its
external behavior is correct. \\
To convince ourselves that $A$'s external behavior is correct, we have
only to show that every trace of $A$ is also a trace of $B$. \\
\\
How? \\
\\
Given an execution of $A$, try to construct an execution of $B$ with
the same trace. \\
Just replace the matrix ops by the corresponding relational ops they
represent. \\
E.g., if a step sets an element of the matrix to $1$ in the $A$
execution, then define a corresponding step in a $B$ execution that
adds the corresponding pair to the relation represented by the matrix. \\
\\
Theorem: \\
The matrix-based labeled state machine implements the
abstract relation-based labeled state machine. \\
\\
\\
Alternative implementation: \\
\\
Let $A$ be a labeled state machine in which the relation and its RTC
are represented by adjacency lists.  \\
Do the operations in terms of the adjacency lists. \\
Again, every trace of $A$ is a trace of $B$ (the relation-based machine). \\
That is, $A$ implements $B$.

\subsection{Caching example}

\subsubsection{Strong vs. Weak Caching}

Now let $A$ be the strong caching system, and $B$ the weak caching system. \\
It should be obvious that $A$ implements $B$. \\
Why? \\
\\
Start with an execution of $A$, construct a execution of $B$ with the
same trace. \\
\\
Not hard---the execution of $A$ is essentially doing a special case of
what is allowed by $B$. \\
So use (essentially) the same execution in $B$. \\
The only difference: \\
In $A$, a write to memory automatically cleans out both caches. \\
In the $B$ execution, this doesn't happen. \\
But, we can let the $A$ compute-write step correspond to a combination
of several steps in $B$: compute-write, drop, drop. \\
\\
Theorem: \\
The strong caching system implements the weak caching system. \\
\\
The other direction doesn't work: \\
Some executions of the weak caching system don't have any executions
of the strong caching system with the same trace. \\
We saw an example above. \\
\\
Theorem: \\
The weak caching system does not implement the strong caching system.

\subsubsection{Strong caching vs. memory only}

Can say something more about a strong caching system, though: \\
\\
It looks, from the outside, just like a single centralized memory,
without any caches. \\
\\
A little more precisely, let $A$ be the strong caching system, as
before. \\
Let $B$ be a ``centralized memory machine'': \\
A state machine whose state contains: \\
-- $\ms{memory}$, mapping addresses to values as before. \\
-- Bookkeeping info to remember active operations. \\
A read or a write, at either location, arrives via an input step, gets
performed on the central memory, and gets a response via an output
step. \\
\\
Details: 
\begin{tabbing}
XXX\=XXX\= \kill
$Q$: \\
$\ms{memory}$, a function from $\ms{addresses}$ to $\ms{values}$ \\
\\
$\ms{req1} \in \{ ``read'', ``write'' \} \cup \{ \ms{null} \}$ \\
$\ms{reqaddress1} \in \ms{addresses} \cup \{ \ms{null} \}$ \\
$\ms{reqval1} \in \ms{values} \cup \{ \ms{null} \}$ \\
$\ms{retval1} \in \ms{values} \cup \{ ``OK'', \ms{null} \}$ \\
\\
$\ms{req2} \in \{ ``read'', ``write'' \} \cup \{ \ms{null} \}$ \\
$\ms{reqaddress2} \in \ms{addresses} \cup \{ \ms{null} \}$ \\
$\ms{reqval2} \in \ms{values} \cup \{ \ms{null} \}$ \\
$\ms{retval2} \in \ms{values} \cup \{ ``OK'', \ms{null} \}$ \\
\\
$Q_0$: \\
$\ms{memory}$ arbitrary, bookkeeping components all $\ms{null}$ \\
\\
$L$: \\
\\
Input, Output: Exactly as for (weak or strong) caching system. \\
\\
Internal: \\
$\ms{compute-read}(x,i)$, $x \in \ms{addresses}$, $i \in \{ 1, 2 \}$ \\
$\ms{compute-write}(x,v,i)$, $x \in \ms{addresses}$, $v \in \ms{values}$, $i \in \{ 1, 2 \}$ \\
\\
Transitions: \\
\\
Input, Output: Exactly as for caching system. \\
\\
Internal: \\
$\ms{compute-read}(x,1)$ \\
Can occur when $\ms{req1} = ``read''$, $\ms{reqaddress1} = x$, and
$\ms{retval1} = \ms{null}$ \\
Effect: \\
$\ms{retval1} := \ms{memory}(x)$ \\
\\
$\ms{compute-write}(x,v,1)$ \\
Can occur when $\ms{req1} = ``write''$, $\ms{reqaddress1} = x$,
$\ms{reqval1} = v$, and $\ms{retval1} = \ms{null}$ \\
Effect: \\
$\ms{memory}(x) := v$ \\
$\ms{retval1} := ``OK''$ \\
\\
And same for client 2.
\end{tabbing}

Theorem: \\
The strong caching system state machine implements the centralized
memory state machine. \\
\\
Proof: \\
Start with an execution of the strong caching system. \\
Construct a corresponding execution of the centralized memory state
machine, step-by-step. \\
Inputs and outputs match up one-for-one. \\
When a read is performed in the caching system, $A$, it's done on the local
cache.  \\
However, since it's a strong caching system, the same value is in the
memory. \\
Let the read in $B$ get performed from the memory. \\
\\
When a write happens in $A$, it happens to the central memory. \\
Corresponding write in $B$ is also to the central memory. \\
\\
Can prove using induction on the number of steps in the execution of
$A$ (strong caching system) that the same external labels appear. \\
\\
A key part of that proof is observing a relationship between the
states of the two algorithms after any number of steps of $A$ (and
corresponding steps of $B$): \\
The memory in $B$ is equal to the memory in $A$. \\
\\
Think of this as an invariant involving the states of the two state
machines.

\section{Abstraction functions and abstraction relations}

\subsection{Overview}

Proofs that one state machine $A$ implements another state machine $B$
are often carried out in a very stylized way, in terms of individual
steps rather than entire executions. \\
\\
Like the way we proved invariants by showing something about the
initial state and about each step. \\
\\
I'll just give you the basic definition and theorem, but we won't have
time in this course to do much with it. 

\subsection{Definition and theorem}

This definition describes a correspondence (formally, a relation)
between the states of $A$ and $B$, which is preserved while the two
machines execute. \\
\\
\\
Definition: \\
\\
Suppose $A$ and $B$ are labeled state machines with the same input
and output labels. \\
\\
A relation $R$ from states of $A$ to states of $B$ is called an {\em
abstraction relation\/} from $A$ to $B$ provided that the following
conditions hold: \\
\\
(1) (Start condition) \\
If $q_A$ is a start state of $A$, then there exists
a start state $q_B$ of $B$ such that $(q_A,q_B) \in R$. \\
\\
(2) (Step condition) \\
If $q_A$ and $q_B$ are reachable states of $A$ and $B$, respectively,
$(q_A,q_B) \in R$, and $(q_A,\ell,q'_A)$ is a step of $A$, 
then there is a sequence of ($0$ or more) steps of $B$ that: \\
-- starts with $q_B$, \\
-- has the same trace as the step of $A$, and \\
-- ends with some state $q'_B$ such that $(q'_A,q'_B) \in R$. \\
\\
\\
The importance of this slightly complicated-looking definition
is that having an abstraction relation from $A$ to $B$ implies that
$A$ implements $B$: \\
\\
\\
Theorem: \\
If there is an abstraction relation from $A$ to $B$, then $A$
implements $B$. \\
\\
Proof:
The two conditions in the definition of an abstraction function
provides a method of constructing an execution of $B$ for any given
execution of $A$. \\
First, it shows how to get started (pick a start state of $B$ that is
related to the start state of $A$). \\
Then it shows how to construct successive steps of $B$ to correspond
to the successive steps of $A$. \\
\\
Construct this recursively on the structure of the execution of
$A$. \\
Then prove by structural induction that the traces of any prefix of the
execution of $A$ and of the corresponding part of the execution of $B$
are the same. \\
Follows that the traces for the entire executions are the same. \\
\\
%[[[Needs some kind of theorem about limits of chains in p.o.s here?
%Naive workaround?  We haven't talked about infinite sequences yet...
%Suggest that next time, we put the stuff about infinities earlier in
%the course.]]]
\\
Note:  The fact that we assume reachable states in (2) means that we
can use any invariants we have previously proved for $A$ and $B$ in
showing that the step correspondence holds. 

\subsection{Examples}

The three implementation examples I presented in the previous section
can all be shown using abstraction relations. \\
\\
Example: \\
Show strong cache implements centralized memory. \\
\\
The key is to define the right relation $R$ between the states of the two
machines: \\
\\
High-level memory = low-level memory. \\
Bookkeeping corresponds (same types of ops are active in both). \\
\\
That is (using CM for centralized memory and SCS for strong caching
system): \\
\\
$\ms{CM}.\ms{memory} = \ms{SCS}.\ms{memory}$ \\
$\ms{CM}.\ms{req1} = \ms{SCS}.\ms{req1}$ \\
$\ms{CM}.\ms{reqaddress1} = \ms{SCS}.\ms{reqaddress1}$ \\
$\ms{CM}.\ms{reqval1} = \ms{SCS}.\ms{reqval1}$ \\
$\ms{CM}.\ms{retval1} = \ms{SCS}.\ms{retval1}$ \\
\\
and same for client 2. 
\end{tabbing}
With this, the two conditions are easy to show: \\
\\
1. Start condition: \\
Whatever memory contents appear in the given start state of $A$ should
also be used in the chosen start state of $B$. \\
\\
2. Step condition: \\
\\
Proof by cases, based on all the kinds of steps of $A$. \\
For each kind, need to say what the corresponding steps of $B$ are (as we
discussed earlier). \\
And must check the stated conditions.  \\
\\
\\
Key steps:  The internal ones---the inputs and outputs correspond in a
straightforward way. \\
\\
\\
$\ms{compute-read}$:  Corresponds to the same-label step in $B$. \\
\\
The key argument is that the same value is written into the $\ms{retval}$
state components in both the step of $A$ and the corresponding step of
$B$. \\
This is needed in order to show that the states correspond after the step. \\
\\
This is not obvious, because in $A$, the value comes from the
cache, whereas in $B$, it comes from the memory. \\
\\
However, the memory in $B$ is the same as that in $A$ because of the
assumed correspondence before the step. \\
And the cache value is the same as the memory value in $A$ because of
an invariant we proved for the strong caching system. \\
So that means the values that are obtained in $A$ and $B$ are the
same. \\
\\
$\ms{compute-write}$:  Corresponds to the same-label step in $B$. \\
\\
The key argument is that, assuming $x$ is the address being written,
the new value of $x$ in the memory in $B$ is the same as that in $A$. \\
Again, that is needed in order to show that the states correspond
after the step. \\
\\
That is pretty obvious, though, because both steps modify the memory
directly. \\
\\
$\ms{copy}$, $\ms{drop}$: Correspond to no steps happening in $B$. \\
\\
The key argument is that the new state in $A$ still corresponds to the
same state of $B$. \\
But that's easy to see, because neither of these steps change the
memory values in $A$, only the cache values. \\
\\
\\
In this example, the relation is actually a function, from the
implementation state to the specification state. \\
That's not always the case. \\
E.g., for the GCD correspondence, will want to allow the two numbers
to appear in either order. \\
\\
This abstraction method is very important. \\
More details on this method, and many examples, appear in later
courses: \\
6.170: Data abstraction. \\
Distributed algorithms. \\
Principles of computer systems. \\
\\
And this way of thinking is also useful in other courses about systems.

\section{Composition}

The rest of the story is composition: \\
building bigger systems out of smaller objects and services. \\
E.g., a complicated distributed system out of communication services,
database services, fault-detectors,...\\
\\
Again, the labels provide the right support. \\
Basically, combine labeled state machines by matching up output labels in one
component with input labels in other components.  \\
Builds a bigger labeled state machine. \\
\\
Again, leave this for later courses, e.g. distributed algorithms. 
\end{document}





