\documentclass[11pt,twoside]{scrartcl}
%opening
\newcommand{\lecid}{15-414}
\newcommand{\leccourse}{Bug Catching: Automated Program Verification}
\newcommand{\lecdate}{} %e.g. {October 21, 2013}
\newcommand{\lecnum}{16}
\newcommand{\lectitle}{Going Temporal}
\newcommand{\lecturer}{Matt Fredrikson}
\usepackage{lecnotes}
\usepackage[irlabel]{bugcatch}
\usepackage{tikz}
\usetikzlibrary{automata,shapes,positioning,matrix,shapes.callouts,decorations.text}
\tikzset{onslide/.code args={<#1>#2}{%
\only<#1>{\pgfkeysalso{#2}} % \pgfkeysalso doesn't change the path
}}
\tikzset{
invisible/.style={opacity=0,text opacity=0},
visible on/.style={alt={#1{}{invisible}}},
alt/.code args={<#1>#2#3}{%
\alt<#1>{\pgfkeysalso{#2}}{\pgfkeysalso{#3}} % \pgfkeysalso doesn't change the path
},
}
% \traceget{v}{i}{\zeta} is the state of trace v at time \zeta of the i-th discrete step
\newcommand{\traceget}[3]{{#1}_{#2}(#3)}
\def\limbo{\mathrm{\Lambda}}
%% the last state of a trace
\DeclareMathOperator{\tlast}{last}
%% the first state of a trace
\DeclareMathOperator{\tfirst}{first}
\begin{document}
% the name of a trace
\newcommand{\atrace}{\sigma}%
%% the standard interpretation naming conventions
\newcommand{\stdI}{\dTLint[state=\omega]}%
\newcommand{\Ip}{\dTLint[trace=\atrace]}%
\def\I{\stdI}%
\maketitle
\thispagestyle{empty}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Introduction}
In the previous lecture, we observed a potential difference between whether a data structure invariant holds literally always at all times during all runs of all its operations (which is essentially a prerequisite for uncorrupted concurrent usages) compared to whether the data structure invariant merely holds at the end of each of the operations if it was true before.
On second thought, what we have seen so far was, from the very semantics, meant as the dynamic logic for proving properties of (all or some) final states of the program under complete ignorance of what happens in between.
There are ways of augmenting dynamic logics to temporal dynamic logics that provide explicit ways of proving formulas that are true, e.g., always throughout an execution.
While this works well and continues the deductive verification principles we saw so far, we will, instead, leverage the motivation of a temporal understanding of programs as a segway into studying temporal logics and their use in model checking.
\section{Traces of Programs}
If we want to study whether a formula such as a data structure invariant is true all the time always throughout the execution of a program, we will at least have to retain all the states that the program visits in its semantics.
One possible approach for that is to make the semantics remember the set of all traces that a program can exhibit where a trace is a sequence of states that the program visited along the way.
Another possible approach is to emphasize the structure that the transitions within the program took and then generate possible traces from that.
We will do the latter for no particular reason except that we would like to relate this as directly as possible to CTL.
While the technical nuances of the following definitions are somewhat subtle, the main point is quite intuitive.
With a little bit of care, we can retain all intermediate states during the execution of a program as an entire trace, instead of just retaining the fact that a particular final state was reachable from a particular initial state by running a program to completion.
A trace is either a finite sequence of states of some length $n\in\naturals$,
\begin{equation}
\label{eq:finite}
(\atrace_0,\atrace_1,\atrace_2,\dots,\atrace_n)
\end{equation}
or it is an infinite sequence of states, one for each natural number, of length $\infty$:
\[
(\atrace_0,\atrace_1,\atrace_2,\atrace_3,\dots)
\]
A trace terminates iff it is finite and its last state, $\atrace_n$ in Eq.~\ref{eq:finite}, is not the special failure state $\limbo$, which indicates abortion due to a failed test.
The special error state $\limbo$ does not provide values for any variables (so no formulas or terms can be evaluated in it), but it is used to mark the end of an aborted execution trace. No program ever continues from the error state $\limbo$.
For stylistic reasons, a trace of length $n$ has $n+1$ states.
\begin{definition}[Trace semantics of programs] \label{def:program-trace}
\newcommand{\ws}{\omega}\newcommand{\wt}{\nu}%
\renewcommand{\I}{\iconcat[state=\ws]{\stdI}}%
\renewcommand{\It}{\iconcat[state=\wt]{\stdI}}%
The \dfn[valuation!of~programs]{trace semantics,
$\iaccess[\alpha]{\I}$, of a program}~$\alpha$, is
the set of all its possible traces and is defined inductively as follows:
\index{_\tau(\alpha)_@$\tau(\alpha)$}%
\begin{enumerate}
\item
\m{\iaccess[\pupdate{\umod{x}{\astrm}}]{\I}}
=
\m{\{(\iget[state]{\I},\iget[state]{\It}) \with
\iget[state]{\It}=\iget[state]{\I}~\text{except that}~ \iget[state]{\It}(x)=\ivaluation{\I}{\astrm}}
for~\m{\ws\in\linterpretations{\Sigma}{V}\}}
\item \(\iaccess[\ptest{\ivr}]{\I} = \{(\ws) \with
\imodels{\I}{\ivr}\} \cup
\{(\ws,\limbo) \with
\inonmodels{\I}{\ivr}\}\)
\index{$\ptest{}$}
\item \(\iaccess[\pif{\ivr}{\alpha}{\beta}]{\I} =
\{\atrace \in \iaccess[\alpha]{\I} \with \atrace_0 \models \ivr\} \cup
\{\atrace \in \iaccess[\beta]{\I} \with \atrace_0 \nonmodels \ivr\}\)
\item \(\iaccess[{\alpha};{\beta}]{\I} =
\{\atrace \compose \varsigma \with \atrace\in\iaccess[\alpha]{\I} \mand \varsigma\in\iaccess[\beta]{\I}\}\);\\
the composition of~\m{\atrace=(\atrace_0,\atrace_1,\atrace_2,\dots)} and~\m{\varsigma=(\varsigma_0,\varsigma_1,\varsigma_2,\dots)} is
\[
\atrace \compose \varsigma \eqdef
\begin{cases}
(\atrace_0,\dots,\atrace_n,\varsigma_1,\varsigma_2,\dots) &\mylpmi[\text{if}~] \text{$\atrace$ terminates in $\atrace_n$}~\text{and}~\atrace_n=\varsigma_0\\
\atrace &\mylpmi[\text{if}~] \atrace~\text{does not terminate}\\
\text{not defined} &\text{otherwise}
\end{cases}
\]
\item
\(\iaccess[\pwhile{\ivr}{\alpha}]{\I}\)
=\(\{\atrace^{(0)} \compose \atrace^{(1)} \compose \dots \compose \atrace^{(n)} \with\)
for some $n\geq0$
such that for all $0\leq i=1pt,
node distance=1.5cm,
on grid,
auto,
/tikz/initial text={},
font=\footnotesize
]
%% Next
\node[state,initial,inner sep=1pt,minimum size=0.5cm] (q_0) {};
\node[state,inner sep=1pt,minimum size=0.5cm] (q_1) [right=of q_0] {};
\node[state,inner sep=1pt,minimum size=0.5cm] (q_2) [right=of q_1] {};
\node[state,inner sep=1pt,minimum size=0.5cm] (q_3) [right=of q_2] {};
\node[state,inner sep=1pt,minimum size=0.5cm] (q_4) [right=of q_3] {};
\node[state,inner sep=1pt,minimum size=0.5cm, draw=none] (q_5) [right=of q_4] {$\cdots$};
\path[->]
(q_0) edge (q_1)
(q_1) edge (q_2)
(q_2) edge (q_3)
(q_3) edge (q_4)
(q_4) edge (q_5);
\node [above of=q_0,yshift=-2.8em] {\scriptsize $\mathit{any}$};
\node [above of=q_1,yshift=-2.8em] {\scriptsize $F$};
\node [above of=q_2,yshift=-2.8em] {\scriptsize $\mathit{any}$};
\node [above of=q_3,yshift=-2.8em] {\scriptsize $\mathit{any}$};
\node [above of=q_4,yshift=-2.8em] {\scriptsize $\mathit{any}$};
\node [left of=q_0,xshift=-2em] {$\tnext{F}$};
\end{tikzpicture}
\begin{tikzpicture}
[
highlight/.style={draw=blue, text=blue},
shorten >=1pt,
node distance=1.5cm,
on grid,
auto,
/tikz/initial text={},
font=\footnotesize
]
%% Global
\node[state,initial,inner sep=1pt,minimum size=0.5cm] (q_0) {};
\node[state,inner sep=1pt,minimum size=0.5cm] (q_1) [right=of q_0] {};
\node[state,inner sep=1pt,minimum size=0.5cm,draw=none] (q_2) [right=of q_1] {$\cdots$};
\node[state,inner sep=1pt,minimum size=0.5cm] (q_3) [right=of q_2] {};
\node[state,inner sep=1pt,minimum size=0.5cm] (q_4) [right=of q_3] {};
\node[state,inner sep=1pt,minimum size=0.5cm, draw=none] (q_5) [right=of q_4] {$\cdots$};
\path[->]
(q_0) edge (q_1)
(q_1) edge (q_2)
(q_2) edge (q_3)
(q_3) edge (q_4)
(q_4) edge (q_5);
\node [above of=q_0,yshift=-2.8em] {\scriptsize $F$};
\node [above of=q_1,yshift=-2.8em] {\scriptsize $F$};
\node [above of=q_3,yshift=-2.8em] {\scriptsize $F$};
\node [above of=q_4,yshift=-2.8em] {\scriptsize $F$};
\node [left of=q_0,xshift=-2em] {$\tbox{F}$};
\end{tikzpicture}
\begin{tikzpicture}
[
highlight/.style={draw=blue, text=blue},
shorten >=1pt,
node distance=1.5cm,
on grid,
auto,
/tikz/initial text={},
font=\footnotesize
]
%% Future
\node[state,initial,inner sep=1pt,minimum size=0.5cm] (q_0) {};
\node[state,inner sep=1pt,minimum size=0.5cm] (q_1) [right=of q_0] {};
\node[state,inner sep=1pt,minimum size=0.5cm,draw=none] (q_2) [right=of q_1] {$\cdots$};
\node[state,inner sep=1pt,minimum size=0.5cm] (q_3) [right=of q_2] {};
\node[state,inner sep=1pt,minimum size=0.5cm] (q_4) [right=of q_3] {};
\node[state,inner sep=1pt,minimum size=0.5cm, draw=none] (q_5) [right=of q_4] {$\cdots$};
\path[->]
(q_0) edge (q_1)
(q_1) edge (q_2)
(q_2) edge (q_3)
(q_3) edge (q_4)
(q_4) edge (q_5);
\node [above of=q_0,yshift=-2.8em] {\scriptsize $\lnot F$};
\node [above of=q_1,yshift=-2.8em] {\scriptsize $\lnot F$};
\node [above of=q_3,yshift=-2.8em] {\scriptsize $F$};
\node [above of=q_4,yshift=-2.8em] {\scriptsize $\mathit{any}$};
\node [left of=q_0,xshift=-2em] {$\tdiamond{F}$};
\end{tikzpicture}
\begin{tikzpicture}
[
highlight/.style={draw=blue, text=blue},
shorten >=1pt,
node distance=1.5cm,
on grid,
auto,
/tikz/initial text={},
font=\footnotesize
]
%% Until
\node[state,initial,inner sep=1pt,minimum size=0.5cm] (q_0) {};
\node[state,inner sep=1pt,minimum size=0.5cm] (q_1) [right=of q_0] {};
\node[state,inner sep=1pt,minimum size=0.5cm,draw=none] (q_2) [right=of q_1] {$\cdots$};
\node[state,inner sep=1pt,minimum size=0.5cm] (q_3) [right=of q_2] {};
\node[state,inner sep=1pt,minimum size=0.5cm] (q_4) [right=of q_3] {};
\node[state,inner sep=1pt,minimum size=0.5cm, draw=none] (q_5) [right=of q_4] {$\cdots$};
\path[->]
(q_0) edge (q_1)
(q_1) edge (q_2)
(q_2) edge (q_3)
(q_3) edge (q_4)
(q_4) edge (q_5);
\node [above of=q_0,yshift=-2.8em] {\scriptsize $F \land \lnot G$};
\node [above of=q_1,yshift=-2.8em] {\scriptsize $F \land \lnot G$};
\node [above of=q_3,yshift=-2.8em] {\scriptsize $F \land \lnot G$};
\node [above of=q_4,yshift=-2.8em] {\scriptsize $G$};
\node [left of=q_0,xshift=-2em] {$\tuntil{F}{G}$};
\end{tikzpicture}
\end{center}
\caption{Examples of traces satisfying LTL formulas}
\end{figure}
Note that the meaning of the box and diamond modalities of LTL is quite analogous to the meaning that the box and diamond modalities already have in dynamic logic.
The only difference is that dynamic logic modalities range over the runs of a concrete program while the modalities of LTL range over time (along a fixed trace of something).
This is not a coincidence. Both are versions of modal logics, which differ in terms of what the box and diamond modalities range over but are otherwise built similarly.
For the cases \(\tnext{\ausfml},\tbox{\ausfml},\tdiamond{\ausfml},\tuntil{\ausfml}{\busfml}\) It is, of course, very important to retain the entire suffix of the trace for the semantics (not just a single state) in case the subformulas $\ausfml$ and $\busfml$ themselves mention further temporal operators.
For example, LTL formula
\[
\tbox{\tdiamond{\ausfml}}
\]
expresses that $\ausfml$ is true infinitely often when referring to an infinite trace.
On a finite trace, it merely means that $\ausfml$ is true in the last (non-failure) state.
The LTL formula
\[
\tdiamond{\tbox{\ausfml}}
\]
expresses that $\ausfml$ is eventually true all the time (so is true almost always, so except at finitely many exception states) when referring to an infinite trace.
On a finite trace, it also merely means that $\ausfml$ is true in the last (non-failure) state.
\section{LTL Formulas on Program Traces}
The following very clever program solves the issue of subtracting from negative numbers by first turning them into positive numbers and then adding, while ultimately flipping the sign again.
\begin{align*}
&\pupdate{\pumod{x}{-x}};\\
&\pupdate{\pumod{x}{x+7}};\\
&\pupdate{\pumod{x}{-x}};
\end{align*}
This program does correctly subtract 7 from a negative number as witnessed by a corresponding proof of the following dynamic logic formula:
\[
x=x_0 \limply \dbox{\pupdate{\pumod{x}{-x}}; \pupdate{\pumod{x}{x+7}}; \pupdate{\pumod{x}{-x}}}{\,x=x_0-7}
\]
This formula means that whenever $x_0$ equals the initial value of variable $x$ then \emph{after} running the program, the resulting value of $x$ will be the result of subtracting 7 from $x_0$, which, since it didn't change, still is the initial value of $x$.
We can write this as an LTL formula in terms of its traces. In particular, we can say that all traces $\sigma$ of the program satisfy the following LTL:
\[
\sigma \models x = x_0 \limply \tbox{\tdiamond{(x = x_0 - 7)}}
\]
Inspecting the semantics of this formula, it says that:
\begin{itemize}
\item If $\sigma \models x = x_0$, which is true if and only if $\sigma_0 \models x = x_0$,
\item then $\sigma \models \tbox{\tdiamond{(x = x_0 - 7)}}$. In other words, $\sigma^i \models \tdiamond{(x = x_0 - 7)}$ for all $i \ge 0$, so there always exists $j \ge i$ where $\sigma^j \models x = x_0 - 7$.
\end{itemize}
Because the traces of this program are finite, this imposes the same condition as the DL box modality, that the final state will satisfy $x = x_0 - 7$ when the initial state satisfies $x = x_0$.
The program also satisfies the property that if $x$ is initially negative then $x$ is finally negative:
\[
x<0 \limply \dbox{\pupdate{\pumod{x}{-x}}; \pupdate{\pumod{x}{x+7}}; \pupdate{\pumod{x}{-x}}}{\,x<0}
\]
But it \emph{does not} satisfy that $x$ is negative always at all times while running the program, because the whole point is that the first assignment flips the sign of $x$ into a positive number.
In fact, all traces of this program are of the following form:
\[
\iaccess[\pupdate{\pumod{x}{-x}}; \pupdate{\pumod{x}{x+7}}; \pupdate{\pumod{x}{-x}}]{\I}
= \{(\omega, \modif{\omega}{x}{-\omega(x)}, \modif{\omega}{x}{-\omega(x)+7}, \modif{\omega}{x}{-(\omega(x)+7)}) \with \omega ~\text{is any state}\}
\]
Consequently, if \(\sigma \in \iaccess[\pupdate{\pumod{x}{-x}}; \pupdate{\pumod{x}{x+7}}; \pupdate{\pumod{x}{-x}}]{\I}\) is a trace of this program starting in an initial state $\sigma_0$ with negative initial value of $x$ so $\sigma_0(x)<0$, then the LTL formula \(\tbox{(x<0)}\) is \emph{not} true for it even if it is true initially and in the end.
Indeed, all traces \(\sigma \in \iaccess[\pupdate{\pumod{x}{-x}}; \pupdate{\pumod{x}{x+7}}; \pupdate{\pumod{x}{-x}}]{\I}\) of the program satisfy:
\[
\sigma \nonmodels x<0 \limply \tbox{(x<0)}
\]
That is, the following condition is false for $\sigma$:
\begin{quote}
if $x<0$ is true (initially, because there's no temporal operator on the left hand side of the implication), then $x<0$ is true always in the future (of $\sigma$).
\end{quote}
But what is, indeed, true for all traces $\sigma$ of the program is:
\[
\sigma \models x<0 \limply \tbox{(x\neq0)}
\]
That is, if $x$ starts negative then it will always be nonzero at every point in time throughout the entire trace $\sigma$.
\section{Summary}
\begin{itemize}
\item A trace is either a finite sequence of states of a given length or an infinite sequence of states;
\item A trace terminates iff it is finite and its last state is not a failure state ($\limbo$);
\item The trace semantics of a program $\alpha$ ($\iaccess[\alpha]{\I}$) is the set of all its possible traces;
\item Linear temporal logic is defined by the following grammar:
\[
\ausfml,\busfml \bebecomes p \alternative \lnot\ausfml \alternative \ausfml\land\busfml \alternative \tnext{\ausfml} \alternative \tbox{\ausfml} \alternative \tdiamond{\ausfml} \alternative \tuntil{\ausfml}{\busfml}
\]
\begin{itemize}
\item The formula $\tbox{\ausfml}$ means that $\ausfml$ is always true in the future;
\item The formula $\tdiamond{\ausfml}$ means that $\ausfml$ is sometimes true in the future, meaning at least at one point;
\item The formula $\tnext{\ausfml}$ means that $\ausfml$ is true in the next state;
\item The formula $\tuntil{\ausfml}{\busfml}$ means that $\ausfml$ is true until $\busfml$ is true (which also will be true at some point).
\end{itemize}
\end{itemize}
\bibliography{platzer,bibliography}
\end{document}