From edaa8ac4a4b88a05deeba398167270c71e033f27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Kn=C3=BCttel?= Date: Mon, 16 Mar 2020 12:59:37 +0100 Subject: [PATCH] some work --- thesis/chapters/implementation.tex | 109 ++++++++++++++++++----------- 1 file changed, 69 insertions(+), 40 deletions(-) diff --git a/thesis/chapters/implementation.tex b/thesis/chapters/implementation.tex index 6640705..0ebae8c 100644 --- a/thesis/chapters/implementation.tex +++ b/thesis/chapters/implementation.tex @@ -64,7 +64,7 @@ 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}} +\lstinputlisting[title={Implementation of the Hadamard Gate in C}, language=C, firstline=153, lastline=178, breaklines=true]{../pyqcs/src/pyqcs/gates/implementations/basic_gates.c} A basic set of gates is implemented in PyQCS: @@ -103,8 +103,7 @@ circuits and can be constructing using the built-in generators. The generators take the act-qbit as first argument, parameters such as the control qbit or an angle as second argument: -%\adjustbox{max width=\textwidth}{ - \begin{lstlisting}[language=Python] +\begin{lstlisting}[language=Python, breaklines=true, caption={Using Single Gate Circuits}] In [1]: from pyqcs import CX, CZ, H, R, Z, X ...: from pyqcs import State ...: @@ -114,9 +113,9 @@ In [1]: from pyqcs import CX, CZ, H, R, Z, X ...: bell_state = CX(1, 0) * intermediate_state In [2]: bell_state -Out[2]: (0.7071067811865476+0j)*|0b0> + (0.7071067811865476+0j)*|0b11> +Out[2]: (0.7071067811865476+0j)*|0b0> + + (0.7071067811865476+0j)*|0b11> \end{lstlisting} -%} Large circuits can be constructed using the binary OR operator \lstinline{|} in an analogy to the pipeline operator on many *NIX systems. As usual circuits are @@ -124,7 +123,7 @@ read from left to right similar to pipelines on *NIX systems: %\adjustbox{max width=\textwidth}{ -\begin{lstlisting}[language=Python] +\begin{lstlisting}[language=Python, breaklines=true, caption={Constructing Circuits Using \lstinline{|}}] In [1]: from pyqcs import CX, CZ, H, R, Z, X ...: from pyqcs import State ...: @@ -137,7 +136,8 @@ In [1]: from pyqcs import CX, CZ, H, R, Z, X ...: bell_state = circuit * 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} %} @@ -145,7 +145,7 @@ A quick way to generate circuits programatically is to use the \lstinline{list_t function: %\adjustbox{max width=\textwidth}{ -\begin{lstlisting}[language=Python] +\begin{lstlisting}[language=Python, breaklines=true, caption={Constructing Circuits Using Python Lists}] In [1]: from pyqcs import CX, CZ, H, R, Z, X ...: from pyqcs import State, list_to_circuit ...: @@ -154,7 +154,8 @@ In [1]: from pyqcs import CX, CZ, H, R, Z, X ...: state = (H(0) | circuit_CX) * State.new_zero_state(5) 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} %} @@ -232,26 +233,31 @@ implemented in \lstinline{C} and are exported to python3 in the class \lstinline{RawGraphState}. This class has three main methods to implement the three classes of operations. +% +% +% \begin{description} - \item[\lstinline{RawGraphState.apply_C_L}]{This method - implements local clifford gates. It takes the qbit index and the index - of the local Clifford operator (ranging form $0$ to $23$).} - \item[\lstinline{RawGraphState.apply_CZ}]{Applies the $CZ$ gate to the - state. The first argument is the act-qbit, the second the control - qbit (note that this is just for consistency to the $CX$ gate).} - \item[\lstinline{RawGraphState.measure}]{Using this method one can - measure a qbit. It takes the qbit index as first argument and - a floating point (double precision) random number as second - argument. This random number is used to decide the measurement outcome - in non-deterministic measurements. This method returns either $1$ or $0$ as - a measurement result.} + \item[\hspace{-1em}]{\lstinline{RawGraphState.apply_C_L}\\ + This method implements local clifford gates. It takes the qbit index + and the index of the local Clifford operator (ranging form $0$ to $23$).} + \item[\hspace{-1em}]{\lstinline{RawGraphState.apply_CZ}\\ + Applies the $CZ$ gate to the state. The first argument is the + act-qbit, the second the control qbit (note that this is just for + consistency to the $CX$ gate).} + \item[\hspace{-1em}]{\lstinline{RawGraphState.measure}\\ + Using this method one can measure a qbit. It takes the qbit index + as first argument and a floating point (double precision) random + number as second argument. This random number is used to decide the + measurement outcome in non-deterministic measurements. This method + returns either $1$ or $0$ as a measurement result.} \end{description} Because this way of modifying the state is rather unconvenient and might lead to many -errors the \lstinline{RawGraphState} is wrapped by the pure python class -\lstinline{pyqcs.graph.state.GraphState}. It allows the use of circuits as described -in \ref{ref:pyqcs_circuits} and provides the method \lstinline{GraphState.to_naive_state} -to convert the graphical state to a dense vector state. +errors the \lstinline{RawGraphState} is wrapped by the pure python class\\ +\lstinline{pyqcs.graph.state.GraphState}. It allows the use of circuits as +described in \ref{ref:pyqcs_circuits} and provides the method +\lstinline{GraphState.to_naive_state} to convert the graphical state to a dense +vector state. \subsubsection{Pure C Implementation} @@ -293,12 +299,12 @@ moment this works for dense vectors only. Checking for equality on graphical states has yet to be implemented but can be done in polynomial time \cite{dahlberg_ea2019}. -Writing circuits out by hand can be rather painful. The function +Writing circuits out by hand can be rather painful. The function\\ \lstinline{pyqcs.list_to_circuit} Converts a list of circuits to a circuit. This is particularely helpful in combination with python's \lstinline{listcomp}: -\begin{lstlisting} +\begin{lstlisting}[caption={Generating a Large Circuit Efficiently}] circuit_H = list_to_circuit([H(i) for i in range(nqbits)]) \end{lstlisting} @@ -308,17 +314,30 @@ dense vector simulation. Using the module \lstinline{pyqcs.util.random_graphs} one can generate random graphical states which is more performant than using random circuits. -\subsubsection{Exporting Circuits and Graphical States} +The function \lstinline{pyqcs.util.to_circuit.graph_state_to_circuit} converts +graphical states into circuits (mapping the $\ket{0b0..0}$ to this state. +Using these circuits the graphical state can be copied or converted to a +dense vector state. Further it is a way to optimize circuits to run them on +other simulators. Also the circuits can be exported to \lstinline{qcircuit} code +(see below) which is a relatively readable way to represent graphical states. + +\subsubsection{Exporting and Flattening Circuits} Circuits can be drawn using the \LaTeX package \lstinline{qcircuit}; all circuits in this documents use \lstinline{qcircuit}. To visualize the circuits -built using \lstinline{pyqcs} the function +built using \lstinline{pyqcs} the function\\ \lstinline{pyqcs.util.to_diagram.circuit_to_diagram} can be used to generate \lstinline{qcircuit} code that can be used in \LaTeX documents or exported to PDFs directly. The diagrams produced by this function is not optimized and the diagrams can be unnecessary long. Usually this can be fixed easily by editing the produced code manually. +The circuits constructed using the \lstinline{|} operator have a tree structure +which is rather unconvenient when optimizing circuits or exporting them. +The function \\ +\lstinline{pyqcs.util.flatten.flatten} converts a circuit +to a list of single gate circuits that can be analyzed or exported easily. + \subsection{Performance} \label{ref:performance} @@ -361,7 +380,7 @@ dense vector simulator $S$ can be replaced by $R_\phi$ with the parameter $x$. Using this method circuits are generated and applied both to graphical and dense vector states and the time required to execute the operations \cite{timeit} is measured. The resulting graph can be seen in -\ref{fig:scaling_qbits_linear} and \ref{fig:scaling_qbits_log}. Note in both cases the length of the circuits +Figure \ref{fig:scaling_qbits_linear} and Figure \ref{fig:scaling_qbits_log}. Note in both cases the length of the circuits have been scaled linearely with the amount of qbits and the measured time was divided by the number of qbits: @@ -399,7 +418,7 @@ One should be aware that the gate execution time (the time required to apply a g to the state) highly depends on the state it is applied to. For the dense vector simulator and CHP this is not true: Gate execution time is constant for all gates and states. Because the graphical simulator has to toggle neighbourhoods the -gate execution time of the $CZ$ gate varies greatly. The plot \ref{fig:scaling_circuits_linear} +gate execution time of the $CZ$ gate varies greatly. The plot Figure \ref{fig:scaling_circuits_linear} shows the circuit execution time for two different numbers of qbits. One can observe three regimes: @@ -426,6 +445,13 @@ regimes: \label{fig:graph_low_linear_regime} \end{figure} +\begin{figure} + \centering + \includegraphics[width=\linewidth]{../performance/regimes/graph_intermediate_regime.png} + \caption[Typical Graphical State in the Intermediate Regime]{Typical Graphical State in the Intermediate Regime} + \label{fig:graph_intermediate_regime} +\end{figure} + \begin{figure} \centering \includegraphics[width=\linewidth]{../performance/regimes/graph_high_linear_regime.png} @@ -433,14 +459,17 @@ regimes: \label{fig:graph_high_linear_regime} \end{figure} -These two regimes can be explained when considering the graphical states that typical live in these -regimes. With increased circuit length the amount of edges increases which makes toggling neighbourhoods -harder. Two graphs from the low-linear and high-linear regime can be seen in \ref{fig:graph_low_linear_regime} -and \ref{fig:graph_high_linear_regime}. The latter is hardly visible; this is due to the great amount of -edges in this regime. Further these two regimes are only visibly for $n>30$ qbits so choosing smaller graphs is -not possible. +These two regimes can be explained when considering the graphical states that +typical live in these regimes. With increased circuit length the amount of +edges increases which makes toggling neighbourhoods harder. Graphs from the +low-linear, intermediate and high-linear regime can be seen in +Figure \ref{fig:graph_low_linear_regime}, Figure \ref{fig:graph_intermediate_regime} and +Figure \ref{fig:graph_high_linear_regime}. The latter is hardly visible; this is due +to the great amount of edges in this regime. Further these two regimes are only +visibly for $n>30$ qbits so choosing smaller graphs is not possible. -Because states with more qbits reach the intermediate regime at higher circuit lengths it is important to -account for this virtual performance boost when comparing with other simulation methods. This explains -why the circuit length in \ref{fig:scaling_qbits_linear} had to be scaled with the qbit number. +Because states with more qbits reach the intermediate regime at higher circuit +lengths it is important to account for this virtual performance boost when +comparing with other simulation methods. This explains why the circuit length +in Figure \ref{fig:scaling_qbits_linear} had to be scaled with the qbit number.