% vim: ft=tex
\section{Implementation}

This chapter discusses how the concepts introduced before are implemented
into a simulator. Futher the infrastructure around the simulation and some
tools are explained.

\subsection{Dense State Vector Simulation}

\subsubsection{Representation of Dense State Vectors}

Recalling \eqref{eq:ci} any $n$-qbit state can be represented as a
$2^n$ component vector in the integer state basis. This representation
has some useful features when it comes to computations:

\begin{itemize}
    \item{The projection on the integer states is trivial.}
    \item{For any qbit $j$ and $0 \le i \le 2^n-1$ the coefficient $c_i$ is part of the $\ket{1}_j$ amplitude iff
        $i \& (1 << j)$ and part of the $\ket{0}_j$ amplitude otherwise.}
    \item{For a qbit $j$ the coefficients $c_i$ and $c_{i \hat{} (1 << j)}$ are the conjugated coefficients.}
\end{itemize}

Where $\hat{}$ is the binary XOR, $\&$ the binary AND and $<<$ the binary leftshift operator.

While implementing the dense state vectors two key points were allowing a simple and readable
way to use them and simple access to the states by users that want more information than an
abstracted view could allow. To meet both requirements the states are implemented as Python objects
providing abstract features such as normalization checking, checking for sufficient qbit number when applying
a circuit, computing overlaps with other states, a stringify method and stored measurement results.
To store the measurement results a NumPy \lstinline{int8} array \cite{numpy_array} is used; this is called
the classical state.
The Python states also have a NumPy \lstinline{cdouble} array that stores the quantum mechanical state.
Using NumPy arrays has the advantage that access to the data is simple and safe while operations
on the states can be implemented in \lstinline{C} \cite{numpy_ufunc} providing a considerable speedup.

This quantum mechanical state is the component vector in integer basis therefore it has $2^n$ components.
Storing those components is acceptable in a range from $1$ to $30$ qbits; above this range the state requires
space in the order of $1 \mbox{ GiB}$  which is in the range of usual RAM sizes for personal computers. For higher
qbit numbers moving to high performance computers and other simulators is necessary.

\subsubsection{Gates}

Gates on dense state vectors are implemented as NumPy Universal Functions (ufuncs) \cite{numpy_ufunc} mapping a classical
and a quantum state to a new classical state, a new quantum state and a $64 \mbox{ bit}$ integer indicating what qbits have
been measured. Using ufuncs has the great advantage that managing memory is done by NumPy and an application programmer
just has to implement the logic of the function. Because ufuncs are written in \lstinline{C} they provide a considerable
speedup compared to an implementation in Python.

The logic of gates is usually easy to implement using the integer basis. The example below implements the Hadamard gate
\ref{ref:singleqbitgates}:

\adjustbox{max width=\textwidth}{\lstinputlisting[language=C, firstline=153, lastline=178]{../pyqcs/src/pyqcs/gates/implementations/basic_gates.c}}

A basic set of gates is implemented in PyQCS:

\begin{itemize}
    \item{Hadamard $H$ gate.}
    \item{Pauli $X$ or \textit{NOT} gate.}
    \item{Pauli $Z$ gate.}
    \item{The $S$ phase gate.}
    \item{$Z$ rotation $R_\phi$ gate.}
    \item{Controlled $X$ gate: $CX$.}
    \item{Controlled $Z$ gate: $CZ$.}
    \item{The measurement "gate" $M$.}
\end{itemize}

To allow the implementation of possible hardware related gates the class \lstinline{GenericGate} takes
a unitary $2\times2$ matrix as a NumPy \lstinline{cdouble} array and builds a gate from it.

\subsubsection{Circuits}

