\documentclass[11pt]{article}
\usepackage{ifthen}
\usepackage{alltt}
% \usepackage{indentfirst}
\usepackage{latexsym}
\usepackage[pdftex]{graphicx}
\usepackage{gdefs}
% \usepackage{logdef}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{setspace}
\usepackage{rotating}
\usepackage{longtable}
\usepackage{hhline}
\usepackage{refs}
\usepackage{graph}
\usepackage{changebar}
\usepackage{array}
\usepackage{wrapfig}
\usepackage[small,compact]{titlesec}
\usepackage {tikz}
\usepackage{spreadtab}
\usetikzlibrary{positioning}
\usepackage{listings}
\usepackage{tweaklist}
% \usepackage{ulem}

% this sets the spacings in the itemize environment
\renewcommand{\itemhook}{\setlength{\topsep}{0pt}%
  \setlength{\itemsep}{0pt}%
  \setlength{\parsep}{0ex}%
}

% this sets the spacings in the enumerate environment
\renewcommand{\enumhook}{\setlength{\topsep}{0pt}%
  \setlength{\itemsep}{0pt}%
  \setlength{\parsep}{0ex}%
}

% this sets the spacings in the description environment
\renewcommand{\descripthook}{\setlength{\topsep}{0pt}%
  \setlength{\itemsep}{0pt}%
  \setlength{\parsep}{0ex}%
}

\setlength{\topmargin}{0.0in}
\setlength{\headheight}{0.0in}
\setlength{\headsep}{0.0in}
\setlength{\textwidth}{6.5in}
\setlength{\oddsidemargin}{0.0in}
\setlength{\evensidemargin}{0.0in}
\setlength{\textheight}{9.0in}

\setcounter{topnumber}{5}
\def\topfraction{0.99}
\def\textfraction{0.05}
\def\floatpagefraction{0.9}
\setcounter{bottomnumber}{5}
\def\bottomfraction{0.99}
\setcounter{totalnumber}{10}
\def\dbltopfraction{0.99}
\def\dblfloatpagefraction{0.8}
\setcounter{dbltopnumber}{5}

\setlength{\marginparwidth}{.95in}
\let\oldmarginpar\marginpar
\renewcommand\marginpar[1]{\-\oldmarginpar[\raggedleft\footnotesize #1]%
{\raggedright\footnotesize #1}}

\let\oldthebibliography=\thebibliography
  \let\endoldthebibliography=\endthebibliography
  \renewenvironment{thebibliography}[1]{%
    \begin{oldthebibliography}{#1}%
      \setlength{\parskip}{0ex}%
      \setlength{\itemsep}{0ex}%
  }%
  {%
    \end{oldthebibliography}%
  }

\newcommand{\SSSD}{\textit{SSSD}}
\newcommand{\ESSSD}{\textit{ESSSD}}
\newcommand{\CS}{\textit{CS}}
\newcommand{\PT}{\textit{PT}}
\DeclareMathOperator{\ulambda}{\underline{\lambda}}
\DeclareMathOperator{\op}{\textrm{op}}
\DeclareMathOperator{\extend}{\otimes}
\DeclareMathOperator{\combine}{\oplus}
\DeclareMathOperator*{\bigcombine}{\bigoplus}
\DeclareMathOperator*{\Min}{\textrm{\large{Min}}}
\newcommand{\bbN}{\mathbb{N}}
\newcommand{\bbZ}{\mathbb{Z}}
\newcommand{\len}{\textrm{len}}
\newcommand{\Edges}{\textrm{Edges}}
\newcommand{\Paths}{\textrm{Paths}}
\newcommand{\Store}{\textrm{Store}}
\newcommand{\Var}{\textrm{Var}}

\title{Static Analysis as a Path Problem \\
       CS701 \\
}

\author{Thomas Reps \\ \small[Based on notes taken by Matt Amodio on October 8, 2015]}
\date{}

\begin{document}           % End of preamble and beginning of text.

\maketitle

\begin{abstract}
This lecture introduces the notion of static-analysis
problems as \emph{path problems}.
Rather than introducing the concept of a path problem
at the same time as we introduce program-analysis notions,
we start by discussing the shortest-distance problem.
\end{abstract}
	
\section{Shortest-Distance Problem}

The shortest-distance problem\footnote{
  Sometimes called the ``shortest-path problem.''
}
is a well-studied problem in graph theory in which we try to find
the shortest distance from a particular node $s$ to every other node.
Technically, this is called the \emph{single-source shortest-distance}
problem ($\SSSD$).
$\SSSD$ can be formulated in different ways, and there are different
algorithms for solving it.
We will give two formulations that are analogous to the formulations
that we will use for dataflow-analysis problems.
You should bear in mind that not all characteristics of the shortest-distance
problem carry over to dataflow-analysis problems;
in particular, the most efficient algorithms for the shortest-distance
problem are different from the algorithms we will use for dataflow analysis.

First, let us consider an example
\[
  \SSSD(s,v)~\textrm{with}~v \in \{s,t,a,b,c\}
\]
where all edge lengths are non-negative, and the graph is
as shown below:
	
	\begin{center}
	\begin {tikzpicture} [node distance=3cm,->]	
	\node (A) {$a$};
	\node (B) [above of = A] {$b$};
	\node (C) [below of = A] {$c$};
	\node (T) [right  of = A] {$t$};
	\node (S) [left of = A] {$s$};
	
	\path (S) edge node [below=.2cm] {$8$} (C);
	\path (S) edge node [below=.2cm] {$7$} (A);
	\path (S) edge node [below=.2cm] {$3$} (B);
	\path (C) edge node [right=.2cm] {$6$} (T);
	\path (A) edge node [below=.2cm] {$8$} (T);
	\path (A) edge node [right=.2cm] {$2$} (C);
	\path (B) edge node [below=.2cm] {$11$} (T);
	\path (T) edge [bend right=45] node [below=.2cm] {$2$} (B);
	\path (A) edge [bend left=25] node [left=.05cm] {$1$} (B);
	\path (B) edge [bend left=25] node [right=.05cm] {$2$} (A);
	\end{tikzpicture}
	\end{center}
	
\noindent
In this example, we can manually figure out that
\begin{eqnarray*}
  \SSSD(s,s) & = & 0 \\
  \SSSD(s,b) & = & 3 \\
  \SSSD(s,a) & = & 5 \\
  \SSSD(s,c) & = & 7 \\
  \SSSD(s,t) & = & 13
\end{eqnarray*}


\section{Similarities/Differences Between Dataflow-Analysis Problems and Path Problems}

\subsection{Similarities}

\begin{itemize}
  \item Edge ``weights'' (lengths in $\SSSD$; actions/transformers in dataflow analysis)
  \item Multiple paths to a given node
  \item Solvable by worklist algorithm
\end{itemize}

\subsection{Differences}

\begin{enumerate}
  \item
    \label{It:WitnessPredecessor}
    In an $\SSSD$ problem, the reason that a node $n$ has a particular
    distance can always be justified by the distance given to one of $n$'s
    predecessors $m$ and the length of the edge $(m,n)$.

    \hspace*{1.5ex}In contrast, in a dataflow-analysis problem,
    the justification for node $n$'s value may require looking
    at the value of \emph{several} of $n$'s predecessors,
    $m_1, \ldots, m_k$ and the actions on the edges $(m_1,n)$
    $\ldots$, $(m_k,n)$.
  \item
    \label{It:ShortestPathWitness}
    In an $\SSSD$ problem, we can always identify at least one shortest
    path (although there may be ties).
    (A shortest-path can be read off by identifying a
    witness predecessor \`{a} la \itemref{WitnessPredecessor},
    a witness predecessor of the witness predecessor, and so on.)

    \hspace*{1.5ex}In contrast, because in a dataflow-analysis problem
    a node may have a \emph{set} of witness predecessors, the
    generalized structure that serves in place of a witness \emph{path}
    has the structure of an \emph{and-or graph}
    \cite[\S3.1.4 and \S3.2.1]{SCP:RSJM05}.
  \item
    In an $\SSSD$ problem, an algorithm need only ``consider'' a node once.

    \hspace*{1.5ex}
    Let me elaborate on what I mean by this observation.
    When all edge lengths are non-negative, Dijkstra's algorithm can
    be applied \cite{NM:Dijkstra59}.
    That algorithm may assign a \emph{candidate distance} to a
    node multiple times, but that is not what I mean by ``considering''
    a node.
    The one time a node $n$ is ``considered'' is when $n$ is
    the node selected from the worklist as one of the
    nodes with the shortest distance among the nodes that have
    yet to be ``considered.''

    \hspace*{1.5ex}
    In contrast, in worklist-based algorithms used in dataflow analysis,
    a given node $n$ may be selected from the worklist---i.e.,
    ``considered''---multiple times.
  \item
    In an $\SSSD$ problem with non-negative edge lengths, cycles
    do not matter (in the sense that paths that go through cycles
    are never the path that serves as the witness of a shortest
    distance from $s$ to $n$).
\end{enumerate}


\section{Alternative Ways of Formulating a Path Problem}

\subsection{Declarative Formulation of the Desired Answer}

\subsubsection*{Distance Along a Path}

	\begin{center}
	\begin {tikzpicture} [node distance=2cm,->]
	\node (A) {$s$};
	\node (B) [right of = A] {$\op$};
	\node (C) [right of = B] {$\op$};
	\node (D) [right of = C] {$t$};
	
	\path (A) edge node [above=.05cm] {$l_1$} (B);
	\path (B) edge node [above=.05cm] {$l_2$} (C);
	\path (C) edge node [above=.05cm] {$l_3$} (D);
	\end{tikzpicture}
	\end{center}

We think of ``op'' in the diagram above as an operator placed between
$l_1$ and $l_2$ and between $l_2$ and $l_3$, giving the expression
$l_1 \op l_2 \op l_3$.
The operator ``$\op$'' will be referred to generically as ``extend'',
and denoted by $\extend$.
In the $\SSSD$ problem, $\extend$ is addition ($+$), which happens to be
a commutative operation.
(In contrast, in dataflow-analysis problems, $\extend$ will typically
be non-commutative.)

 Note that
\begin{itemize}
  \item
    Each path from $s$ to $n$ has a finite number of edges
    (i.e., each path is of finite length).
  \item
    The number of paths from $s$ to $n$ can be infinite due to cycles.
\end{itemize}

\subsubsection*{Combining Distances from Two or More Paths}

Consider the following situation:

	\begin{center}
	\begin {tikzpicture} [node distance=2cm,->]
	\node (A) {$s$};
	\node (B) [above right of = A] {};
	\node (C) [right of = B] {};
	\node (D) [below right of = A] {};
	\node (E) [right of = D] {};
	\node (F) [above right of = E] {$\op$ $t$};
	
	\path (A) edge [bend left=25] node [above=.05cm] {$l_1$} (B);
	\path (B) edge node [above=.05cm] {$l_2$} (C);
	\path (C) edge node [above=.05cm] {$l_3$} (F);
	
	\path (A) edge [bend right=25] node [above=.05cm] {$l_5$} (D);
	\path (D) edge node [above=.05cm] {$l_4$} (E);
	\path (E) edge node [above=.05cm] {$l_6$} (F);
	\end{tikzpicture}
	\end{center}

\noindent
The operator labeled $\op$ in the diagram above will be referred to
generically as \emph{combine}, and denoted by $\combine$.
In the $\SSSD$ problem, $\combine$ is $\min$.
$\min$ is commutative, which is also the case with $\combine$ in
dataflow-analysis problems.

We can specify the solution to the shortest-distance problem as follows:

Let $l: \Edges \rightarrow \bbN$ be the assignment of
non-negative lengths to edges.
Let $p$ be a path $e_1, e_2, \ldots, e_k$.
The \emph{length} of $p$ is defined as follows:
\[
  \len(p) \eqdef l(e_1) + l(e_2) + ... + l(e_k).
\]
Then
\[
  \SSSD(s,t) = \Min_{p \in \Paths(s,t)} \len(p).
\]
Using our generic notation, the \emph{length} of $p$ is
defined as follows:
\[
  \len(p) \eqdef l(e_1) \extend l(e_2) \extend ... \extend l(e_k),
\]
and
\begin{equation}
  \label{Eq:SSSDGenericNotation}
  \SSSD(s,t) = \bigoplus_{p \in \Paths(s,t)} \len(p).
\end{equation}
This formulation really just serves as a specification of the problem.
Its outlook is global, because is specifies the answer in terms of
the global concepts of the set of all paths in $Paths(s,t)$.
We will call the node values specified by this formulation of the
problem the \emph{combine-over-all-paths solution}.

\subsection{Equational Formulation}

In the \emph{equational formulation} of a path problem, 
we adopt a local view.
For a given node $n$, we look at all of its predecessors
$m_1, \ldots, m_k$.

	\begin{center}
	\begin{tikzpicture} [node distance=3cm,->]
	\node (A) {$s$};
	\node (B) [above right=1.5cm and 5cm of A] {$m_1$};
	\node (B2)[above right=0.8cm and 5cm of A] {$m_2$};
	\node (Dot1)[below right=-0.6cm and 5.15cm of A] {$\cdot$};
	\node (Dot2)[below right=-0.2cm and 5.15cm of A] {$\cdot$};
	\node (Dot3)[below right=0.2cm and 5.15cm of A] {$\cdot$};
	\node (C) [below right=1.5cm and 5cm of A] {$m_k$};
	\node (D) [below right of = B] {$n$};
	
	\path (A) edge[dotted, thick] node [above=.3cm] {$\ESSSD(m_1)$} (B);
	\path (A) edge[dotted, thick] node [below=.1cm] {$\ESSSD(m_2)$} (B2);
	\path (A) edge[dotted, thick] node [below=.3cm] {$\ESSSD(m_k)$} (C);
	\path (B) edge node [above=.2cm] {$l_{{m_1},n}$} (D);
	\path (B2) edge node[below=.0cm] {$l_{{m_2},n}$} (D);
	\path (C) edge node [above=.2cm] {$l_{{m_k},n}$} (D);
	
%	\path (B) edge [dotted, -] node [right=.05cm] {} (C);
	\end{tikzpicture}
	\end{center}
	
Each node $n$ of the graph gives rise to a variable $\ESSSD(n)$.
We can formulate the $\SSSD$ problem as follows:
\begin{eqnarray*}
  \ESSSD(s) & = & 0 \\
  \ESSSD(n) & = & \Min_{(m_i,n) \in \Edges} \ESSSD(m_i + l_{m_i,n})
\end{eqnarray*}
or, using our generic notation, as
\begin{eqnarray*}
  \ESSSD(s) & = & 0 \\
  \ESSSD(n) & = & \bigcombine_{(m_i,n) \in \Edges} \ESSSD(m_i \extend l_{m_i,n})
\end{eqnarray*}

\subsubsection*{Equational Formulation of  the  Example}

Returning to the running example, we have the following
set of equations:
\begin{eqnarray*}
  \ESSSD(s) & = & 0 \\
  \ESSSD(a) & = & \min (\ESSSD(s) + 7, \ESSSD(b) + 2) \\
  \ESSSD(b) & = & \min (\ESSSD(s) + 3, \ESSSD(a) + 1, \ESSSD(t) + 2) \\
  \ESSSD(c) & = & \min (\ESSSD(s) + 8, \ESSSD(a) + 2) \\
  \ESSSD(t) & = & \min (\ESSSD(a) + 8, \ESSSD(b) + 11, \ESSSD(c) + 6)
\end{eqnarray*}

We wish to solve the equations by a worklist algorithm
that performs a process of successive approximations.
Before we can get started, we have to decide how to
initialize the variables---i.e., what is the initial
approximation?
We will set $s \mapsto 0$ and everything else to $\infty$.
To motivate this choice, recall what we did for the
binding-time analysis phase of partial evaluation,
which was another worklist algorithm.
There, we set dynamic variables to $D$ and everything else to $S$.
In both situations, we are making the analogous choice:
we assume an extremal value, picking the extremum that
indicates the \emph{absence of influence}.
The value only changes from that extremal value when we
have proved that there could be an influence from the
initial node $s$.

In this case, iteration by iteration we obtain
	
\qquad\qquad\qquad	\begin{spreadtab}{{tabular}{r|r|r|r|r|r|r}}
	@   Iteration:   & 0 & 1 & 2 & 3 & 4 & \\
       \hline
	@ s   & 0 & 0 & 0  & 0 & 0 & \\
	@ a  & @ $\infty$  & 7 & 5  & 5 & 5  & \\
	@ b  & @ $\infty$   & 3 & 3  & 3 & 3  & \\
         @ c & @ $\infty$   & 8 & 7  & 7 & 7 & \\
         @ t & @ $\infty$ & @ $\infty$ & 14  & 13 & 13 &
	\end{spreadtab}
	
Once we have the same set of values on consecutive iterations,
the successive-approximation process has quiesced---we are done!

\subsection{Lattice Duality}

In the $\SSSD$ problem, the values were totally ordered.
There are two possibilities:
\begin{enumerate}
  \item
    Put 0 at the bottom and $\infty$.
    In between, working up from 0, we have 1, 2, $\ldots$.
  \item
    put $\infty$ at the bottom and 0 at the top.
    In between, working down from 0, we have 1, 2, $\ldots$.
\end{enumerate}

In dataflow analysis, we work with value spaces that
are \emph{partially ordered}.  An example of such a space
is the power set of $\{ a, b, c \}$, which is illustrated
below.

	\begin{center}
	\begin{tikzpicture}[node distance=2cm]
	\node (A) {$\{a, b, c\}$};
	\node (B)   [below left of =A] {$\{a, b\}$};
	\node (C)   [below of  =A, yshift=.6cm ]  {$\{a, c\}$};
	\node (D)   [below right of =A]  {$\{b, c\}$};
	\node (E) [below of =B] {$\{a\}$};
	\node (F) [below of =C] {$\{b\}$};
	\node (G) [below of =D] {$\{c\}$};
	\node (H) [below of =F] {$\emptyset$};
	
	\path (A) edge node {} (B);
	\path (A) edge node {} (C);
	\path (A) edge node {} (D);
	\path (B) edge node {} (E);
	\path (B) edge node {} (F);
	\path (C) edge node {} (E);
	\path (C) edge node {} (G);
	\path (D) edge node {} (F);
	\path (D) edge node {} (G);
	\path (E) edge node {} (H);
	\path (F) edge node {} (H);
	\path (G) edge node {} (H);
	\end{tikzpicture}
	\end{center}

Unfortunately, the dataflow-analysis literature and the
abstract-interpretation literature orient the partially ordered
sets in opposite ways.
(This difference is a vestige of the two subareas having been
developed mostly independently in an era in which scientific
communication was more difficult than it is now.)
In the dataflow-analysis community, problems are typically formulated
as ``meet-over-all-paths problems,'' while the order in the
abstract-interpretation community leads to ``join-over-all-paths problems.''

Fortunately, it does not really matter, except that you have
to orient yourself in different ways depending on which camp
the author of a paper you are reading is in.
All results obtainable in one orientation are obtainable
in the other orientation, thanks to the \emph{duality property
of lattices}.
Intuitively, just think of flipping the orientation of the
diagram above: $\cup$ becomes $\cap$, $\cap$ becomes $\cup$,
$\bot$ becomes $\top$, and $\top$ becomes $\bot$.
In general, if property $P$ holds in one orientation, then
in the other orientation the property that holds is
$P[\cup \leftarrow \cap, \cap \leftarrow \cup, \bot \leftarrow \top, \top \leftarrow \bot]$
(where $x \leftarrow y$ denotes the replacement of all instances
of $x$ by $y$.
For instance, if
\[
  a \cap (b \cup c) = (a \cap b) \cup (a \cap c)
\]
for one orientation, then
\[
  a \cup (b \cap c) = (a \cup b) \cap (a \cup c)
\]
holds for the other orientation.

\subsection{Dataflow Analysis}

Moving back to dataflow analysis, we must make a few changes.
In dataflow analysis, we do not always work with powerset lattices,
so instead of $\cup$ and $\cap$, we have the \emph{join} ($\sqcup$)
and \emph{meet} ($\sqcap$) operators.)
I will generally adopt the orientation used by the
abstract-interpretation community, so $\combine$ is $\sqcup$ instead
of $\min$.
For purposes of this example, I will use a powerset lattice,
so $\combine$ is $\cup$.
	
	\begin{center}
	\begin{tikzpicture}[node distance=2cm]
	\node (A) {$\{p\}$};
	\node (B) [below of=A] {$\{r\}$};
	\node (C) [right of =A, yshift=-1cm] {$\{p, r\}$};
	
	\path (A)  edge [->] node {} (C);
	\path (B)  edge [->] node {} (C);
	\end{tikzpicture}
	\end{center}

The $\extend$ operator is now (the reversal of) function composition,
instead of addition.
Consider the path depicted below:

	\begin{center}
	\begin{tikzpicture}[node distance=2cm]
	\node (A) {$\emptyset$};
	\node (B) [right of=A] {$.$};
	\node (C) [right of =B] {$.$};
	\node (D) [right of =C] {$.$};
	
	\path (A)  edge [->] node [above=.1cm] {$\lambda z. z \cup \{r\}$} (B);
	\path (B)  edge [->] node [above=.1cm] {$\lambda z. z \cup \{s\}$} (C);
	\path (C)  edge [->] node [above=.1cm] {$\lambda z. z \cup \{t\}$} (D);
	\end{tikzpicture}
	\end{center}
	
The result of applying the transformation represented by this path is
\[
  (\lambda z. z \cup \{t\})((\lambda z. z \cup \{s\})((\lambda z. z \cup \{r\})(\emptyset))) = \{ r, s, t \}
\]
Alternatively, we can consider the compound transformation performed
by the path, namely, the composition from right-to-left of the three functions:
\[
  (\lambda z. z \cup \{t\}) \composition (\lambda z. z \cup \{s\}) \composition (\lambda z. z \cup \{r\}) = \lambda z.z \cup \{ r, s, t \}.
\]

\subsection{An Example}

We will formulate a particular dataflow-analysis problem, namely,
``For each point $p$ in the program, what variables have a constant value
at $p$?''
First, we need to define some notation.

	\begin{tikzpicture}[node distance=2.5cm]
	\node[rectangle, draw] (A) {1: start};
	\node[rectangle, draw] (B) [below of =A] {2: a=1};
	\node[rectangle, draw] (C) [below of =B] {3: b=1};
	\node[rectangle, draw] (D) [below of =C] {4: a$<$3};
	\node[rectangle, draw] (E) [below of =D] {5: a=a+b};
	\node[rectangle, draw] (F) [right=4cm of D] {6: c=a+b};
	\node[rectangle, draw] (G) [right=4cm of F] {7: exit};
	
	\path (A) edge [->, thick] node [right =.5cm] {$\ulambda s.(???)$} (B);
	\path (B) edge [->, thick] node [right =.5cm] {$\ulambda s.[s\leftarrow1]$} (C);
	\path (C) edge [->, thick] node [right =.5cm] {$\ulambda s.[b\leftarrow1]$} (D);
	\path (D) edge [->, thick] node [right =.5cm] {T, $\ulambda s.s(a)<3?s\Box \bot$} (E);
	\path (D) edge [->, thick] node [above =.2cm]{F, $\ulambda s.s(a)>=3?s\Box \bot$} (F);
	\path (F) edge [->, thick] node [above =.2cm]{$\ulambda s.[c\leftarrow s(a)+s(b)]$} (G);
	\path (E) edge [->, bend left=90] node [left =.5cm] {$\ulambda s.[a\leftarrow s(a)+s(b)]$} (D);
	\end{tikzpicture}	
	

\paragraph{Some standard notation.}

\[
  \Store = (\Var \rightarrow \bbZ \cup \{?\})_\bot
\]
\[
  M: \Edges \rightarrow \textrm{store transformer}; \Store_\bot \rightarrow \Store_\bot
\]
\[
  M(<1,2>) = \lambda s.(?, ?, ?) = \lambda s.(a \mapsto ?, b \mapsto ?, c \mapsto ?)
\]
\[
  \ulambda s.e \quad \textrm{means} \quad \lambda s . s=\bot ? \bot \Box e
\]
\[
  s[a \leftarrow v] = \lambda k. k=a ? v \Box s(k)
\]


\paragraph{Collecting semantics.}

Let $M$ be the assignment of edges to actions.
Let $q = e_1, e_2, \ldots, e_k$ be a path from $s$ to $n$;
$q$'s \emph{path transformer} $\PT(q)$ is
\[
  M(e_k) \composition \ldots \composition M(e_2) \composition M(e_1).
\]
Then for each node $n$, the \emph{collecting semantics} at $n$ is the
set of $\Store$s defined as follows:
\begin{equation}
  \label{Eq:CollectingSemantics}
  \CS(n) = \{ \PT(q)(\sigma) \mid p \in \Paths(s,n), \sigma \in \Store \}
\end{equation}
The collecting semantics is already an abstraction, in the sense that
it has forgotten all information about the order in which stores
arise at $n$---or even whether two stores $\sigma_1$ and $\sigma_2$
can arise in the same run.

Note, however, that \eqref{CollectingSemantics} does not have
exactly the same form as \eqref{SSSDGenericNotation}.
We will address this issue next time.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
{\small
  \bibliography{cs701}
  \bibliographystyle{plain}
}

\end{document}
