\documentclass[conference]{IEEEtran}

\usepackage{cite}
\usepackage{graphicx}
\usepackage[cmex10]{amsmath}
\usepackage{array}
\usepackage[font=footnotesize,caption=false]{subfig}
\usepackage[hyphens]{url}
\usepackage{cprotect}
\usepackage{comment}
\usepackage{color}
\usepackage{amsfonts,amssymb,amsmath}
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{booktabs}
\usepackage{footmisc}
\usepackage{hyperref}
\hypersetup{colorlinks=true,linkcolor=blue}

\newcommand{\TODO}{{\color{red} TODO}}
\newcommand{\Z}{\mathbb{Z}}
\newcommand{\wrt}{w.\thinspace r.\thinspace t.\ }
\DeclareMathOperator{\rmax}{rmax}

\begin{document}

\title{Pentago is a First Player Win: Strongly Solving a Game Using Parallel In-Core Retrograde Analysis}
\author{\IEEEauthorblockN{Geoffrey Irving}\IEEEauthorblockA{Otherlab\\San Francisco, CA\\irving@naml.us}}

\maketitle

\begin{abstract}
\boldmath
We present a strong solution of the board game pentago, computed using exhaustive parallel
retrograde analysis in 4 hours on 98304 ($3 \times 2^{15}$) threads of NERSC's Cray Edison.
At $3.0 \times 10^{15}$ states, pentago is the largest divergent game solved to date by
two orders of magnitude, and the only example of a nontrivial divergent game solved
using retrograde analysis.
Unlike previous retrograde analyses, our computation was performed entirely in-core,
writing only a small portion of the results to disk; an out-of-core implementation would
have been much slower.  Symmetry was used to reduce branching factor
and exploit instruction level parallelism.  Despite a theoretically
embarrassingly parallel structure, asynchronous message passing was required to fit the
computation into available RAM, causing latency problems
on an older Cray machine.  All code and data for the project are
open source, together with a website which combines database lookup and on-the-fly computation
to interactively explore the strong solution.
\end{abstract}

\IEEEpeerreviewmaketitle

\vspace{-.02in}
\section{Introduction}

Computer play of combinatorial games such as chess, checkers, and go has been an active area of research
since the early days of computer science \cite{shannon1950chess}.  The limit of computer play is a solved
game, when a computer can play perfectly either from the start position (weakly solved) or from any
position (strongly solved).  The first nontrivial weakly solved game was Connect-Four in 1988 by both
Allen and Allis \cite{allis1988connectfour}, later strongly solved by Tromp \cite{tromp1995}.  Many games
have been solved since, the most challenging being the weak solution of checkers \cite{schaeffer2007checkers}.
The checkers solution involved 18 years of parallel out-of-core retrograde analysis culminating in a
$3.9 \times 10^{13}$ position endgame database together with a $10^{14}$ operation forward search.

To date, all solved games have been either convergent (fewer positions near the end of the game) or amenable
to knowledge-based strategies.  Checkers is an example of a convergent game: while the
entire $10^{20}$ state space is too large to explore fully, the set of positions with 10 or fewer pieces
has a more manageable $3.9 \times 10^{13}$ positions.  Pieces are removed but never added, so a database
of $\le 10$ piece positions can be computed via retrograde (backward) analysis starting with 1 piece, then 2 pieces,
and so on up to 10 pieces.  The computed database is then used to prune a forward search starting from the
beginning of the game.

\begin{figure}
\begin{center}
\includegraphics[width=.45\columnwidth]{move-0.pdf}
\hspace{.1in}
\includegraphics[width=.45\columnwidth]{move-4.pdf} \\
\vspace{.1in}
\begin{tikzpicture}
  \definecolor{win} {rgb}{0,1,0};
  \definecolor{tie} {rgb}{0,0,1};
  \definecolor{loss}{rgb}{1,0,0};
  \tikzstyle{kind}=[circle,draw=gray,inner sep=0pt,minimum size=15]
  \node at (-2,0) [kind,label=below:win, fill=win] {};
  \node at ( 0,0) [kind,label=below:tie, fill=tie] {};
  \node at ( 2,0) [kind,label=below:loss,fill=loss] {};
\end{tikzpicture}
\end{center}
\vspace{-.14in}
\cprotect\caption{(Left) With perfect play, the first player wins with any opening move except the
corners, which tie.  (Right) A more delicate position with black to play.
The full strong solution can be explored at \url{http://perfect-pentago.net}.}
\label{opening}
\vspace{-.04in}
\end{figure}

\begin{figure}
\begin{center}
\includegraphics[width=\columnwidth]{counts.pdf}
\end{center}
\vspace{-.1in}
\cprotect\caption{Counts of pentago positions vs.\ stones on the board, with symmetries removed.
Run \verb+web/counts+ in the source repository to reproduce.}
\label{counts}
\vspace{-.1in}
\end{figure}

In contrast to convergent games, the number of positions in a divergent game increases with time
(typically as more stones are added to the board), making traditional retrograde analysis plus forward
search impractical.  Thus, all nontrivial
divergent games solved to date have game-specific knowledge based strategies which can be used to avoid brute
force: zuzgwang control in Connect-Four \cite{allis1988connectfour}, threat-space search for gomoku and renju
\cite{allis1993gomoku,wagner2001renju}, and H-search in hex \cite{arneson2011hex}.  For discussion
on the characteristic of various solved games, see \cite{van2002games}.

Pentago is a divergent game designed by Tomas Flod\'en and sold by Mindtwister \cite{mindtwister2013pentago}.
We reproduce the rules here for completeness.  Pentago is played on a $6 \times 6$ board, divided into four
$3 \times 3$ quadrants. There are two players, black and white, who alternate turns. The goal of each
player is to get five stones of their color in a row, either horizontally, vertically, or diagonally.
Each turn, a player places a stone in an empty space in some quadrant, then chooses a possibly different
quadrant to rotate 90 degrees left or right. If both players get five in a row at the same time, or the
last move is played with no five in a row, the game is a tie. If a player makes five a row by placing a
stone, there is no need to rotate a quadrant: the player wins immediately.

Unlike divergent games solved to date, no strong knowledge based strategies are known for pentago, and
existing programs are capable of searching only to fairly low depths \cite{buescher2009pentago,ewalds2012havannah}.
This is primarily a consequence of the high branching factor of pentago: there are
$36 \cdot 8 = 288$ possible first moves including rotation and an average branching factor of $97.3$ over all
states.\cprotect\footnote{To reproduce the branching factor average, run \verb+bin/analyze branch+ in
\url{https://github.com/girving/pentago}.  For the rest of the paper, only the command will be given.}
To reduce the branching factor to a manageable level, our solver performs all computations in terms of
\emph{rotation abstracted positions} consisting of all 256 ways to rotate the quadrants of a given board;
this eliminates the factor of 8 due to rotation for an average branching factor of only $12.2$.  Operating
on more than one board at a time lets us take full advantage of SSE acceleration.

Unfortunately, symmetry techniques alone are insufficient to solve pentago on commodity hardware.  The game has
3,009,081,623,421,558 ($3 \times 10^{15}$) states with symmetries removed, all but $0.3\%$ of which are reachable
with valid play;\cprotect\footnote{Run \verb+analyze counts+ and \verb+analyze reachable+, respectively.} the number of
states over time is shown in \autoref{counts}.  To solve the game using retrograde analysis, we traverse all
positions in reverse order of the number of stones, starting from the 35 stone \emph{slice} (the 36th is computed
on demand) and iteratively computing the $n$-stone slice from the $(n+1)$-stone slice up to the beginning of the game.
This requires storing two adjacent slices at a time, requiring $213$ TB at peak before compression.  Our initial
target was to fit into half of the NERSC Cray Hopper's $217$ TB, which was plausible using fast but weak compression
only if minimal memory was wasted communication buffers and working storage.

In order to minimize working memory, our parallel solver grabs inputs from other processes immediately before
they are used, overlapping a small number of work chunks to hide latency.  Since computing each chunk takes a variable
amount of time (see below), we opted for a fully asynchronous communication
pattern: when a process needs an input block, it sends a message to the owner of that block, and the owner
replies asynchronously with the data.

The solver was run exactly once at full scale, generating
a $3.7$ TB database of perfect results with $0$ through $18$ stones and establishing that pentago is a win for
the first player to move (\autoref{opening}).  The full strong solution can be explored online at
\url{http://perfect-pentago.net}.

At $3.0 \times 10^{15}$ states, pentago is the largest divergent game computation by a factor of
150 (vs.\ $2 \times 10^{13}$ for $9 \times 6$ Connect-Four), and the largest strongly solved game by a factor of
660 (vs.\ $4.5 \times 10^{12}$ for $7 \times 6$ Connect-Four).  Among retrograde analyses used to solve games,
it is the largest by state space by a factor of 77 (vs.\ $3.9 \times 10^{13}$ in the solution of Checkers).
However, it is not the largest endgame database over any game: the 7-piece Lomonosov Endgame Tablebases for
chess are 140 TB in size, and were computed over six months at Moscow State's Lomonosov supercomputer
\cite{makhnychev2012chess}.  Unfortunately, the technical details of the Lomonosov computation are unpublished,
so a detailed comparison is difficult.

\section{Problem definition}

Let $S$ be the set of arrangements of black and white stones on a $6 \times 6$ board.  Only some of these are
valid pentago positions: if we let black play first, we have equal numbers of black and white stones on black's
turn and one extra black stone on white's turn.  Define predicates $f_b,f_w : S \to \{0,1\}$ by $f_c(s) = 1$
if color $c$ has a five in a row.  Given color $c$, let $\bar{c}$ be the other color.  For $s \in S$, let
$p_c(s) \subset S$ be the positions reached by placing a stone of color $c$, $r(s)$ the positions reached by
rotating exactly one quadrant $90^\circ$ left or right.  Let $v_c(s)$ be the value of position $s$ with $c$
to play: $v_c(s) = -1,0,1$ if $c$ loses, ties, or wins, respectively.  If $f_b(s)$, $f_w(s)$, or $s$ has 36 stones,
the game is over and $v_c(s) = f_c(s) - f_{\bar{c}}(s)$.  Otherwise
\begin{align*}
v_c(s) = \max_{a \in p_c(s)} \begin{cases}
  1 & \mbox{if } f_c(a) \\
  \max_{b \in r(a)} h_c(b) & \mbox{otherwise}
\end{cases}
\end{align*}
where
\begin{align*}
h_c(s) &= \begin{cases} f_c(s) - f_{\bar{c}}(s) & \mbox{if } f_c(s) \lor f_{\bar{c}}(s) \\
                        -v_{\bar{c}}(s) & \mbox{otherwise} \end{cases}
\end{align*}

\begin{figure*}[ht!]
\begin{center}
\begin{tikzpicture}[scale=.7]
% Show slices
\usetikzlibrary[decorations.pathreplacing]
\draw [decoration={brace,amplitude=7},decorate] (-13.5,4) -- node [above,yshift=6] {Slice $n+1$} (-6.5,4);
\draw [decoration={brace,amplitude=7},decorate] (0,4)     -- node [above,yshift=6] {Slice $n$} (9,4);
% Horizontal compute
\begin{scope}[shift={(-5,0)}]
  \filldraw[draw=black,fill=red!10!white,rounded corners=10] (-.5-3.5,-.5) rectangle (4+5,3.5);
  \node at (3.5/2,3) {Compute};
  \filldraw[draw=black,fill=green!50!white] (0,1.5) rectangle (3.5,1+1.5);
  \filldraw[draw=black,fill=blue!30!white] (.5,0) rectangle (2.5+.5,1);
  \pgfmathsetseed{1138};
  \foreach \y in {1/3,2/3}
    \foreach \i in {0,...,3}
      \foreach \j in {0,1}{
        \pgfmathrnd \pgfmathsetmacro{\v}{\pgfmathresult};
        \draw[->] (2.5*\v+.5,\y) -- (3.5*\i/4+3.5*.5/4,\y+1.5);
      }
\end{scope}
\draw[->,very thick] (3.5-5,1.5+.5) -- (0,1.5);
% Output block with contributing lines
\fill[white] (0,0) rectangle (3.5,2.5);
\fill[green!50!white] (0,1) rectangle (3.5,2);
%\fill[green!50!black] (1,1) rectangle (2,2);
% Output section
\node at (3.5/2,3) {Scatter};
\draw[step=1] (0,0) grid (3.5,2.5);
\draw (0,2.5) -- (3.5,2.5) -- (3.5,0);
\foreach \x in {.5,2.5,3.25}{
  \draw[->,very thick] (\x,1) -- (\x,1-1.75);
  \node at (\x,1-1.75-.125) {$\vdots$};
}
% Horizontal input section
\begin{scope}[shift={(-8.5,0)}]
  % Rotated line
  \fill[white] (0,0) rectangle (1,2.5);
  \fill[blue!30!white] (1,0) rectangle (2,2.5);
  \draw[->,very thick] (2,1.25) -- node [above] {$\curvearrowright$} node [below,sloped] {rotate} (3.5+.5,.5);
  % Section
  \node at (2/2,3) {Gather};
  \draw[step=1] (0,0) grid (2,2.5);
  \draw (0,2.5) -- (2,2.5);
\end{scope}
% Input blocks
\pgfmathsetmacro{\sx}{3.75}
\pgfmathsetmacro{\sy}{.4}
\begin{scope}[shift={(-\sx-8.5,-\sy)}]
  \begin{scope}[shift={(0,2.5)}]
    \filldraw[draw=black,fill=red!10!white,rounded corners=5] (-.25,-.25) rectangle (1.25,1.5) ;
    \node at (.5,1) {Input};
    \filldraw[draw=black,fill=blue!30!white] (0,0) rectangle +(1,.5);
  \end{scope}
  \begin{scope}[shift={(1,0)}]
    \filldraw[draw=black,fill=red!10!white,rounded corners=5] (-.25,-.25) rectangle (1.25,2);
    \node at (.5,1.5) {Input};
    \filldraw[draw=black,fill=blue!30!white] (0,0) rectangle +(1,1);
  \end{scope}
  \begin{scope}[shift={(-1,0)}]
    \filldraw[draw=black,fill=red!10!white,rounded corners=5] (-.25,-.25) rectangle (1.25,2);
    \node at (.5,1.5) {Input};
    \filldraw[draw=black,fill=blue!30!white] (0,0) rectangle +(1,1);
  \end{scope}
  \draw[->,very thick] (1-1,.5) -- (1-1+\sx+1,1+.5+\sy);
  \draw[->,very thick] (1+1,.5) -- (1-1+\sx+1,.5+\sy);
  \draw[->,very thick] (1,.5+2.25) -- (1-1+\sx+1,2+.25+\sy);
\end{scope}
\pgfmathsetmacro{\sx}{4.5}
\pgfmathsetmacro{\sy}{0}
\begin{scope}[shift={(\sx,\sy)}]
  \begin{scope}[shift={(1,0)}]
    \filldraw[draw=black,fill=red!10!white,rounded corners=10] (-.5,-.5) rectangle (4,3.5);
    \node at (3.5/2,3) {Combine};
    \fill[white] (0,0) rectangle (3.5,2.5);
    \fill[green!50!black] (1,1) rectangle (2,2);
    \draw[step=1] (0,0) grid (3.5,2.5);
    \draw (0,2.5) -- (3.5,2.5) -- (3.5,0);
  \end{scope}
  \draw[->,very thick] (2-\sx,1.5) -- (2,1.5);
  \draw[->,very thick] (2.5,1-1.75) -- (2.5,1);
  \node at (2.5,1-1.75-.175) {$\vdots$};
\end{scope}
\end{tikzpicture}
\end{center}
\vspace{-.2in}
\caption{We decompose the set of pentago position into sections, each a 4D array of blocks (shown here as
2D).  The results for a given block are the combination of results from each block line that contains it,
with each such block line depending on exactly one block line from a different section.  Each computation
from input line to output line can be performed on a different processor, first gathering the input blocks
together into a complete line, and finally scattering the output blocks to their owners.  Each pink rounded
rectangle lies in a possibly different process.  Since we compute only those sections which
are unique with symmetries removed, some input lines must be rotated before computation.}
\label{lines}
\vspace{-.1in}
\end{figure*}

\section{Abstracting over rotations}

The exact symmetry group of pentago is the 8 element dihedral group $D_4$ with 4 global reflections and
4 global rotations.  Computing only one element from each $D_4$ equivalence class saves a factor of $8$,
but does nothing for the large branching factor of the game.  Thus, we consider the \emph{local} group of
all 256 ways to rotate the four quadrants, which has the abelian group structure
$L = \Z_4 \times \Z_4 \times \Z_4 \times \Z_4$.
Combined with the group of global symmetries, the full group of \emph{approximate symmetries} is a semidirect product
$G = \Z_4^4 \rtimes D_4$ with $2048$ elements.

Computing one board $b$ from each equivalence class \wrt $G$ is not enough; we must compute a function
$$f_b : L \to \{-1,0,1\}$$
mapping $g \in L$ to the result of the quadrant rotated board $gb$.  Each board is a win,
loss, or tie, so there are $3^{256}$ such functions.  To avoid ternary arithmetic we use 2 bits per value for
uncompressed data: one bit for win vs. loss/tie and one for win/tie vs. loss.  Thus, for each board we have two
functions $\Z_4^4 \to \{0,1\}$, each a $4 \times 4 \times 4 \times 4$ array of bits.  Each such function
$L \to \{0,1\}$ is packed into a 256 bit table.

Since quadrant rotations do not change the equivalence class \wrt $G$, operating on these functions $f_b$ removes
the branching factor due to rotations.  In its place, we have the mixing operation
\begin{align*}
\rmax &: \left(L \to \{0,1\}\right) \to \left(L \to \{0,1\}\right) \\
\rmax&(f)(g) = \max_{r \in R} f(g+r)
\end{align*}
where $R \subset L$ is the set of $90^\circ$ degree rotations left or right, and we use $+$ because the
group $L$ is abelian.  In addition to $\rmax$, two other rotation abstracted routines are needed.
First, given the position of stones of one color, we must be able to compute the set of rotations
$g \in L$ which produce five in a row.  Second, our equivalence class representative \wrt $G$ can change
when we add a stone, so we must be able to transform $f_b$ into $f_{gb}$ for any $g \in G$; this
involves cyclic shifts, dimension transpositions, and reflections of $4 \times 4 \times 4 \times 4$ bit tables.

Although the code required for these operations is complex, verifying their correctness was a
straightforward process of checking group theoretic definitions against the much simpler routines
operating on one board at a time.  The ease of verification frees us to make the routines as complicated as required
for speed without reducing confidence in the code.

\section{Data layout and distribution}

Given a board $b$, we must choose a unique representative out of the equivalence class $Gb$.  This choice
should be made such that adding a stone changes the representative choice in as few ways as possible, so
that the effective branching factor will be smaller once we take data layout into account.
Concretely, since we have eliminated
branching factor due to rotation, an average board has $12.2$ child boards which are needed as input; if
representatives were chosen arbitrarily, the representatives of the child equivalent classes \wrt $G$
might be located in up to $12.2$ processes depending on how data is distributed.

\begin{figure}
\begin{center}
\input{section.tex}
\end{center}
\vspace{-.15in}
\caption{Each section is a 4D array of functions $f_{abcd} : L \to \{-1,0,1\}$.  Each dimension
corresponds to all patterns of stones in one of the four quadrants with fixed counts of black and white
stones, including only patterns lexicographically minimal under rotation.  For each $(a,b,c,d)$
describing the four quadrants of a board, $f_{abcd}$ gives the loss/tie/win values for all 256 ways to rotate the
four quadrants.  The order is chosen so that reflected pairs are adjacent so that
reflection preserves the block structure.  The figure shows 2D slices of the full 4D array.}
\label{section}
\vspace{-.1in}
\end{figure}

Therefore, we partition all boards in a given slice (fixed number of stones) into \emph{sections} defined by the
numbers of stones of each color in the four quadrants, computing only sections whose counts are lexicographically
minimal under $D_4$ symmetry.  Within a given section, we consider only boards whose quadrants are lexicographically
minimal under per-quadrant rotations; each quadrant is independent under this requirement, so the section becomes
a four dimensional rectangular array where each dimension defines the stones in one quadrant.  We precompute an
ordering of these rotation minimal quadrant states so that we can convert from a position in the four dimensional
section array to a board state using table lookup.  The structure of one such section is shown in \autoref{section}.
There are at most four quadrants to chose from when placing a stone (some may be full near the end of the game),
so at most four child sections contribute to the results for a given parent.  In other words, we have reduced the
effective branching factor from $12.2$ to $4$.

Sections alone provide insufficiently fine parallelism (the largest is $1.8$ TB uncompressed), so we
divide each 4D section array into $8 \times 8 \times 8 \times 8$ blocks and partition the blocks for all
sections among the different processes.  When a stone is added in a quadrant, we move to a child section with
index layout different from the parent only for the quadrant where the stone was added
since the other quadrants have the same pattern of stones.  Therefore, a single block in a parent section depends
on inputs from one \emph{line} of blocks in up to four child sections.  Since the different input lines for a block
correspond to moves in different quadrants, we can compute each line contribution separately on different processes
and combine them with $\max$ on whichever process owns the output block.

The structure of the computation is illustrated in \autoref{lines}.  Say we want to compute an output block line
in section $n$, which is a $k \times 1 \times 1 \times 1$ grid of blocks (possibly transposed) corresponding to
an $8k \times 8 \times 8 \times 5$ grid of boards (block sizes may differ from $8$ at section boundaries).
Our output block line depends on a single input block line in section $n+1$, which (possibly after rotation) is an
$8k' \times 8 \times 8 \times 5$ grid of nodes.  The input and output block lines differ in size only along the
long dimension, since the long dimension corresponds to the quadrant where we will place a stone.  When we
compute index $(a,b,c,d)$ of the output line, we mix together several indices $(a',b,c,d)$ with different $a'$
corresponding to the different places to put a stone in the lines' quadrant.  Since the map from
$a$ to $a'$ is many to many, computing the entire block line on a single processor gives an effective
branching factor of $4$ for communication cost even though the underlying branching factor is $12.2$.
Once the block line is computed, its component blocks are scattered to their owners to be merged together
via $\max$ with other contributions (each block needs up to four such block line contributions).

Since our block structure is symmetric \wrt dimension and our quadrant positions are always minimal with respect
to rotation, the block structure of sections is preserved when the board is rotated.  However, the ordering of
quadrant states does change when a quadrant is reflected, since a lexicographically least quadrant may no longer
be lexicographically least after reflection.  To maintain the block structure, we require even sized blocks
(8 in our case) and adjust our precomputed quadrant state ordering so that reflected pairs occur next to each
other in the same block.  With this trick, the block structure is invariant to all symmetries.

The relatively simple structure of sections, blocks, and rotation-abstracted values within blocks does have
a cost: if a position or section is preserved by a symmetry it will be double counted in the data layout.
Abstracting over rotations increases the number of effective positions by 5.4\% and removing symmetries only
at the section level costs an additional 9.3\%, for a total overcounting of 15.2\% relative to storing each
symmetry-unique position once.\cprotect\footnote{Run \verb+analyze ratio+.}

\subsection{Deterministic pseudorandom partitioning}

In parallel, we must partition the set of blocks across processes to balance memory usage and the
set of block lines across processes to balance compute.  Ideally, the process computing a given block line
would also own many of the input and output blocks in order to minimize communication.  Unfortunately, these
desires couple together the partition for all slices.  Over all slices, there are 3,654,002,393 blocks and
996,084,744 block lines.\cprotect\footnote{Run \verb+analyze approx+.}  Thus, we have a graph partitioning
problem with 4,650,087,137 nodes divided into 72 clusters, each cluster defining a load balancing constraint.
Although existing graph partitioning codes such as ParMETIS\cite{schloegel2002parmetis} might be sufficient
for our problem, we have sufficient computation to hide communication latency and opt for a
simple randomized partitioning scheme instead.

We partition each slice independently.  Since there are at most 8239 sections to a slice, and
each section is a regular 4D grid of blocks, we can define an ordering of all block lines
by arranging the sections back to back.  We choose a pseudorandom permutation of the ordered block
lines and give each process a contiguous chunk of the scrambled ordering.  Each block is then randomly
assigned to one of its four lines, and given to the process which owns that line.  In both cases, these choices
can be made consistently with only an $O(1)$ size random seed shared between processes: we use the arbitrary size
cipher technique of \cite{black2002ciphers} for random access random permutations and the Threefry generator
of \cite{salmon2011parallel} for conventional random numbers.  Since the cipher permutations are invertible,
we can find the process owning a given block or block line in $O(1)$ time.

\begin{figure}
\begin{center}
\includegraphics[width=\columnwidth]{balance.pdf}
\end{center}
\vspace{-.1in}
\cprotect\caption{Load balance ratio ($\max/\min$) for various quantities as a function of stones on the board using
deterministic pseudorandom partitioning.  89\% of the computation occurs from slice $20$ to $28$, where
all quantities balance to within 20\%.  Run \verb+paper/numbers load+ to reproduce.}
\label{balance}
\vspace{-.1in}
\end{figure}

At scale, a pseudorandom partitioning scheme automatically balances any quantity where the central limit
theorem applies.  In particular, though our scheme does not explicitly account for the different amounts of
work required to compute different block lines, or the different sizes of blocks at the boundary of sections,
there are enough blocks and block lines to keep the $\max/\min$ ratio to within 10-20\% for all large slices and
all relevant quantities (\autoref{balance}).

\subsection{Compression}

Since our uncompressed memory usage would be at least $213$ TB, we compress all data in memory until needed
using the fast but weak compression library Snappy \cite{snappy2014}.  Most blocks are $256$ KB
($64 \cdot 8^4$ bytes) uncompressed, large enough to compress each block separately without harming compression ratio.
Despite its speed relative to stronger compression such as ZLIB or LZMA\cite{deutsch1996zlib,xz2014}, Snappy still consumed about 29\% of our
compute time ignoring I/O.\cprotect\footnote{See \verb+snappy fraction+ in \verb+paper/numbers+.}  Stronger
compression is thus out of reach for in memory purposes, although we do use LZMA when writing
out the smaller final data set.

With compression the memory usage varies unpredictably, with two consequences.  First, repeatedly
allocating and deallocating irregular block sizes results in significant fragmentation.  During early testing on
BlueGene, which has no virtual memory system, fragmentation caused the code to run out of memory much earlier
than necessary.  We solved this with a manual compacting garbage collector for bulk data storage, which is
straightforward in our case due to the lack of pointers.  Second, estimates from slices near the end of the game
gave a compression ratio of roughly $1/3$.  Since we were uncertain whether this ratio should grow or shrink at
the peak of the computation, and wanted a high probability of solving the game in a single run, we used a
conservative estimate of $0.4$ when determining how many nodes to use.  However, the actual average compression
ratio was $0.26$.\cprotect\footnote{See \verb+total data+ in \verb+paper/numbers+.}  Taking advantage
of the unexpectedly good compression would have required dynamic partitioning, or even (ideally) a dynamic
number of MPI nodes.

\section{Asynchronous control flow}

\begin{figure*}
\begin{center}
\begin{tikzpicture}
  \node[anchor=south west,inner sep=0] (full) at (0,0) {\includegraphics[height=2.0in]{tiny-history.png}};
  \begin{scope}[x={(full.south east)},y={(full.north west)}]
    \foreach \i in {0,...,15}{
      \node at (0,\i/16+1/32) [left,scale=.5] {\i};
    }
    \node at (-.04,.5) [rotate=90,scale=.8] {Process rank};
    \foreach \j in {0,20,...,260}{
      \draw (\j/263.473,0) -- +(0,-.01);
      \node at (\j/263.473,-.01) [below,scale=.5] {\j};
    }
    \node at (.5,-.1) [scale=.8] {Time (s)};
    % Legend (generated by paper/history)
    \begin{scope}[shift={(.4,0)}]
      \filldraw[draw=black,fill=white] (0,1) rectangle +(0.16,-0.260417);
      \definecolor{compute}{rgb}{0,1,0.571429};
      \fill[compute] (0.0025,1-0.00520833) rectangle +(0.05,-0.03125);
      \node [right,scale=.5,anchor=base west] at (0.0525,1-0.0302083) {compute};
      \definecolor{snappy}{rgb}{1,0.857143,0};
      \fill[snappy] (0.0025,1-0.0364583) rectangle +(0.05,-0.03125);
      \node [right,scale=.5,anchor=base west] at (0.0525,1-0.0614583) {snappy};
      \definecolor{wait}{rgb}{1,0,0};
      \fill[wait] (0.0025,1-0.0677083) rectangle +(0.05,-0.03125);
      \node [right,scale=.5,anchor=base west] at (0.0525,1-0.0927083) {wait};
      \definecolor{unsnappy}{rgb}{0,0.571429,1};
      \fill[unsnappy] (0.0025,1-0.0989583) rectangle +(0.05,-0.03125);
      \node [right,scale=.5,anchor=base west] at (0.0525,1-0.123958) {unsnappy};
      \definecolor{compact}{rgb}{0.285714,0,1};
      \fill[compact] (0.0025,1-0.130208) rectangle +(0.05,-0.03125);
      \node [right,scale=.5,anchor=base west] at (0.0525,1-0.155208) {compact};
      \definecolor{count}{rgb}{0.285714,1,0};
      \fill[count] (0.0025,1-0.161458) rectangle +(0.05,-0.03125);
      \node [right,scale=.5,anchor=base west] at (0.0525,1-0.186458) {count};
      \definecolor{accumulate}{rgb}{1,0,0.857143};
      \fill[accumulate] (0.0025,1-0.192708) rectangle +(0.05,-0.03125);
      \node [right,scale=.5,anchor=base west] at (0.0525,1-0.217708) {accumulate};
      \definecolor{other}{rgb}{0.5,0.5,0.5};
      \fill[other] (0.0025,1-0.223958) rectangle +(0.05,-0.03125);
      \node [right,scale=.5,anchor=base west] at (0.0525,1-0.248958) {other};
    \end{scope}
    % Draw zoom rectangle
    \filldraw[draw=black,fill=white] (0.8730960728423784,0) rectangle (0.8752302148607257,1);
    \coordinate (fromleft) at (0.8730960728423784,1);
    \coordinate (fromright) at (0.8752302148607257,1);
  \end{scope}
  % full sizes = 263.473 96
  % box = Box((230.03724159999999,0),(230.59953039999999,96))
  \begin{scope}[shift={(11.5,0)}]
    \node[anchor=south west,inner sep=0] (image) at (0,0) {\includegraphics[height=2.0in]{tiny-history-graph.png}};
    \begin{scope}[x={(image.south east)},y={(image.north west)}]
      \draw (0,0) rectangle (1,1);
      \draw (0.498646,0.536458) -- (0.428306,0.536458); \draw (0.428306,0.536458) -- (0.211131,0.505208); \draw (0.428306,0.536458) -- (0.339567,0.505208); \draw (0.428306,0.536458) -- (0.338751,0.505208); \draw (0.428306,0.536458) -- (0.210792,0.505208); \draw (0.428306,0.536458) -- (0.211169,0.505208); \draw (0.211169,0.505208) -- (0.18248,0.880208); \draw (0.18248,0.880208) -- (0.0833778,0.505208); \draw (0.0833778,0.505208) -- (0.0833333,0.505208); \draw (0.210792,0.505208) -- (0.128093,0.817708); \draw (0.128093,0.817708) -- (0.0833716,0.505208); \draw (0.0833716,0.505208) -- (0.0833333,0.505208); \draw (0.338751,0.505208) -- (0.121249,0.0677083); \draw (0.121249,0.0677083) -- (0.0833653,0.505208); \draw (0.0833653,0.505208) -- (0.0833333,0.505208); \draw (0.339567,0.505208) -- (0.143496,0.317708); \draw (0.143496,0.317708) -- (0.0833556,0.505208); \draw (0.0833556,0.505208) -- (0.0833333,0.505208); \draw (0.211131,0.505208) -- (0.182467,0.880208); \draw (0.182467,0.880208) -- (0.0833449,0.505208); \draw (0.0833449,0.505208) -- (0.0833333,0.505208); \draw (0.498646,0.536458) -- (0.658716,0.515625); \draw (0.658716,0.515625) -- (0.722719,0.505208); \draw (0.658716,0.515625) -- (0.722741,0.505208); \draw (0.658716,0.515625) -- (0.722758,0.505208); \draw (0.658716,0.515625) -- (0.722817,0.505208); \draw (0.658716,0.515625) -- (0.722873,0.505208); \draw (0.658716,0.515625) -- (0.722928,0.505208); \draw (0.658716,0.515625) -- (0.723026,0.505208); \draw (0.658716,0.515625) -- (0.723078,0.505208); \draw (0.658716,0.515625) -- (0.723085,0.505208); \draw (0.658716,0.515625) -- (0.723096,0.505208); \draw (0.658716,0.515625) -- (0.723151,0.505208); \draw (0.658716,0.515625) -- (0.723251,0.505208); \draw (0.658716,0.515625) -- (0.723351,0.505208); \draw (0.658716,0.515625) -- (0.723405,0.505208); \draw (0.658716,0.515625) -- (0.723461,0.505208); \draw (0.658716,0.515625) -- (0.723518,0.505208); \draw (0.658716,0.515625) -- (0.723526,0.505208); \draw (0.658716,0.515625) -- (0.723577,0.505208); \draw (0.658716,0.515625) -- (0.723631,0.505208); \draw (0.658716,0.515625) -- (0.723639,0.505208); \draw (0.723639,0.505208) -- (0.777085,0.880208); \draw (0.777085,0.880208) -- (0.826283,0.901042); \draw (0.723631,0.505208) -- (0.782422,0.380208); \draw (0.782422,0.380208) -- (0.8089,0.432292); \draw (0.723577,0.505208) -- (0.72372,0.505208); \draw (0.72372,0.505208) -- (0.789167,0.515625); \draw (0.723526,0.505208) -- (0.811573,0.942708); \draw (0.811573,0.942708) -- (0.863152,0.953125); \draw (0.723518,0.505208) -- (0.811551,0.442708); \draw (0.811551,0.442708) -- (0.884799,0.484375); \draw (0.723461,0.505208) -- (0.723717,0.505208); \draw (0.723717,0.505208) -- (0.792687,0.515625); \draw (0.723405,0.505208) -- (0.811528,0.442708); \draw (0.811528,0.442708) -- (0.888,0.484375); \draw (0.723351,0.505208) -- (0.723712,0.505208); \draw (0.723712,0.505208) -- (0.795999,0.515625); \draw (0.723251,0.505208) -- (0.723674,0.505208); \draw (0.723674,0.505208) -- (0.810391,0.536458); \draw (0.723151,0.505208) -- (0.7237,0.505208); \draw (0.7237,0.505208) -- (0.806281,0.515625); \draw (0.723096,0.505208) -- (0.811523,0.442708); \draw (0.811523,0.442708) -- (0.891369,0.484375); \draw (0.723085,0.505208) -- (0.782361,0.380208); \draw (0.782361,0.380208) -- (0.812741,0.432292); \draw (0.723078,0.505208) -- (0.774176,0.880208); \draw (0.774176,0.880208) -- (0.843946,0.890625); \draw (0.723026,0.505208) -- (0.723705,0.505208); \draw (0.723705,0.505208) -- (0.802709,0.515625); \draw (0.722928,0.505208) -- (0.723708,0.505208); \draw (0.723708,0.505208) -- (0.799383,0.515625); \draw (0.722873,0.505208) -- (0.780705,0.0677083); \draw (0.780705,0.0677083) -- (0.794775,0.119792); \draw (0.722817,0.505208) -- (0.723695,0.505208); \draw (0.723695,0.505208) -- (0.916667,0.515625); \draw (0.722758,0.505208) -- (0.761403,0.192708); \draw (0.761403,0.192708) -- (0.795137,0.203125); \draw (0.722741,0.505208) -- (0.732447,0.255208); \draw (0.732447,0.255208) -- (0.739917,0.296875); \draw (0.722719,0.505208) -- (0.811442,0.942708); \draw (0.811442,0.942708) -- (0.86977,0.994792);
      \coordinate (toleft) at (0,1);
      \coordinate (toright) at (1,1);
      \foreach \i in {230.1,230.2,230.3,230.4,230.5}{
        \draw (\i/.5622888-409.1087026809,0) -- +(0,-.015);
        \node at (\i/.5622888-409.1087026809,-.015) [below,scale=.5] {\i};
      }
      \node at (.5,-.1) [scale=.8] {Time (s)};
    \end{scope}
  \end{scope}
  \draw plot [smooth,tension=1] coordinates { (fromleft) ($ .5*(fromleft) + .5*(toleft) + (0,.12) $) (toleft) };
  \draw plot [smooth,tension=1] coordinates { (fromright) ($ .5*(fromright) + .5*(toright) + (0,.5) $) (toright) };
\end{tikzpicture}
\end{center}
\vspace{-.15in}
\cprotect\caption{Images from a trace visualization tool used to diagnose performance problems in asynchronous
code.  (Left) The history of a 16 process, 96 thread run computing the section with four stones of
each color in each quadrant.  Each process has one communication thread (mostly red for waiting) and five worker
threads performing computation, with colors showing the type of computation performed.  (Right) A zoom showing the
information flow related to part of a block line computation.  At the leftmost point in the graph shown, the process
decides to compute a given block line, and sends out requests for input data to other processes.
Once all responses arrive, the computation begins.  When the computation finishes, the results are scattered to
other processes and compressed for storage.  To reproduce, run \verb+paper/history+.}
\label{history}
\vspace{-.1in}
\end{figure*}

Within slice $n$, each process can compute its allocation of block lines in any order, since all inputs
are from slice $n+1$ which has already been computed.  However, most of these inputs are stored on other
processes, and due to memory limitations only a small fraction of them can be stored locally at any given time.
Moreover, the time to compute a given block line varies with size, ruling out a lockstep
communication/compute cycle.  Instead, we use an asynchronous control flow where each process sends
requests for input data for at most five block lines at time, begins computing as soon as all inputs for a
block line are in place, sends out output data when ready, and listens for incoming output data from other
processes to be merged.

We emphasize that asynchrony is needed only because of the memory constraint: if we had 4 times as much memory
in order to store all inputs locally, we could split the computation into communication / compute epochs and
use an embarrassingly parallel control flow during compute.

We use a hybrid MPI/Pthread model where each 6 thread process has 1 communication thread and 5 worker threads
(with 8 processes per 48-hyperthread Edison node).
A hybrid structure reduces the memory usage by allowing several threads to share the same temporary storage
required when computing a block line.  The communication thread must simultaneously listen for
incoming remote messages and completed tasks from the worker threads; this can be done with self-to-self MPI
messages in environments which support \verb+MPI_THREAD_MULTIPLE+ but requires alternatively polling between
\verb+MPI_Testsome+ and \verb+pthread_spin_trylock+ if only \verb+MPI_THREAD_FUNNELED+ is available.

At the time the code was written, the MPI 3 standard was not yet available on the target machine, and the
one sided communication primitives in MPI 2 were not sufficient for our communication
pattern.\footnote{For more discussion, see \url{http://scicomp.stackexchange.com/questions/2846/simulating-the-mpi-isend-irecv-wait-model-with-one-sided-communication}.}
Specifically, the MPI 2 one sided primitives provide no asynchronous way to know when a request completes; and
our only synchronization points are between entire slices.  MPI 3 solves this problem:
after an initial communication phase exchanging pointers to the required compressed slice $n+1$ blocks, all
input requests during slice $n$ computation could be handled with \verb+MPI_Rget+\cite{mpi-3:2012}.
Unfortunately, MPI 3 does
not solve the reverse problem of output messages: when an output block arrives, any previous data for that
block must be uncompressed, combined with the new data, and recompressed for storage.  \verb+MPI_Accumulate+ has
no support for user defined operations, so output messages would still be limited to two sided communication.
Finally, MPI 3 provides the useful \verb+MPI_Ibarrier+ primitive which is exactly what we need to know when
all processes have finished computing and thus when the previous slice can be deallocated; since we use MPI 2
we must simulate \verb+MPI_Ibarrier+ using a manual tree reduction.

The asynchronous control flow was tricky to write but straightforward to debug,
since most bugs manifested as deadlocks.  Each message and response is labeled with a unique global id,
so deadlocks were easy to eliminate by reading traces of events.  However, the performance
characteristics of the code were harder to understand, since high latency might be a result of
unrelated communication at the same time.  Existing profiling tools such as TAU
\cite{shende2006tau} were insufficient for tracing the dependencies between asynchronous messages
combined with control flow across threads.  Thus, we wrote a custom trace visualizer with
knowledge of the information flow between inputs through compute to output; an example visualization is
shown in \autoref{history}.  On NERSC's Cray XE6 Hopper, this tool confirmed that long idle periods
were due to high latency, but was not sufficient to diagnose the underlying cause of the problem.
Unfortunately, we still do not know the cause of this latency.  Testing on the Argonne's BlueGene/Q Vesta
was inconclusive since the code easily saturated BlueGene's poor integer performance.  On the newer Cray
Edison used for the final production run, the problem went away: worker threads were idle only
16.4\% of the time with I/O excluded.\cprotect\footnote{See \verb+Idle vs. total time+ in
\verb+paper/numbers+\label{idle}.}

\section{Performance}

Our final production job ran on NERSC's Cray XC30 Edison, using 98304 ($3 \times 2^{15}$) threads
including hyperthreading (49152 cores, 2048 nodes).  The bulk of the computation from slice $35$ down
to $19$ took $2.7$ hours.  Starting at slice $18$ we began writing output results to disk, though our
first computation finished writing only slices $17$ and $18$ before hitting an unfortunately chosen
wall clock limit of $4$ hours.  Two smaller jobs on 192 and 128 nodes were used to finish the computation
down to slice $0$, the start of the game.

\begin{figure}
\begin{center}
\includegraphics[width=\columnwidth]{profile.pdf}
\end{center}
\vspace{-.1in}
\cprotect\caption{Time profile of the main production run, showing total worker thread time usage over
all processes.  All game logic is contained in the \emph{compute} (green) section; the other sections are
overhead due to load imbalance, non-hidden communication latency, compression/decompression, and I/O.
Slices 17 and 18 include LZMA compression and final I/O.  During I/O, all workers are idle and the communication
thread on each process is inside MPI/IO.}
\label{profile}
\vspace{-.12in}
\end{figure}

The time profile of the computation is shown in \autoref{profile}.  Excluding I/O, only 16.4\% of the
total worker thread time is spent idle,\footref{idle}
confirming that our random load balancing scheme is sufficient for near peak performance.  Since only 5
out of 6 threads per rank are workers, we could theoretically speed up the computation by up to 6/5 if
the communication thread performed useful work.  Unfortunately, existing MPI implementations do not
implement performant asynchronous progress, even ignoring our need for active responses to messages
(see \cite{squyres2012progress} for a good discussion).

Including I/O, our performance is further from optimal: 51.0\% of worker thread time is
idle, with 34.6\% due entirely to I/O.  This is due to both very high latency when
writing small files during every slice (around 200 seconds independent of file size) and low bandwidth
when writing file results in slices 17 and 18.  The high latency was a consequence of
using \verb+MPI_File_write_ordered+ when writing small files, since MPICH and thus Cray MPI
implement this routine using shared files for synchronization rather than fast network collectives.
Unfortunately, the low bandwidth is likely user error: we accidentally wrote to NERSC's
global scratch filesystem rather than the special filesystem optimized for Edison.

Since much of the complexity of our implementation derives from the memory constrained in core
structure, it is important to estimate how much slower the computation would have been if run out of core.
Edison's peak I/O bandwidth is 168 GB/s, or 66 GB/s on our 2048 out of 5192 nodes if bandwidth is shared
proportionally.  An out of core version of our algorithm would write each block once and read each block
four times, for a total of 3.6 PB of I/O uncompressed or 0.94 PB with Snappy compression.  Thus, at peak I/O
bandwidth, a Snappy compressed out of core version of our code would take 4.0 hours for
I/O.\cprotect\footnote{See \verb+Total I/O time estimates+ in \verb+paper/numbers+.}  In contrast,
the non-I/O portion of our main run took 1.8 hours, for a speedup of 2.25.  If peak I/O performance could
not be achieved, or I/O and compute could not be fully overlapped, the speedup would be larger.

The rotation abstracted compute kernel uses SSE for instruction level parallelism, packing
each 256-bit $L \to \{0,1\}$ function into two 128 bit SSE registers.  $\rmax$ can be computed for one such
table in $152$ SSE instructions, or $3/5$ths of an instruction per position since each function
encodes $256$ positions.  Computing which of the 256 quadrant rotations of a board give five in
a row takes $190$ instructions and $640$ bytes of cache coherent table lookup, and transforming a
function $f_b$ into $f_{gb}$ takes between $60$ and $200$ instructions dependent on the particular $g \in G$.
Although bit-level representations of board state are standard in computational games,
we believe this is the first instance where values of many distinct positions are evaluated in parallel
using bit twiddling.

Measuring over the entire compute kernel (which excludes idle time, communication, and (de)compression),
our SSE routines achieve a $1.81\times$ speedup on Edison over 64-bit versions and a $2.25\times$ speedup on
an Intel Core i7, compared to a naive speedup of $2$ for twice as many bits per
instruction.\cprotect\footnote{See \verb+SSE vs. non-SSE speedup+ in \verb+paper/numbers+.}  We are not sure what
caused the superlinear speedup in the $2.25$ case; one possibility is reduced register pressure.

\begin{figure}
\begin{center}
\includegraphics[width=\columnwidth]{request-latency.pdf}
\end{center}
\vspace{-.1in}
\cprotect\caption{Latency for 8-byte request-for-input messages between different nodes in a 96-thread test run
on Edison.  Around 20\% of the messages complete in under $10$ ms, but the tail is quite long.  To reproduce,
run \verb+paper/numbers messages 0,2+.}
\label{latency}
\vspace{-.1in}
\end{figure}

Even on Edison's faster network, our typical message latency is still quite high as shown in \autoref{latency}.
Here latency is measured from immediately before \verb+MPI_Isend+ to the time we start responding to the finished
message when \verb+MPI_Testsome+ succeeds.  The large latencies may be due to interference between small messages
and larger messages, as the communication thread processes different types of messages asynchronously.  Since
the latency on Edison is low enough for our purposes, we have not investigated in detail.

\begin{comment}
\begin{table}
\begin{center}
\begin{tabular}{l|SSS}
\toprule & {instructions/cycle} & {bandwidth (GB/s)} & {misprediction rate} \\
compute  & 1.206 & 0.014 & 0.002 \\
snappy   & 1.049 & 0.002 & 0.064 \\
unsnappy & 1.394 & 0.004 & 0.070
\end{tabular}
\end{center}
\cprotect\caption{Issue rate (instructions/cycle), bandwidth, and misprediction rate
(mispredicted branches / conditional branches) for a 96-thread test run on Edison.
To reproduce, run \verb+paper/numbers papi+.}
\label{papi}
\end{table}
\end{comment}

Within each node, we used the PAPI hardware counter library \cite{mucci1999papi} to measure instruction issue,
cache misses, and branch mispredications.  The results show that our workload has minimal memory
bandwidth requirements (under $15$ MB/s per core in performance critical sections), mispredicts branches mostly
during Snappy (de)compression, and makes significant use of dual instruction issue only for the one out of five
worker threads that share a core with the less active communication thread; if hyperthreading is turned off, the
issue rate jumps from $1.21$ to $1.89$ instructions per cycle.\cprotect\footnote{Run \verb+paper/numbers papi+.}

\section{Correctness and fault detection}

Since our goal is a database of perfect play, it is important to consider the possible sources of error in the
computation.  We are interested only in undetected errors, since empirically the code ran
without crashing or failing an assertion.

On the software side, we make heavy use of unit tests throughout the code, including simple tests
for correctness of simple routines, bootstrap tests comparing simple routines to more complicated variants
(such as when abstracting over rotations), and comparison tests between different algorithms.
In particular, we compare our parallel backward code against the results of forward search, both at the
beginning and end of the game.  Near the end of the game this is easy, as forward search can quickly compute
perfect play.  Near the beginning, our tests replace all values at slice 4 or 5 with random values,
compute optimal play up to the random slice with both backward and forward algorithms, and compare.

On the hardware side, the main failure points are DRAM, CPU, network, and disk.  Undetected disk errors are unlikely
since the primary output files are checksummed as part of LZMA compression.  Of DRAM, CPU, and network, DRAM errors
dominate according to \cite{bergman2008exascale} (Tables 6.11 and 6.12), at least on BlueGene.  Edison memory
and network are SECDED (single error correct, double error detect), so an undetectable error would require three
simultaneous failures.  Unfortunately, we do not know a reliable method to estimate this probability conditional
on an apparently successful run, since DRAM errors are far from uncorrelated events \cite{schroeder2009dram}.
However, we believe the probability is quite small, and in particular that undetected hardware errors are less
likely than software errors.

As a test on software errors when running the code at scale, we write out small sample files with the
results of randomly chosen boards during each slice.  Large numbers of samples generated by the main run were
validated against forward search for slices 20 and up, so any remaining software errors in the parallel code
must manifest only on a small set of positions.  We also write out win/loss/tie counts for each slice.
Both sample and count files would be useful for cross-validation should someone reproduce the calculation
in the future.

Unfortunately, the sample files are insufficient to detect rare software bugs or hardware failures, and indeed
we know of no cheap method for detecting this kind of unlikely error without rerunning or rewriting the code.
Solving pentago falls most naturally into the complexity class PSPACE (polynomial space), and indeed the similar
five-in-a-row game gomoku has been proven PSPACE-complete \cite{reisch1980gobang}.  Unless
$\textrm{NP}=\textrm{PSPACE}$, it is unlikely that a short certificate exists proving that pentago is
a first player win, especially if we require a strong solution with perfect play known from all positions.

\section{Open source and data}

All code for this project is open source, and is available online at \url{https://github.com/girving/pentago}.
The repository includes the paper source and all log files used to generate timing and other numbers.
To regenerate any reported number from the data, run either
\verb+bin/analyze <command>+ or \verb+paper/numbers+; see the footnotes and figure captions.

The 3.7 TB strong solution is hosted on Rackspace Cloud Files; see the download instructions at
\url{https://github.com/girving/pentago/#data}.  We store small sparse sample and count files in
Numpy's \verb+.npy+ format \cite{kern2007npy}, and the main solution files in a custom \verb+.pentago+ format
using the described block structure with LZMA compression per block.  The format is described
at \url{https://github.com/girving/pentago/blob/master/pentago/data/supertensor.h}.  Both \verb+.npy+ and
\verb+.pentago+ formats are easy to write in parallel using MPI I/O.

The strong solution is useless without a convenient method for exploring the data, so we have built a website
showing which moves win, lose, or tie from any position: \url{http://perfect-pentago.net}.  The frontend
Javascript uses a backend server at \url{http://backend.perfect-pentago.net:2048} to look up the value of
positions.  Any position with 18 or fewer stones is fetched from the database using an HTTP range request
to download the surrounding compressed block.  As in the parallel algorithm, the children of a position
fall into at most four blocks; we cache the uncompressed blocks to take advantage of this locality.

Positions with more than 18 stones fall outside the database and are recomputed from scratch using a specialized
serial retrograde solver.  Since there are at least 18 stones already on the board, usually in an asymmetric
configuration, this solver rotates the board only through the $\rmax$ function, avoiding the complexity of
standardizing positions into rotation minimal configurations.  In addition, we use the fact that $\rmax$ flips
the parity of the $\Z_4^4$ symmetry group to store half the required bits, reducing the storage per rotation
abstracted position from 64 bytes to 32.  With these optimizations, evaluating all child values of an 18
stone position takes 16 seconds on a single 2.6 GHz Intel Xeon thread, fast enough for interactive use.

Both remote lookups and from-scratch computation have significant latency, so the backend server is written
in Javascript using Node.js \cite{dahl2014nodejs} for asynchronous use by multiple clients.  The Javascript
handles asynchronous logic and I/O, but calls down to C++ for performance intensive computation.  The backend
server has a simple JSON API, and anyone wishing to develop their own frontend is welcome to query it directly.

\section{Conclusion}

We have strongly solved the board game pentago using retrograde analysis on 98304 threads of Edison, a
Cray XC30 machine at NERSC.  Symmetry techniques were used to improve the branching factor of the game
and take advantage of SSE instruction level parallelism.  Unlike previous retrograde analysis, the computation
was almost entirely in-core, writing results to disk only near the end.  To fit safely into
memory, we use a fully asynchronous communication structure where each process requests data from other
processes as needed, performs computation, and scatters results to their destinations.

The asynchronous control flow was a primary complicating factor during development and optimization of the
code, and runs against several limitations of MPI including difficulties in synchronizing between MPI and threads,
lack of support for asynchronous progress in existing implementations, poor one-sided communication in MPI 2
(fixed in MPI 3 too late for use in this project), and lack of user defined operations in one-sided
\verb+MPI_Accumulate+.  Unfortunately, the latter would require both strong asynchronous progress and careful
consideration of threading semantics.  Profiling tools were also a significant limitation, leading us to implement
our own tracing and visualization tool to understand the flow of information across processes and between threads
without one process.  Although our custom tool helped localize the problem to high latency, we were unable to
diagnose the underlying cause; further analysis would likely require network profiling and visualization tools
incorporating knowledge of network topology.

Compression was a requirement to fit into memory, but we were limited to the fast and weak Snappy library to
prevent compression from becoming a compute bottleneck.  Compression also makes memory usage difficult to
predict in advance, causing us to overestimate memory requirements and use more Edison nodes than required.
Avoiding such overestimate without the I/O cost of checkpointing would require a dynamic number of MPI nodes.

Our computation shares many characteristics with other irregularly structured, data intensive HPC workloads.
These characteristics include multiple levels of structure (slices, sections, block lines, blocks, boards,
bits), memory restrictions, asynchronous control flow, reliance on integer performance (compression and game
logic), and reliance on both fast compute and fast communication.  Multiple levels of structure are important
in many applications (e.g., domains, pages, paragraphs, sentences, words for web search) and often warrant
different parallelism strategies at different levels.  In addition to allowing larger problem sizes either
in RAM or on-package RAM, the ability to operate near a memory limit improves performance for codes with
imperfect parallel scaling and eases the scheduling problem for shared clusters, increasing both latency and
bandwidth for users.  Asynchronous control flow adds flexibility which can be spent on memory constraints or
irregular work chunk sizes (common with multiple levels of structure).  Poor integer performance ruled out
BlueGene for our purposes, which is problematic even for floating point codes if compression is required.
Finally, traditional Big Data applications often have less tightly coupled communication patterns such as
MapReduce \cite{dean2008mapreduce}; our application is sufficiently latency-critical to obtain clear benefit
from the faster network on Edison compared to Hopper, and serves as an intermediate example between traditional
HPC and Big Data (see the Graph 500 benchmark suite for other examples \cite{murphy2010graph500}).

\section*{Acknowledgments}

I am grateful to Jeff Hammond for valuable advice throughout the project, and to Jed Brown for numerous helpful
suggestions including the initial suggestion that supercomputer time might be a possibility.
Hosting for the 3.7 TB data set and compute servers for \url{http://perfect-pentago.net} were generously donated
by Rackspace; thanks especially to Jesse Noller at Rackspace for offering to host this open source project.
Since a primary goal of this project is open data, accessible hosting for the final results is essential.
This research used resources of the National Energy Research Scientific Computing Center, which is supported by
the Office of Science of the U.S. Department of Energy under Contract No.\ DE-AC02-05CH11231.  This research also
used resources of the Argonne Leadership Computing Facility at Argonne National Laboratory, which is supported by
the Office of Science of the U.S. Department of Energy under contract DE-AC02-06CH11357.

\bibliography{references}
\bibliographystyle{IEEEtran}
\end{document}


\section{Introduction}

\paragraph{Target Customers}
Aircraft and airport noise are complex subject matters which have been studied for decades and are still the focus of many research efforts nowadays. Also at the department of Air Transport \& Operations (ATO) at TU Delft’s Faculty of Aerospace Engineering. ATO has three research aims: 1) To develop radical new ways to optimize aircraft operations for efficiency, safety, cost and environmental impact; 2) To extend the analysis to an airline fleet and network level to include capacity and resilience; 3) To synthesize these to include operational safety at an airline and ATM level. To support their research findings at conferences, the researchers at ATO need a standalone application for model-based optimization and visualization of aircraft noise. This report summarizes the research phase of this project, during which we familiarize ourselves with the field and investigate what programming language, libraries, tools, etc. are best suited for our product.

\paragraph{Customer Needs}
We will provide a product that represents a creative and efficient way to minimize and visualize aircraft noise along simulated and real flight routes. This requires the implementation of two mathematical models: one for the computation of noise contours and one for the iterative optimization of aircraft trajectories for minimum noise and population annoyance. The models will be deployed by the research team to predict aircraft noise along a particular trajectory (flight route) and to update the trajectory design in an iterated manner to minimize the produced noise over populated areas around airports. Therefore, the parameters that take part in the optimization of aircraft trajectories range from the generic criterion of contour areas to a number of site-specific criteria based on the impact on population

Additionally, the program should be able to visualize the noise produced along the trajectory through a 3D animation pictured on a real map. This requires a visualization of noise contours, which are ‘noise footprints’ whose shape indicate areas of constant noise. Noise contours are a new subject to the research group and have not been implemented in relation with noise minimization before so this will be a challenging topic for us. The visualization should also show the effects of the produced aircraft noise on population annoyance.

In this orientation report we summarize the results of the research phase of this project as follows: first in Section 2 we give some more detailed information on the applications that we will extend and on similar products in the field. Section 3 focuses on the requirements gathering for this project, including a description of the various interviews we have conducted as well as the main requirements we identify. Section 4 then describes our chosen approach based on these requirements, including what programming language and tools we will use. Finally, Section 5 details what quality guarantees we will provide and how these are verified.


    \filetitle{icrf}{Initial-condition response functions}{model/icrf}

	\paragraph{Syntax}\label{syntax}

\begin{verbatim}
S = icrf(M,NPer,...)
S = icrf(M,Range,...)
\end{verbatim}

\paragraph{Input arguments}\label{input-arguments}

\begin{itemize}
\item
  \texttt{M} {[} model {]} - Model object for which the initial
  condition responses will be simulated.
\item
  \texttt{Range} {[} numeric {]} - Date range with the first date being
  the shock date.
\item
  \texttt{NPer} {[} numeric {]} - Number of periods.
\end{itemize}

\paragraph{Output arguments}\label{output-arguments}

\begin{itemize}
\itemsep1pt\parskip0pt\parsep0pt
\item
  \texttt{S} {[} struct {]} - Database with initial condition response
  series.
\end{itemize}

\paragraph{Options}\label{options}

\begin{itemize}
\item
  \texttt{'delog='} {[} \emph{\texttt{true}} \textbar{} \texttt{false}
  {]} - Delogarithmise the responses for variables declared as
  \texttt{!variables:log}.
\item
  \texttt{'size='} {[} numeric \textbar{} \emph{\texttt{1}} for linear
  models \textbar{} \emph{\texttt{log(1.01)}} for non-linear models {]}
  - Size of the deviation in initial conditions.
\end{itemize}

\paragraph{Description}\label{description}

\paragraph{Example}\label{example}


    \filetitle{regen}{Regeneration time MCMC Metropolis posterior simulator}{poster/regen}

	\paragraph{Syntax}\label{syntax}

\begin{verbatim}
[Theta,LogPost,AR,Scale,FinalCov] = regen(Pos,NDraw,...)
\end{verbatim}

\paragraph{Input arguments}\label{input-arguments}

\begin{itemize}
\item
  \texttt{Pos} {[} poster {]} - Initialised posterior simulator object.
\item
  \texttt{NDraw} {[} numeric {]} - Length of the chain not including
  burn-in.
\end{itemize}

\paragraph{Output arguments}\label{output-arguments}

\begin{itemize}
\item
  \texttt{Theta} {[} numeric {]} - MCMC chain with individual parameters
  in rows.
\item
  \texttt{LogPost} {[} numeric {]} - Vector of log posterior density (up
  to a constant) in each draw.
\item
  \texttt{AR} {[} numeric {]} - Vector of cumulative acceptance ratios
  in each draw.
\item
  \texttt{Scale} {[} numeric {]} - Vector of proposal scale factors in
  each draw.
\item
  \texttt{FinalCov} {[} numeric {]} - Final proposal covariance matrix;
  the final covariance matrix of the random walk step is
  \texttt{Scale(end)\^{}2*FinalCov}.
\end{itemize}

\paragraph{Options}\label{options}

\paragraph{References}\label{references}

\paragraph{Brockwell, A.E., and Kadane, J.B., 2004. ``Identification of
Regeneration Times in MCMC Simulation, with Application to Adaptive
Schemes,'' mimeo, Carnegie Mellon
University.}\label{brockwell-a.e.-and-kadane-j.b.-2004.-identification-of-regeneration-times-in-mcmc-simulation-with-application-to-adaptive-schemes-mimeo-carnegie-mellon-university.}

\paragraph{Example}\label{example}


\documentclass[../../main]{subfiles}
\graphicspath{{images/rubiks-exp/}}

\begin{document}

At this point, there was an idea to use planes as an alternative to contours approach. \ac{PTAM} was chosen for finding planes and in future it could become the mechanism for an object tracking due to its nature \cite{puzzle_games_solver_q3_zakharov}.

\subsubsection{Bootstrapping}

This step is the first one and the most important. It, firstly, stores initial data, for instance, extracted key points from the first image, gray-scale representation of the first image and sets the pipeline to move on. Next it should analyze images from a video stream or an image sequence by comparing it to the first image stored in memory. As a result 3D point cloud is created, actually its simplified version. In addition, \ac{PTAM} is the algorithm for augmented reality thus it is possible to project different objects onto the detected surface.

\paragraph*{Optical flow and homography filtering.}

Optical flow algorithm is used within this solution in order to detect key points location in the next view using previous views. After that the algorithm checks if 80\% of the points have survived and if not it stops bootstrapping and says that tracking has failed. If this condition is satisfied and more than the specified number of points are tracked then algorithm homography is used for filtering. Using the mask it checks how many points have survived homography in order to filter the bootstrapped and tracked key points again. This is done to achieve the most precise computations using only the most effective key points. Afterwards, it checks camera motion by comparing key points stored in memory with the currently tracked ones. To analyze if the motion is sufficient it uses \ac{OpenCV}'s function $estimateRigidTransform$. If all the conditions are met it moves to the next step.

\paragraph*{Extracting essential matrix from fundamental.}

During this step it finds fundamental matrix using the bootstrapped and tracked key points. In computer vision, the fundamental matrix is a 3 by 3 matrix that relates corresponding points in stereo images. Then it filters key points again. The next step is computing essential matrix. In computer vision, the essential matrix is a 3 by 3 matrix, with some additional properties, which relates corresponding points in stereo images assuming that the cameras satisfy the pinhole camera model. The algorithm needs to have intrinsic parameters of the camera before it starts the work because it does not include calibration part. Intrinsic parameters are physical camera parameters like focal length and etc. To compute essential matrix it multiplies transposed intrinsic matrix by fundamental matrix and multiplies this by intrinsic matrix, not transposed. This matrix is needed to extract camera rotation and translation in relative perspective from the bootstrapped image to the current one. In order to do this it decomposes essential matrix with \ac{SVD}. Decomposition results into 2 possible rotation matrices and 2 possible translation vectors. If the determinant of the first rotation matrix plus $1.0f$ is less than $1e-09$ than algorithm changes signs of all numbers of the essential matrix and computes decomposition again. Then it proceeds to the next step.

\paragraph*{Triangulation of points.}

At this stage the assumption is made. Its essence is that the both views (images) extrinsic parameters matrix are composed of the rotation matrices and translation vectors. The constructed matrix is 3 by 4. However, \ac{SVD} generates 2 rotation matrices and 2 translation vectors. Thus the algorithm has to create four possible extrinsic matrices for the second view (camera) in order to triangulate until the correct one is found. It might happen that algorithm fails if the triangulation results will be wrong for all 4 tries.  In order to triangulate, it normalizes bootstrapped and tracked key points coordinates. Afterwards, it proceeds triangulation and computes status by checking $z$ component of the points. Key points are filtered by this status array. After that the reprojection error is computed. If the error is not acceptable it runs triangulation again with different set of rotation and translation components. However, if the error is acceptable it goes to the next step where it finds the plane.

\paragraph*{Finding a plane using 3d point cloud.}

This step contains \ac{PCA}. This analysis is necessary because it allows to get 3D structure information. It is used for plane's normal extraction in order to proceed key points filtering again. If more than 75\% of key points belong to the same plane the bootstrap tracking is considered to be finished.

\subsubsection{Tracking}

This stage is final for the \ac{PTAM} part of this solution. It computes optical flow for the key points that passed triangulation. \ac{PTAM} is the augmented reality algorithm hence it requires the logic to compute projection matrix in order to render something on the plane. In order to do this it solves \ac{PnP}.
The main difference between the real \ac{PTAM} and this simplified version is that the real one can track all captured key points even if they are out of the camera view. On the contrary, the described approach just removes all unseen key points until the minimal amount exists and in the end it terminates. The suggested simplification significantly reduced time for implementation and understanding of the algorithm work flow.

\subsubsection{Planes for a corner detection}

The idea behind the \ac{PTAM} usage was to find all contiguous planes where every 3 of them with 90 degrees angle between each other create a Rubik's cube angle. For this purpose, this triplets are used to find planes intersections (cube's corners). Using the triangulated data it is possible to find 3D coordinate for any 2D point in the specific view.

\begin{figure} [ht]
    \begin{center}
        \includegraphics[width=190pt]{planes_intersection}
        \caption{Example of planes intersection, cube's corner.}
        \label{fig:planes_intersection}
    \end{center}
\end{figure}

\newpage

These manipulations are useful in two senses, they allow to find a Rubik's cube corners and to track those corners in the 3D space, however it requires algorithm modification. Figure \ref{fig:planes_intersection} illustrates 3 planes intersection with the underlined corner point.

\subsubsection{Conclusion}

Algorithm performed very well for finding and tracking a single Rubik's cube face but it showed pretty bad results for the continuous running of the algorithm to find contiguous planes. The reason for that is illumination. The algorithm was able to find one, two or three random planes in a row but it definitely brakes somewhere in the middle. This causes problems for triangulation stage, since the algorithm world space is relative to the first bootstrap thus if the algorithm fails the next bootstrap will become new first bootstrap. In simple words, algorithm is lost and it has to start from the beginning. It is possible to modify this algorithm to handle such type of problems but this topic is out of thesis scope.

\end{document}


We now present monadic AAM.
%
Monadic AAM is part an extension of the AAM methodology, and part extension of
the AAM toolkit.
%
In methodology, interpreters are first abstracted in the AAM tradition, and
then further factored to be monadic.
%
In toolkit, several monads are provided by our library to recover various
analysis properties in a language agnostic fashion.

\subsection{AAM in Monadic Style}
\label{section:MonadicAAM:00}
\input{sections/04MonadicAAM/00MonadicStyle/main.tex}

\subsection{Generalizing the Monad}
\label{section:MonadicAAM:01}
\input{sections/04MonadicAAM/01Generalizing/main.tex}

\subsection{Recovering a Concrete Interpreter}
\label{section:MonadicAAM:02}
\input{sections/04MonadicAAM/02RecoveringConcrete/main.tex}

\subsection{Recovering kCFA}
\label{section:MonadicAAM:03}
\input{sections/04MonadicAAM/03RecoveringKCFA/main.tex}

\subsection{Optimizations}
\label{section:MonadicAAM:04}
\input{sections/04MonadicAAM/04Optimizations/main.tex}


%
% %CopyrightBegin%
%
% Copyright Ericsson AB 2017. All Rights Reserved.
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
%     http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
%
% %CopyrightEnd%
%

\chapter{Implementation constants}

This appendix summarizes the constants that characterize a \StdErlang\ implementation
and requirements on the values for these constants.  An implementation should
document the values, most of which are also available to programs.

\section*{Atoms}

The constant
\I{maxatomlength} (\S\ref{section:atoms}) must be at least $2^8-1$ ($255$)
and at most $2^{16}-1$ ($65\,535$).

The constant $\mathit{atom\_table\_size}$ gives
the size of the atom tables used when transforming terms to and from
the external representation (\S\ref{section:atom-tables}).
It must be at least $2^8$ ($256$).

\section*{Integers}

The integers in an implementation of \StdErlang\ are characterized by
five constants $\mathit{bounded}$, $\mathit{maxint}$, $\mathit{minint}$,
$\mathit{minfixnum}$ and $\mathit{maxfixnum}$
(\S\ref{section:integer-type}):
\begin{itemize}
\item There is no requirement on the constant $\mathit{bounded}$.
\item The constant $\mathit{maxint}$ is only relevant if $\it{bounded}=\B{true}$,
in which case $\mathit{maxint}$ must be at least $2^{59}-1$ (576\,460\,752\,303\,423\,487).
\item The only requirement on the constant $\mathit{maxfixnum}$ is the obvious
condition that $0 < \I{maxfixnum} \leq \I{maxint}$.
\item Either
$\I{minint} = -(\I{maxint}+1)$ or $\I{minint} = -\I{maxint}$ must hold.
\end{itemize}

\section*{Floats}

The floating-point numbers in an implementation of \StdErlang\ are characterized by
five constants $r$, $p$, $\mathit{emin}$, $\mathit{emax}$ and $\mathit{denorm}$
(\S\ref{section:float-type}):
\begin{itemize}
\item The radix $r$ should be even.
\item The precision $p$ should be such that $r^{p-1}\geq 10^6$.
\item For the constants $\mathit{emin}$ and $\mathit{emax}$ it should hold that
$\mathit{emin}-1 \leq k*(p-1)$ and  $\mathit{emax} > k*(p-1)$,
with $k\geq 2$ and $k$ as large an integer as practical, and that
$-2 \leq (emin-1) + emax \leq 2$.
\item There is no requirement on the constant $\mathit{denorm}$.
\end{itemize}

\section*{Refs}

The refs in an implementation are characterized by two constants:
\I{refs\_bounded} and \I{maxrefs} (\S\ref{section:refs}).
If \I{refs\_bounded} is \B{true},
then the value of \I{maxrefs} must be at least XXX.

\section*{PIDs}

The PIDs in an implementation are characterized by two constants:
\I{pids\_bounded} and \I{maxpids} (\S\ref{section:pids}).
If \I{pids\_bounded} is \B{true},
then the value of \I{maxpids} must be at least XXX.

\section*{Ports}

The ports in an implementation are characterized by two constants:
\I{ports\_bounded} and \I{maxports} (\S\ref{section:ports}).
If \I{ports\_bounded} is \B{true},
then the value of \I{maxports} must be at least XXX.

\section*{Tuples}

The constant \I{maxtuplesize} (\S\ref{section:tuples})
must be at least $2^{16}-1$ ($65\,535$) and at most
$2^{32}-1$ ($4\,294\,967\,296$).

\section*{Scheduling}

The constant \I{normal\_advantage} (\S\ref{section:scheduling})
should be between $4$ and $32$.


\section{$p$-$n$ Junction in $3D$ and Lower-Dimensions}\label{sec:sec004}

\begin{figure}[h!]\label{fig:fig09}
    \centering
    \includegraphics[height=4cm,width=5cm]{figs/2d_3d}
    \caption{Built-in potential as a function of position for $1D$, $2D$, and $3D$.}
\end{figure}

\begin{figure}[h!]\label{fig:fig08}
    \centering
    \includegraphics[height=4.5cm,width=8cm]{figs/2d_3d_wd_dep}
    \caption{Depletion width as a function of thickness (left) and carrier concentration (right) for $1D$, $2D$, and $3D$.}
\end{figure}


\section{Introduction}

The {\Python} library consists of three parts, with different levels of
integration with the interpreter.
Closest to the interpreter are built-in types, exceptions and functions.
Next are built-in modules, which are written in C and linked statically
with the interpreter.
Finally there are standard modules that are implemented entirely in
{\Python}, but are always available.
For efficiency, some standard modules may become built-in modules in
future versions of the interpreter.

\section{Built-in Types, Exceptions and Functions}

Names for built-in exceptions and functions are found in a separate
read-only symbol table which cannot be modified.
This table is searched last, so local and global user-defined names can
override built-in names.
Built-in types have no names but are created by syntactic constructs
(such as constants) or built-in functions.
They are described together here for easy reference.%
\footnote{
The descriptions sorely lack explanations of the exceptions that
may be raised---this will be fixed in a future version of this
document.
}

\subsection{Built-in Types}

The following sections describe the standard types that are built into the
interpreter.
\subsubsection{Numeric Types}

There are two numeric types: integers and floating point numbers.
Integers are implemented using {\tt long} in C, so they have at least 32
bits of precision.
Floating point numbers are implemented using {\tt double} in C.
All bets on precision are off.
Numbers are created by numeric constants or as the result of built-in
functions and operators.

Numeric types support the following operations:

\begin{center}
\begin{tabular}{|c|l|c|}
\hline
Operation & Result & Notes \\
\hline
{\tt abs}({\em x}) & absolute value of {\em x} & \\
{\tt int}({\em x}) & {\em x} converted to integer & (1) \\
{\tt float}({\em x}) & {\em x} converted to floating point & \\
{\tt -}{\em x} & {\em x} negated & \\
{\tt +}{\em x} & {\em x} unchanged & \\
{\em x}{\tt +}{\em y} & sum of {\em x} and {\em y} & \\
{\em x}{\tt -}{\em y} & difference of {\em x} and {\em y} & \\
{\em x}{\tt *}{\em y} & product of {\em x} and {\em y} & \\
{\em x}{\tt /}{\em y} & quotient of {\em x} and {\em y} & (2) \\
{\em x}{\tt \%}{\em y} & remainder of {\em x}{\tt /}{\em y} & (3) \\
\hline
\end{tabular}
\end{center}

\noindent
Notes:
\begin{description}
\item[(1)]
This may round or truncate as in C; see functions {\tt floor} and
{\tt ceil} in module {\tt math}.
\item[(2)]
Integer division is defined as in C: the result is an integer; with
positive operands, it truncates towards zero; with a negative operand,
the result is unspecified.
\item[(3)]
Only defined for integers.
\end{description}

Mixed arithmetic is not supported; both operands must have the same type.
Mixed comparisons return the wrong result (floats always compare smaller
than integers).%
\footnote{
These restrictions are bugs in the language definitions and will be
fixed in the future.
}
\subsubsection{Sequence Types}

There are three sequence types: strings, lists and tuples.
Strings constants are written in single quotes: {\tt 'xyzzy'}.
Lists are constructed with square brackets: {\tt [a,~b,~c]}.
Tuples are constructed by the comma operator or with an empty set of
parentheses: {\tt a,~b,~c} or {\tt ()}.

Sequence types support the following operations ({\em s} and {\em t} are
sequences of the same type; {\em n}, {\em i} and {\em j} are integers):

\begin{center}
\begin{tabular}{|c|l|c|}
\hline
Operation & Result & Notes \\
\hline
{\tt len}({\em s}) & length of {\em s} & \\
{\tt min}({\em s}) & smallest item of {\em s} & \\
{\tt max}({\em s}) & largest item of {\em s} & \\
{\em x} {\tt in} {\em s} &
	true if an item of {\em s} is equal to {\em x} & \\
{\em x} {\tt not} {\tt in} {\em s} &
	false if an item of {\em s} is equal to {\em x} & \\
{\em s}{\tt +}{\em t} & the concatenation of {\em s} and {\em t} & \\
{\em s}{\tt *}{\em n}, {\em n}*{\em s} &
	{\em n} copies of {\em s} concatenated & (1) \\
{\em s}[{\em i}] & {\em i}'th item of {\em s} & \\
{\em s}[{\em i}:{\em j}] &
	slice of {\em s} from {\em i} to {\em j} & (2) \\
\hline
\end{tabular}
\end{center}

\noindent
Notes:
\begin{description}
\item[(1)]
Sequence repetition is only supported for strings.
\item[(2)]
The slice of $s$ from $i$ to $j$ is defined as the sequence
of items with index $k$ such that $i \leq k < j$.
Special rules apply for negative and omitted indices; see the Tutorial
or the Reference Manual.
\end{description}

\paragraph{Mutable Sequence Types.}

List objects support additional operations that allow in-place
modification of the object.
These operations would be supported by other mutable sequence types
(when added to the language) as well.
Strings and tuples are immutable sequence types and such objects cannot
be modified once created.
The following operations are defined on mutable sequence types (where
{\em x} is an arbitrary object):

\begin{center}
\begin{tabular}{|c|l|}
\hline
Operation & Result \\
\hline
{\em s}[{\em i}] = {\em x} &
	item {\em i} of {\em s} is replaced by {\em x} \\
{\em s}[{\em i}:{\em j}] = {\em t} &
	slice of {\em s} from {\em i} to {\em j} is replaced by {\em t} \\
{\tt del} {\em s}[{\em i}:{\em j}] &
	same as {\em s}[{\em i}:{\em j}] = [] \\
{\em s}.{\tt append}({\em x}) &
	same as {\em s}[{\tt len}({\em x}):{\tt len}({\em x})] = [{\em x}] \\
{\em s}.{\tt insert}({\em i}, {\em x}) &
	same as {\em s}[{\em i}:{\em i}] = [{\em x}] \\
{\em s}.{\tt sort}() &
	the items of {\em s} are permuted to satisfy \\
	&
	$s[i] \leq s[j]$ for $i < j$\\
\hline
\end{tabular}
\end{center}

\subsubsection{Mapping Types}

A
{\em mapping}
object maps values of one type (the key type) to arbitrary objects.
Mappings are mutable objects.
There is currently only one mapping type, the
{\em dictionary}.
A dictionary's keys are strings.
An empty dictionary is created by the expression \verb"{}".
An extension of this notation is used to display dictionaries when
written (see the example below).

The following operations are defined on mappings (where {\em a} is a
mapping, {\em k} is a key and {\em x} is an arbitrary object):

\begin{center}
\begin{tabular}{|c|l|c|}
\hline
Operation & Result & Notes\\
\hline
{\tt len}({\em a}) & the number of elements in {\em a} & \\
{\em a}[{\em k}] & the item of {\em a} with key {\em k} & \\
{\em a}[{\em k}] = {\em x} & set {\em a}[{\em k}] to {\em x} & \\
{\tt del} {\em a}[{\em k}] & remove {\em a}[{\em k}] from {\em a} & \\
{\em a}.{\tt keys}() & a copy of {\em a}'s list of keys & (1) \\
{\em a}.{\tt has\_key}({\em k}) & true if {\em a} has a key {\em k} & \\
\hline
\end{tabular}
\end{center}

\noindent
Notes:
\begin{description}
\item[(1)]
Keys are listed in random order.
\end{description}

A small example using a dictionary:
\bcode\begin{verbatim}
>>> tel = {}
>>> tel['jack'] = 4098
>>> tel['sape'] = 4139
>>> tel['guido'] = 4127
>>> tel['jack']
4098
>>> tel
{'sape': 4139; 'guido': 4127; 'jack': 4098}
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'guido': 4127; 'irv': 4127; 'jack': 4098}
>>> tel.keys()
['guido', 'irv', 'jack']
>>> tel.has_key('guido')
1
>>>
\end{verbatim}\ecode
\subsubsection{Other Built-in Types}

The interpreter supports several other kinds of objects.
Most of these support only one or two operations.

\paragraph{Modules.}

The only operation on a module is member acces: {\em m}{\tt .}{\em name},
where {\em m} is a module and {\em name} accesses a name defined in
{\em m}'s symbol table.
Module members can be assigned to.

\paragraph{Classes and Class Objects.}

XXX Classes will be explained at length in a later version of this
document.

\paragraph{Functions.}

Function objects are created by function definitions.
The only operation on a function object is to call it:
{\em func}({\em optional-arguments}).

Built-in functions have a different type than user-defined functions,
but they support the same operation.

\paragraph{Methods.}

Methods are functions that are called using the member acces notation.
There are two flavors: built-in methods (such as {\tt append()} on
lists) and class member methods.
Built-in methods are described with the types that support them.
XXX Class member methods will be described in a later version of this
document.

\paragraph{Type Objects.}

Type objects represent the various object types.
An object's type is accessed by the built-in function
{\tt type()}.
There are no operations on type objects.

\paragraph{The Null Object.}

This object is returned by functions that don't explicitly return a
value.
It supports no operations.
There is exactly one null object, named {\tt None}
(a built-in name).

\paragraph{File Objects.}

File objects are implemented using C's
{\em stdio}
package and can be created with the built-in function
{\tt open()}.
They have the following methods:
\begin{description}
\funcitem{close}{}
Closes the file.
A closed file cannot be read or written anymore.
\funcitem{read}{size}
Reads at most
{\tt size}
bytes from the file (less if the read hits EOF).
The bytes are returned as a string object.
An empty string is returned when EOF is hit immediately.
(For certain files, like ttys, it makes sense to continue reading after
an EOF is hit.)
\funcitem{readline}{size}
Reads a line of at most
{\tt size}
bytes from the file.
A trailing newline character, if present, is kept in the string.
The size is optional and defaults to a large number (but not infinity).
EOF is reported as by
{\tt read().}
\funcitem{write}{str}
Writes a string to the file.
Returns no value.
\end{description}

\subsection{Built-in Exceptions}

The following exceptions can be generated by the interpreter or
built-in functions.
Except where mentioned, they have a string argument (also known as the
`associated value' of an exception) indicating the detailed cause of the
error.
The strings listed with the exception names are their values when used
in an expression or printed.
\begin{description}
\excitem{EOFError}{end-of-file read}
(No argument.)
Raised when a built-in function ({\tt input()} or {\tt raw\_input()})
hits an end-of-file condition (EOF) without reading any data.
(N.B.: the {\tt read()} and {\tt readline()} methods of file objects
return an empty string when they hit EOF.)
\excitem{KeyboardInterrupt}{end-of-file read}
(No argument.)
Raised when the user hits the interrupt key (normally Control-C or DEL).
During execution, a check for interrupts is made regularly.
Interrupts typed when a built-in function ({\tt input()} or
{\tt raw\_input()}) is waiting for input also raise this exception.
\excitem{MemoryError}{out of memory}
%.br
Raised when an operation runs out of memory but the situation
may still be rescued (by deleting some objects).
\excitem{NameError}{undefined name}
%.br
Raised when a name is not found.
This applies to unqualified names, module names (on {\tt import}),
module members and object methods.
The string argument is the name that could not be found.
\excitem{RuntimeError}{run-time error}
%.br
Raised for a variety of reasons, e.g., division by zero or index out of
range.
\excitem{SystemError}{system error}
%.br
Raised when the interpreter finds an internal error, but the situation
does not look so serious to cause it to abandon all hope.
\excitem{TypeError}{type error}
%.br
Raised when an operation or built-in function is applied to an object of
inappropriate type.
\end{description}

\subsection{Built-in Functions}

The {\Python} interpreter has a small number of functions built into it that
are always available.
They are listed here in alphabetical order.
\begin{description}
\funcitem{abs}{x}
Returns the absolute value of a number.
The argument may be an integer or floating point number.
\funcitem{chr}{i}
Returns a string of one character
whose ASCII code is the integer {\tt i},
e.g., {\tt chr(97)} returns the string {\tt 'a'}.
This is the inverse of {\tt ord()}.
\funcitem{dir}{}
Without arguments, this function returns the list of names in the
current local symbol table, sorted alphabetically.
With a module object as argument, it returns the sorted list of names in
that module's global symbol table.
For example:
\bcode\begin{verbatim}
>>> import sys
>>> dir()
['sys']
>>> dir(sys)
['argv', 'exit', 'modules', 'path', 'stderr', 'stdin', 'stdout']
>>>
\end{verbatim}\ecode
\funcitem{divmod}{a, b}
%.br
Takes two integers as arguments and returns a pair of integers
consisting of their quotient and remainder.
For
\bcode\begin{verbatim}
q, r = divmod(a, b)
\end{verbatim}\ecode
the invariants are:
\bcode\begin{verbatim}
a = q*b + r
abs(r) < abs(b)
r has the same sign as b
\end{verbatim}\ecode
For example:
\bcode\begin{verbatim}
>>> divmod(100, 7)
(14, 2)
>>> divmod(-100, 7)
(-15, 5)
>>> divmod(100, -7)
(-15, -5)
>>> divmod(-100, -7)
(14, -2)
>>>
\end{verbatim}\ecode
\funcitem{eval}{s}
Takes a string as argument and parses and evaluates it as a {\Python}
expression.
The expression is executed using the current local and global symbol
tables.
Syntax errors are reported as exceptions.
For example:
\bcode\begin{verbatim}
>>> x = 1
>>> eval('x+1')
2
>>>
\end{verbatim}\ecode
\funcitem{exec}{s}
Takes a string as argument and parses and evaluates it as a sequence of
{\Python} statements.
The string should end with a newline (\verb"'\n'").
The statement is executed using the current local and global symbol
tables.
Syntax errors are reported as exceptions.
For example:
\bcode\begin{verbatim}
>>> x = 1
>>> exec('x = x+1\n')
>>> x
2
>>>
\end{verbatim}\ecode
\funcitem{float}{x}
Converts a number to floating point.
The argument may be an integer or floating point number.
\funcitem{input}{s}
Equivalent to
{\tt eval(raw\_input(s))}.
As for
{\tt raw\_input()},
the argument is optional.
\funcitem{int}{x}
Converts a number to integer.
The argument may be an integer or floating point number.
\funcitem{len}{s}
Returns the length (the number of items) of an object.
The argument may be a sequence (string, tuple or list) or a mapping
(dictionary).
\funcitem{max}{s}
Returns the largest item of a non-empty sequence (string, tuple or list).
\funcitem{min}{s}
Returns the smallest item of a non-empty sequence (string, tuple or list).
\funcitem{open}{name, mode}
%.br
Returns a file object (described earlier under Built-in Types).
The string arguments are the same as for stdio's
{\tt fopen()}:
{\tt 'r'}
opens the file for reading,
{\tt 'w'}
opens it for writing (truncating an existing file),
{\tt 'a'}
opens it for appending.%
\footnote{
This function should go into a built-in module
{\tt io}.
}
\funcitem{ord}{c}
Takes a string of one character and returns its
ASCII value, e.g., {\tt ord('a')} returns the integer {\tt 97}.
This is the inverse of {\tt chr()}.
\funcitem{range}{}
This is a versatile function to create lists containing arithmetic
progressions of integers.
With two integer arguments, it returns the ascending sequence of
integers starting at the first and ending one before the second
argument.
A single argument is used as the end point of the sequence, with 0 used
as the starting point.
A third argument specifies the step size; negative steps are allowed and
work as expected, but don't specify a zero step.
The resulting list may be empty.
For example:
\bcode\begin{verbatim}
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> range(1, 1+10)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> range(0, 30, 5)
[0, 5, 10, 15, 20, 25]
>>> range(0, 10, 3)
[0, 3, 6, 9]
>>> range(0, -10, -1)
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
>>> range(0)
[]
>>> range(1, 0)
[]
>>>
\end{verbatim}\ecode
\funcitem{raw\_input}{s}
%.br
The argument is optional; if present, it is written to standard output
without a trailing newline.
The function then reads a line from input, converts it to a string
(stripping a trailing newline), and returns that.
EOF is reported as an exception.
For example:
\bcode\begin{verbatim}
>>> raw_input('Type anything: ')
Type anything: Mutant Teenage Ninja Turtles
'Mutant Teenage Ninja Turtles'
>>>
\end{verbatim}\ecode
\funcitem{reload}{module}
Causes an already imported module to be re-parsed and re-initialized.
This is useful if you have edited the module source file and want to
try out the new version without leaving {\Python}.
\funcitem{type}{x}
Returns the type of an object.
Types are objects themselves:
the type of a type object is its own type.
\end{description}


\section{Sorting}\label{Sorting}