some more stuff

This commit is contained in:
Daniel Knüttel 2020-03-09 18:02:31 +01:00
parent 75aeb83766
commit cd2a837f06

View File

@ -1,23 +1,23 @@
% vim: ft=tex % vim: ft=tex
\section{Implementation} \section{Implementation}
This chapter discusses how the concepts introduced before are implemented This chapter discusses how the concepts introduced before are implemented into
into a simulator. Futher the infrastructure around the simulation and some a simulator. Futher the infrastructure around the simulation and some tools are
tools are explained. explained.
The implementation is written as a \lstinline{python3} module. This allows The implementation is written as a \lstinline{python3} module. This allows
users to quickly construct circuit, apply them to a state and measure amplitudes. users to quickly construct circuit, apply them to a state and measure
Full access to the state (including intermediate) state has been priorized over amplitudes. Full access to the state (including intermediate) state has been
execution speed. To keep the simulation speed as high as possible under these priorized over execution speed. To keep the simulation speed as high as
constraints some parts are implemented in \lstinline{C} possible under these constraints some parts are implemented in \lstinline{C}
\subsection{Dense State Vector Simulation} \subsection{Dense State Vector Simulation}
\subsubsection{Representation of Dense State Vectors} \subsubsection{Representation of Dense State Vectors}
Recalling \eqref{eq:ci} any $n$-qbit state can be represented as a Recalling \eqref{eq:ci} any $n$-qbit state can be represented as a $2^n$
$2^n$ component vector in the integer state basis. This representation component vector in the integer state basis. This representation has some
has some useful features when it comes to computations: useful features when it comes to computations:
\begin{itemize} \begin{itemize}
\item{The projection on the integer states is trivial.} \item{The projection on the integer states is trivial.}
@ -26,34 +26,43 @@ has some useful features when it comes to computations:
\item{For a qbit $j$ the coefficients $c_i$ and $c_{i \hat{} (1 << j)}$ are the conjugated coefficients.} \item{For a qbit $j$ the coefficients $c_i$ and $c_{i \hat{} (1 << j)}$ are the conjugated coefficients.}
\end{itemize} \end{itemize}
Where $\hat{}$ is the binary XOR, $\&$ the binary AND and $<<$ the binary leftshift operator. 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 While implementing the dense state vectors two key points were allowing
way to use them and simple access to the states by users that want more information than an a simple and readable way to use them and simple access to the states by users
abstracted view could allow. To meet both requirements the states are implemented as Python objects that want more information than an abstracted view could allow. To meet both
providing abstract features such as normalization checking, checking for sufficient qbit number when applying requirements the states are implemented as Python objects providing abstract
a circuit, computing overlaps with other states, a stringify method and stored measurement results. features such as normalization checking, checking for sufficient qbit number
To store the measurement results a NumPy \lstinline{int8} array \cite{numpy_array} is used; this is called when applying a circuit, computing overlaps with other states, a stringify
the classical state. method and stored measurement results. To store the measurement results
The Python states also have a NumPy \lstinline{cdouble} array that stores the quantum mechanical state. a NumPy \lstinline{int8} array \cite{numpy_array} is used; this is called the
Using NumPy arrays has the advantage that access to the data is simple and safe while operations classical state. The Python states also have a NumPy \lstinline{cdouble} array
on the states can be implemented in \lstinline{C} \cite{numpy_ufunc} providing a considerable speedup. 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. This quantum mechanical state is the component vector in integer basis
Storing those components is acceptable in a range from $1$ to $30$ qbits; above this range the state requires therefore it has $2^n$ components. Storing those components is acceptable in
space in the order of $1 \mbox{ GiB}$ which is in the range of usual RAM sizes for personal computers. For higher a range from $1$ to $30$ qbits; above this range the state requires space in
qbit numbers moving to high performance computers and other simulators is necessary. 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} \subsubsection{Gates}
Gates on dense state vectors are implemented as NumPy Universal Functions (ufuncs) \cite{numpy_ufunc} mapping a classical Gates on dense state vectors are implemented as NumPy Universal Functions
and a quantum state to a new classical state, a new quantum state and a $64 \mbox{ bit}$ integer indicating what qbits have (ufuncs) \cite{numpy_ufunc} mapping a classical and a quantum state to a new
been measured. Using ufuncs has the great advantage that managing memory is done by NumPy and an application programmer classical state, a new quantum state and a $64 \mbox{ bit}$ integer indicating
just has to implement the logic of the function. Because ufuncs are written in \lstinline{C} they provide a considerable what qbits have been measured. Using ufuncs has the great advantage that
speedup compared to an implementation in Python. 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 The logic of gates is usually easy to implement using the integer basis. The
\ref{ref:singleqbitgates}: 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}} \adjustbox{max width=\textwidth}{\lstinputlisting[language=C, firstline=153, lastline=178]{../pyqcs/src/pyqcs/gates/implementations/basic_gates.c}}
@ -70,25 +79,28 @@ A basic set of gates is implemented in PyQCS:
\item{The measurement "gate" $M$.} \item{The measurement "gate" $M$.}
\end{itemize} \end{itemize}
To allow the implementation of possible hardware related gates the class \lstinline{GenericGate} takes To allow the implementation of possible hardware related gates the class
a unitary $2\times2$ matrix as a NumPy \lstinline{cdouble} array and builds a gate from it. \lstinline{GenericGate} takes a unitary $2\times2$ matrix as a NumPy
\lstinline{cdouble} array and builds a gate from it.
\subsubsection{Circuits} \subsubsection{Circuits}
As mentioned in \ref{ref:quantum_circuits} quantum circuits are central in quantum programming. As mentioned in \ref{ref:quantum_circuits} quantum circuits are central in
In the implementation great care was taken to make writing circuits as convenient and readable as quantum programming. In the implementation great care was taken to make
possible. Users will almost never access the actual gates that perform the operation on a state; writing circuits as convenient and readable as possible. Users will almost
instead they will handle circuits.\\ never access the actual gates that perform the operation on a state; instead
Circuits can be applied to a state by multiplying them from the left on a state object: they will handle circuits.\\ Circuits can be applied to a state by multiplying
them from the left on a state object:
\begin{lstlisting}[language=Python] \begin{lstlisting}[language=Python]
new_state = circuit * state new_state = circuit * state
\end{lstlisting} \end{lstlisting}
The elementary gates such as $H, R_\phi, CX$ are implemented as single gate circuits and can be constructing using The elementary gates such as $H, R_\phi, CX$ are implemented as single gate
the built-in generators. The generators take the act-qbit as first argument, parameters such as the control qbit circuits and can be constructing using the built-in generators. The generators
or an angle as second argument: take the act-qbit as first argument, parameters such as the control qbit or an
angle as second argument:
%\adjustbox{max width=\textwidth}{ %\adjustbox{max width=\textwidth}{
\begin{lstlisting}[language=Python] \begin{lstlisting}[language=Python]
@ -105,25 +117,25 @@ Out[2]: (0.7071067811865476+0j)*|0b0> + (0.7071067811865476+0j)*|0b11>
\end{lstlisting} \end{lstlisting}
%} %}
Large circuits can be constructed using the binary OR operator \lstinline{|} in an analogy to the Large circuits can be constructed using the binary OR operator \lstinline{|} in
pipeline operator on many *NIX systems. As usual circuits are read from left to right similar to pipelines on an analogy to the pipeline operator on many *NIX systems. As usual circuits are
*NIX systems: read from left to right similar to pipelines on *NIX systems:
%\adjustbox{max width=\textwidth}{ %\adjustbox{max width=\textwidth}{
\begin{lstlisting}[language=Python] \begin{lstlisting}[language=Python]
In [1]: from pyqcs import CX, CZ, H, R, Z, X In [1]: from pyqcs import CX, CZ, H, R, Z, X
...: from pyqcs import State ...: from pyqcs import State
...: ...:
...: state = State.new_zero_state(2) ...: state = State.new_zero_state(2)
...: ...:
...: # This is the same as ...: # This is the same as
...: # circuit = H(0) | CX(1, 0) ...: # circuit = H(0) | CX(1, 0)
...: circuit = H(0) | H(1) | CZ(1, 0) | H(1) ...: circuit = H(0) | H(1) | CZ(1, 0) | H(1)
...: ...:
...: bell_state = circuit * state ...: bell_state = circuit * state
In [2]: bell_state In [2]: bell_state
Out[2]: (0.7071067811865477+0j)*|0b0> + (0.7071067811865477+0j)*|0b11> Out[2]: (0.7071067811865477+0j)*|0b0> + (0.7071067811865477+0j)*|0b11>
\end{lstlisting} \end{lstlisting}
%} %}
@ -138,9 +150,9 @@ In [1]: from pyqcs import CX, CZ, H, R, Z, X
...: ...:
...: circuit_CX = list_to_circuit([CX(i, i-1) for i in range(1, 5)]) ...: circuit_CX = list_to_circuit([CX(i, i-1) for i in range(1, 5)])
...: ...:
...: state = (H(0) | circuit_CX) * State.new_zero_state(5) ...: state = (H(0) | circuit_CX) * State.new_zero_state(5)
In [2]: state In [2]: state
Out[2]: (0.7071067811865476+0j)*|0b0> + (0.7071067811865476+0j)*|0b11111> Out[2]: (0.7071067811865476+0j)*|0b0> + (0.7071067811865476+0j)*|0b11111>
\end{lstlisting} \end{lstlisting}
@ -150,3 +162,53 @@ Out[2]: (0.7071067811865476+0j)*|0b0> + (0.7071067811865476+0j)*|0b11111>
\subsubsection{Graphical States} \subsubsection{Graphical States}
For the graphical state $(V, E, O)$ the list of vertices $V$ can be stored implicitly
by demanding $V = \{0, ..., n - 1\}$. This leaves two components that have to be stored:
The edges $E$ and the vertex operators $O$. Storing the vertex operators is done using
a \lstinline{uint8_t} array. Every local Clifford operator is associated from $0$ to $24$,
their order is
\begin{equation}
\begin{aligned}
&\left(\begin{matrix}\frac{\sqrt{2}}{2} & \frac{\sqrt{2}}{2}\\\frac{\sqrt{2}}{2} & - \frac{\sqrt{2}}{2}\end{matrix}\right),
\left(\begin{matrix}1 & 0\\0 & i\end{matrix}\right),
\left(\begin{matrix}1 & 0\\0 & 1\end{matrix}\right),
\left(\begin{matrix}\frac{\sqrt{2}}{2} & \frac{\sqrt{2}}{2}\\\frac{\sqrt{2} i}{2} & - \frac{\sqrt{2} i}{2}\end{matrix}\right), \\
&\left(\begin{matrix}\frac{\sqrt{2}}{2} & \frac{\sqrt{2} i}{2}\\\frac{\sqrt{2}}{2} & - \frac{\sqrt{2} i}{2}\end{matrix}\right),
\left(\begin{matrix}1 & 0\\0 & -1\end{matrix}\right),
\left(\begin{matrix}\frac{\sqrt{2}}{2} & \frac{\sqrt{2} i}{2}\\\frac{\sqrt{2} i}{2} & \frac{\sqrt{2}}{2}\end{matrix}\right),
\left(\begin{matrix}\frac{\sqrt{2}}{2} & - \frac{\sqrt{2}}{2}\\\frac{\sqrt{2}}{2} & \frac{\sqrt{2}}{2}\end{matrix}\right), \\
&\left(\begin{matrix}1 & 0\\0 & - i\end{matrix}\right),
\left(\begin{matrix}\frac{\sqrt{2}}{2} & - \frac{\sqrt{2}}{2}\\\frac{\sqrt{2} i}{2} & \frac{\sqrt{2} i}{2}\end{matrix}\right),
\left(\begin{matrix}\frac{\sqrt{2}}{2} & - \frac{\sqrt{2} i}{2}\\\frac{\sqrt{2}}{2} & \frac{\sqrt{2} i}{2}\end{matrix}\right),
\left(\begin{matrix}\frac{\sqrt{2}}{2} & - \frac{\sqrt{2} i}{2}\\\frac{\sqrt{2} i}{2} & - \frac{\sqrt{2}}{2}\end{matrix}\right), \\
&\left(\begin{matrix}\frac{1}{2} + \frac{i}{2} & \frac{1}{2} - \frac{i}{2}\\\frac{1}{2} - \frac{i}{2} & \frac{1}{2} + \frac{i}{2}\end{matrix}\right),
\left(\begin{matrix}\frac{\sqrt{2}}{2} & \frac{\sqrt{2}}{2}\\- \frac{\sqrt{2}}{2} & \frac{\sqrt{2}}{2}\end{matrix}\right),
\left(\begin{matrix}0 & 1\\1 & 0\end{matrix}\right),
\left(\begin{matrix}\frac{\sqrt{2}}{2} & \frac{\sqrt{2}}{2}\\- \frac{\sqrt{2} i}{2} & \frac{\sqrt{2} i}{2}\end{matrix}\right), \\
&\left(\begin{matrix}0 & 1\\i & 0\end{matrix}\right),
\left(\begin{matrix}\frac{1}{2} - \frac{i}{2} & \frac{1}{2} + \frac{i}{2}\\- \frac{1}{2} + \frac{i}{2} & \frac{1}{2} + \frac{i}{2}\end{matrix}\right),
\left(\begin{matrix}0 & i\\1 & 0\end{matrix}\right),
\left(\begin{matrix}\frac{\sqrt{2}}{2} & \frac{\sqrt{2} i}{2}\\- \frac{\sqrt{2} i}{2} & - \frac{\sqrt{2}}{2}\end{matrix}\right), \\
&\left(\begin{matrix}\frac{1}{2} - \frac{i}{2} & - \frac{1}{2} + \frac{i}{2}\\- \frac{1}{2} + \frac{i}{2} & - \frac{1}{2} + \frac{i}{2}\end{matrix}\right),
\left(\begin{matrix}0 & -1\\1 & 0\end{matrix}\right),
\left(\begin{matrix}\frac{\sqrt{2}}{2} & - \frac{\sqrt{2}}{2}\\- \frac{\sqrt{2} i}{2} & - \frac{\sqrt{2} i}{2}\end{matrix}\right),
\left(\begin{matrix}\frac{1}{2} - \frac{i}{2} & \frac{i \left(-1 + i\right)}{2}\\- \frac{1}{2} + \frac{i}{2} & \frac{i \left(-1 + i\right)}{2}\end{matrix}\right)
\end{aligned}
\end{equation}
The edges are stored in an adjacency matrix
\begin{equation}
A = (a_{i,j})_{i,j = 0, ..., n-1}
\end{equation}
\begin{equation}
\begin{aligned}
a_{i,j} = \left\{ \begin{array}{c} 1 \mbox{, if } \{i,j\} \in E\\
0 \mbox{, if} \{i,j\} \notin E \end{array}\right.
.
\end{aligned}
\end{equation}
Because it is