diff --git a/projectq/backends/_circuits/_drawer.py b/projectq/backends/_circuits/_drawer.py index 269f592a2..85aee3dac 100755 --- a/projectq/backends/_circuits/_drawer.py +++ b/projectq/backends/_circuits/_drawer.py @@ -11,7 +11,6 @@ # 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. - """ Contains a compiler engine which generates TikZ Latex code describing the circuit. @@ -42,9 +41,9 @@ def __init__(self, gate, lines, ctrl_lines): self.id = -1 def __eq__(self, other): - return (self.gate == other.gate and self.lines == other.lines and - self.ctrl_lines == other.ctrl_lines and - self.id == other.id) + return (self.gate == other.gate and self.lines == other.lines + and self.ctrl_lines == other.ctrl_lines + and self.id == other.id) def __ne__(self, other): return not self.__eq__(other) @@ -153,6 +152,9 @@ def __init__(self, accept_input=False, default_measure=0): self._free_lines = [] self._map = dict() + # Order in which qubit lines are drawn + self._drawing_order = [] + def is_available(self, cmd): """ Specialized implementation of is_available: Returns True if the @@ -190,7 +192,7 @@ def set_qubit_locations(self, id_to_loc): raise RuntimeError("set_qubit_locations() has to be called before" " applying gates!") - for k in range(min(id_to_loc), max(id_to_loc)+1): + for k in range(min(id_to_loc), max(id_to_loc) + 1): if k not in id_to_loc: raise RuntimeError("set_qubit_locations(): Invalid id_to_loc " "mapping provided. All ids in the provided" @@ -221,7 +223,7 @@ def _print_cmd(self, cmd): self._free_lines.append(qubit_id) if self.is_last_engine and cmd.gate == Measure: - assert(get_control_count(cmd) == 0) + assert (get_control_count(cmd) == 0) for qureg in cmd.qubits: for qubit in qureg: if self._accept_input: @@ -244,7 +246,9 @@ def _print_cmd(self, cmd): for l in all_lines: self._qubit_lines[l].append(item) - def get_latex(self): + self._drawing_order.append(all_lines[0]) + + def get_latex(self, ordered=False, draw_gates_in_parallel=True): """ Return the latex document string representing the circuit. @@ -256,6 +260,12 @@ def get_latex(self): python3 my_circuit.py | pdflatex where my_circuit.py calls this function and prints it to the terminal. + + Args: + ordered(bool): flag if the gates should be drawn in the order they + were added to the circuit + draw_gates_in_parallel(bool): flag if parallel gates should be drawn + parallel (True), or not (False) """ qubit_lines = dict() @@ -271,10 +281,13 @@ def get_latex(self): new_cmd.id = cmd.lines[0] qubit_lines[new_line].append(new_cmd) - circuit = [] - for lines in qubit_lines: - circuit.append(qubit_lines[lines]) - return to_latex(qubit_lines) + drawing_order = None + if ordered: + drawing_order = self._drawing_order + + return to_latex(qubit_lines, + drawing_order=drawing_order, + draw_gates_in_parallel=draw_gates_in_parallel) def receive(self, command_list): """ diff --git a/projectq/backends/_circuits/_drawer_test.py b/projectq/backends/_circuits/_drawer_test.py index 7df4bd0ee..b73513b64 100755 --- a/projectq/backends/_circuits/_drawer_test.py +++ b/projectq/backends/_circuits/_drawer_test.py @@ -11,7 +11,6 @@ # 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. - """ Tests for projectq.backends.circuits._drawer.py. """ @@ -20,19 +19,17 @@ from projectq import MainEngine from projectq.cengines import LastEngineException -from projectq.ops import (H, - X, - CNOT, - Measure) +from projectq.ops import (H, X, CNOT, Measure) from projectq.meta import Control import projectq.backends._circuits._drawer as _drawer from projectq.backends._circuits._drawer import CircuitItem, CircuitDrawer -def test_drawer_getlatex(): +@pytest.mark.parametrize("ordered", [False, True]) +def test_drawer_getlatex(ordered): old_latex = _drawer.to_latex - _drawer.to_latex = lambda x: x + _drawer.to_latex = lambda x, drawing_order, draw_gates_in_parallel: x drawer = CircuitDrawer() drawer.set_qubit_locations({0: 1, 1: 0}) @@ -46,13 +43,13 @@ def test_drawer_getlatex(): X | qureg[0] CNOT | (qureg[0], qureg[1]) - lines = drawer2.get_latex() + lines = drawer2.get_latex(ordered=ordered) assert len(lines) == 2 assert len(lines[0]) == 4 assert len(lines[1]) == 3 # check if it was sent on correctly: - lines = drawer.get_latex() + lines = drawer.get_latex(ordered=ordered) assert len(lines) == 2 assert len(lines[0]) == 3 assert len(lines[1]) == 4 diff --git a/projectq/backends/_circuits/_to_latex.py b/projectq/backends/_circuits/_to_latex.py index 1c028acfc..385f3d3f3 100755 --- a/projectq/backends/_circuits/_to_latex.py +++ b/projectq/backends/_circuits/_to_latex.py @@ -17,7 +17,7 @@ Measure, SqrtSwap, Swap, X, Z) -def to_latex(circuit): +def to_latex(circuit, drawing_order=None, draw_gates_in_parallel=True): """ Translates a given circuit to a TikZ picture in a Latex document. @@ -38,8 +38,12 @@ class name string as a key. Every gate can have its own width, height, pre function, and written using write_settings(). Args: - circuit (list>): Each qubit line is a list of + circuit (list): Each qubit line is a list of CircuitItem objects, i.e., in circuit[line]. + drawing_order (list): A list of qubit lines from which + the gates to be read from + draw_gates_in_parallel (bool): If gates should (False) + or not (True) be parallel in the circuit Returns: tex_doc_str (string): Latex document string which can be compiled @@ -57,7 +61,10 @@ class name string as a key. Every gate can have its own width, height, pre settings = write_settings(get_default_settings()) text = _header(settings) - text += _body(circuit, settings) + text += _body(circuit, + settings, + drawing_order, + draw_gates_in_parallel=draw_gates_in_parallel) text += _footer(settings) return text @@ -83,39 +90,88 @@ def get_default_settings(): """ settings = dict() settings['gate_shadow'] = True - settings['lines'] = ({'style': 'very thin', 'double_classical': True, - 'init_quantum': True, 'double_lines_sep': .04}) - settings['gates'] = ({'HGate': {'width': .5, 'offset': .3, - 'pre_offset': .1}, - 'XGate': {'width': .35, 'height': .35, - 'offset': .1}, - 'SqrtXGate': {'width': .7, 'offset': .3, - 'pre_offset': .1}, - 'SwapGate': {'width': .35, 'height': .35, - 'offset': .1}, - 'SqrtSwapGate': {'width': .35, 'height': .35, - 'offset': .1}, - 'Rx': {'width': 1., 'height': .8, 'pre_offset': .2, - 'offset': .3}, - 'Ry': {'width': 1., 'height': .8, 'pre_offset': .2, - 'offset': .3}, - 'Rz': {'width': 1., 'height': .8, 'pre_offset': .2, - 'offset': .3}, - 'Ph': {'width': 1., 'height': .8, 'pre_offset': .2, - 'offset': .3}, - 'EntangleGate': {'width': 1.8, 'offset': .2, - 'pre_offset': .2}, - 'DeallocateQubitGate': {'height': .15, 'offset': .2, - 'width': .2, - 'pre_offset': .1}, - 'AllocateQubitGate': {'height': .15, 'width': .2, - 'offset': .1, - 'pre_offset': .1, - 'draw_id': False, - 'allocate_at_zero': False}, - 'MeasureGate': {'width': 0.75, 'offset': .2, - 'height': .5, 'pre_offset': .2} - }) + settings['lines'] = ({ + 'style': 'very thin', + 'double_classical': True, + 'init_quantum': True, + 'double_lines_sep': .04 + }) + settings['gates'] = ({ + 'HGate': { + 'width': .5, + 'offset': .3, + 'pre_offset': .1 + }, + 'XGate': { + 'width': .35, + 'height': .35, + 'offset': .1 + }, + 'SqrtXGate': { + 'width': .7, + 'offset': .3, + 'pre_offset': .1 + }, + 'SwapGate': { + 'width': .35, + 'height': .35, + 'offset': .1 + }, + 'SqrtSwapGate': { + 'width': .35, + 'height': .35, + 'offset': .1 + }, + 'Rx': { + 'width': 1., + 'height': .8, + 'pre_offset': .2, + 'offset': .3 + }, + 'Ry': { + 'width': 1., + 'height': .8, + 'pre_offset': .2, + 'offset': .3 + }, + 'Rz': { + 'width': 1., + 'height': .8, + 'pre_offset': .2, + 'offset': .3 + }, + 'Ph': { + 'width': 1., + 'height': .8, + 'pre_offset': .2, + 'offset': .3 + }, + 'EntangleGate': { + 'width': 1.8, + 'offset': .2, + 'pre_offset': .2 + }, + 'DeallocateQubitGate': { + 'height': .15, + 'offset': .2, + 'width': .2, + 'pre_offset': .1 + }, + 'AllocateQubitGate': { + 'height': .15, + 'width': .2, + 'offset': .1, + 'pre_offset': .1, + 'draw_id': False, + 'allocate_at_zero': False + }, + 'MeasureGate': { + 'width': 0.75, + 'offset': .2, + 'height': .5, + 'pre_offset': .2 + } + }) settings['control'] = {'size': .1, 'shadow': False} return settings @@ -153,8 +209,7 @@ def _header(settings): gate_style += ("\\tikzstyle{operator}=[basic,minimum size=1.5em]\n" "\\tikzstyle{phase}=[fill=black,shape=circle," + "minimum size={}".format(settings['control']['size']) + - "cm,inner sep=0pt,outer sep=0pt,draw=black" - ) + "cm,inner sep=0pt,outer sep=0pt,draw=black") if settings['control']['shadow']: gate_style += ",basicshadow" gate_style += ("]\n\\tikzstyle{none}=[inner sep=0pt,outer sep=-.5pt," @@ -167,9 +222,9 @@ def _header(settings): x_gate_radius = min(settings['gates']['XGate']['height'], settings['gates']['XGate']['width']) gate_style += ("{x_rad}cm,minimum width={x_rad}cm,inner sep=-1pt," - "{linestyle}]\n" - ).format(x_rad=x_gate_radius, - linestyle=settings['lines']['style']) + "{linestyle}]\n").format( + x_rad=x_gate_radius, + linestyle=settings['lines']['style']) if settings['gate_shadow']: gate_style += ("\\tikzset{\nshadowed/.style={preaction={transform " "canvas={shift={(0.5pt,-0.5pt)}}, draw=gray, opacity=" @@ -182,13 +237,19 @@ def _header(settings): return packages + init + gate_style + edge_style -def _body(circuit, settings): +def _body(circuit, settings, drawing_order=None, draw_gates_in_parallel=True): """ Return the body of the Latex document, including the entire circuit in TikZ format. Args: circuit (list>): Circuit to draw. + settings: Dictionary of settings to use for the TikZ image. + drawing_order: A list of circuit wires from where to read + one gate command. + draw_gates_in_parallel: Are the gate/commands occupying a + single time step in the circuit diagram? For example, False means + that gates can be parallel in the circuit. Returns: tex_str (string): Latex string to draw the entire circuit. @@ -196,8 +257,19 @@ def _body(circuit, settings): code = [] conv = _Circ2Tikz(settings, len(circuit)) - for line in range(len(circuit)): - code.append(conv.to_tikz(line, circuit)) + + to_where = None + if drawing_order is None: + drawing_order = list(range(len(circuit))) + else: + to_where = 1 + + for line in drawing_order: + code.append( + conv.to_tikz(line, + circuit, + end=to_where, + draw_gates_in_parallel=draw_gates_in_parallel)) return "".join(code) @@ -219,7 +291,6 @@ class _Circ2Tikz(object): It uses the settings dictionary for gate offsets, sizes, spacing, ... """ - def __init__(self, settings, num_lines): """ Initialize a circuit to latex converter object. @@ -234,7 +305,7 @@ def __init__(self, settings, num_lines): self.op_count = [0] * num_lines self.is_quantum = [settings['lines']['init_quantum']] * num_lines - def to_tikz(self, line, circuit, end=None): + def to_tikz(self, line, circuit, end=None, draw_gates_in_parallel=True): """ Generate the TikZ code for one line of the circuit up to a certain gate. @@ -247,6 +318,7 @@ def to_tikz(self, line, circuit, end=None): line (int): Line to generate the TikZ code for. circuit (list>): The circuit to draw. end (int): Gate index to stop at (for recursion). + draw_gates_in_parallel (bool): True or False for how to place gates Returns: tikz_code (string): TikZ code representing the current qubit line @@ -272,19 +344,23 @@ def to_tikz(self, line, circuit, end=None): gate_idx += 1 tikz_code.append(self.to_tikz(l, circuit, gate_idx)) + # we are taking care of gate 0 (the current one) circuit[l] = circuit[l][1:] all_lines = lines + ctrl_lines - pos = max([self.pos[l] for l in range(min(all_lines), - max(all_lines) + 1)]) + pos = max([ + self.pos[l] for l in range(min(all_lines), + max(all_lines) + 1) + ]) for l in range(min(all_lines), max(all_lines) + 1): self.pos[l] = pos + self._gate_pre_offset(gate) connections = "" for l in all_lines: connections += self._line(self.op_count[l] - 1, - self.op_count[l], line=l) + self.op_count[l], + line=l) add_str = "" if gate == X: # draw NOT-gate with controls @@ -298,7 +374,8 @@ def to_tikz(self, line, circuit, end=None): elif gate == Swap: add_str = self._swap_gate(lines, ctrl_lines) elif gate == SqrtSwap: - add_str = self._sqrtswap_gate(lines, ctrl_lines, + add_str = self._sqrtswap_gate(lines, + ctrl_lines, daggered=False) elif gate == get_inverse(SqrtSwap): add_str = self._sqrtswap_gate(lines, ctrl_lines, daggered=True) @@ -319,10 +396,13 @@ def to_tikz(self, line, circuit, end=None): "cm,xshift=-{shift2}cm]{op}.east);\n" "\\draw[edgestyle] ([yshift=-{shift1}cm]{op}." "center) to ([yshift=-{shift2}cm,xshift=-" - "{shift1}cm]{op}.north east);" - ).format(op=op, pos=self.pos[l], line=l, - shift0=shift0, shift1=shift1, - shift2=shift2) + "{shift1}cm]{op}.north east);").format( + op=op, + pos=self.pos[l], + line=l, + shift0=shift0, + shift1=shift1, + shift2=shift2) self.op_count[l] += 1 self.pos[l] += (self._gate_width(gate) + self._gate_offset(gate)) @@ -336,15 +416,15 @@ def to_tikz(self, line, circuit, end=None): xpos = self.pos[line] try: if (self.settings['gates']['AllocateQubitGate'] - ['allocate_at_zero']): + ['allocate_at_zero']): self.pos[line] -= self._gate_pre_offset(gate) xpos = self._gate_pre_offset(gate) except KeyError: pass - self.pos[line] = max(xpos + self._gate_offset(gate) + - self._gate_width(gate), self.pos[line]) - add_str = add_str.format(self._op(line), xpos, line, - id_str) + self.pos[line] = max( + xpos + self._gate_offset(gate) + self._gate_width(gate), + self.pos[line]) + add_str = add_str.format(self._op(line), xpos, line, id_str) self.op_count[line] += 1 self.is_quantum[line] = self.settings['lines']['init_quantum'] elif gate == Deallocate: @@ -353,9 +433,10 @@ def to_tikz(self, line, circuit, end=None): add_str = "\n\\node[none] ({}) at ({},-{}) {{}};" add_str = add_str.format(op, self.pos[line], line) yshift = str(self._gate_height(gate)) + "cm]" - add_str += ("\n\\draw ([yshift={yshift}{op}.center) edge " - "[edgestyle] ([yshift=-{yshift}{op}.center);" - ).format(op=op, yshift=yshift) + add_str += ( + "\n\\draw ([yshift={yshift}{op}.center) edge " + "[edgestyle] ([yshift=-{yshift}{op}.center);").format( + op=op, yshift=yshift) self.op_count[line] += 1 self.pos[line] += (self._gate_width(gate) + self._gate_offset(gate)) @@ -370,6 +451,11 @@ def to_tikz(self, line, circuit, end=None): if not gate == Allocate: tikz_code.append(connections) + if not draw_gates_in_parallel: + for l in range(len(self.pos)): + if l != line: + self.pos[l] = self.pos[line] + circuit[line] = circuit[line][end:] return "".join(tikz_code) @@ -402,7 +488,7 @@ def _sqrtswap_gate(self, lines, ctrl_lines, daggered): ctrl_lines (list): List of qubit lines which act as controls. daggered (bool): Show the daggered one if True. """ - assert(len(lines) == 2) # sqrt swap gate acts on 2 qubits + assert (len(lines) == 2) # sqrt swap gate acts on 2 qubits delta_pos = self._gate_offset(SqrtSwap) gate_width = self._gate_width(SqrtSwap) lines.sort() @@ -420,20 +506,27 @@ def _sqrtswap_gate(self, lines, ctrl_lines, daggered): swap_style += ",shadowed" gate_str += ("\n\\node[swapstyle] ({op}) at ({pos},-{line}) {{}};" "\n\\draw[{swap_style}] ({s1})--({s2});\n" - "\\draw[{swap_style}] ({s3})--({s4});" - ).format(op=op, s1=s1, s2=s2, s3=s3, s4=s4, - line=line, pos=self.pos[line], - swap_style=swap_style) + "\\draw[{swap_style}] ({s3})--({s4});").format( + op=op, + s1=s1, + s2=s2, + s3=s3, + s4=s4, + line=line, + pos=self.pos[line], + swap_style=swap_style) # add a circled 1/2 midpoint = (lines[0] + lines[1]) / 2. pos = self.pos[lines[0]] - op_mid = "line{}_gate{}".format( - '{}-{}'.format(*lines), self.op_count[lines[0]]) + op_mid = "line{}_gate{}".format('{}-{}'.format(*lines), + self.op_count[lines[0]]) gate_str += ("\n\\node[xstyle] ({op}) at ({pos},-{line})\ - {{\\scriptsize $\\frac{{1}}{{2}}{dagger}$}};" - ).format(op=op_mid, line=midpoint, pos=pos, - dagger='^{{\\dagger}}' if daggered else '') + {{\\scriptsize $\\frac{{1}}{{2}}{dagger}$}};").format( + op=op_mid, + line=midpoint, + pos=pos, + dagger='^{{\\dagger}}' if daggered else '') # add two vertical lines to connect circled 1/2 gate_str += "\n\\draw ({}) edge[edgestyle] ({});".format( @@ -468,7 +561,7 @@ def _swap_gate(self, lines, ctrl_lines): ctrl_lines (list): List of qubit lines which act as controls. """ - assert(len(lines) == 2) # swap gate acts on 2 qubits + assert (len(lines) == 2) # swap gate acts on 2 qubits delta_pos = self._gate_offset(Swap) gate_width = self._gate_width(Swap) lines.sort() @@ -486,10 +579,15 @@ def _swap_gate(self, lines, ctrl_lines): swap_style += ",shadowed" gate_str += ("\n\\node[swapstyle] ({op}) at ({pos},-{line}) {{}};" "\n\\draw[{swap_style}] ({s1})--({s2});\n" - "\\draw[{swap_style}] ({s3})--({s4});" - ).format(op=op, s1=s1, s2=s2, s3=s3, s4=s4, - line=line, pos=self.pos[line], - swap_style=swap_style) + "\\draw[{swap_style}] ({s3})--({s4});").format( + op=op, + s1=s1, + s2=s2, + s3=s3, + s4=s4, + line=line, + pos=self.pos[line], + swap_style=swap_style) gate_str += self._line(lines[0], lines[1]) if len(ctrl_lines) > 0: @@ -519,15 +617,15 @@ def _x_gate(self, lines, ctrl_lines): ctrl_lines (list): List of qubit lines which act as controls. """ - assert(len(lines) == 1) # NOT gate only acts on 1 qubit + assert (len(lines) == 1) # NOT gate only acts on 1 qubit line = lines[0] delta_pos = self._gate_offset(X) gate_width = self._gate_width(X) op = self._op(line) gate_str = ("\n\\node[xstyle] ({op}) at ({pos},-{line}) {{}};\n\\draw" "[edgestyle] ({op}.north)--({op}.south);\n\\draw" - "[edgestyle] ({op}.west)--({op}.east);" - ).format(op=op, line=line, pos=self.pos[line]) + "[edgestyle] ({op}.west)--({op}.east);").format( + op=op, line=line, pos=self.pos[line]) if len(ctrl_lines) > 0: for ctrl in ctrl_lines: @@ -705,10 +803,16 @@ def _line(self, p1, p2, double=False, line=None): line_sep = self.settings['lines']['double_lines_sep'] shift1 = shift.format(line_sep / 2.) shift2 = shift.format(-line_sep / 2.) - edges_str = edge_str.format(shift=shift1, op1=op1, op2=op2, - loc1=loc1, loc2=loc2) - edges_str += edge_str.format(shift=shift2, op1=op1, op2=op2, - loc1=loc1, loc2=loc2) + edges_str = edge_str.format(shift=shift1, + op1=op1, + op2=op2, + loc1=loc1, + loc2=loc2) + edges_str += edge_str.format(shift=shift2, + op1=op1, + op2=op2, + loc1=loc1, + loc2=loc2) return edges_str def _regular_gate(self, gate, lines, ctrl_lines): @@ -744,23 +848,24 @@ def _regular_gate(self, gate, lines, ctrl_lines): for l in lines: node1 = node_str.format(self._op(l), pos, l) node2 = ("\n\\node[none,minimum height={}cm,outer sep=0] ({}) at" - " ({},-{}) {{}};" - ).format(gate_height, self._op(l, offset=1), - pos + gate_width / 2., l) - node3 = node_str.format(self._op(l, offset=2), - pos + gate_width, l) + " ({},-{}) {{}};").format(gate_height, + self._op(l, offset=1), + pos + gate_width / 2., l) + node3 = node_str.format(self._op(l, offset=2), pos + gate_width, l) tex_str += node1 + node2 + node3 if l not in gate_lines: - tex_str += self._line(self.op_count[l] - 1, self.op_count[l], + tex_str += self._line(self.op_count[l] - 1, + self.op_count[l], line=l) tex_str += ("\n\\draw[operator,edgestyle,outer sep={width}cm] ([" "yshift={half_height}cm]{op1}) rectangle ([yshift=-" - "{half_height}cm]{op2}) node[pos=.5] {{{name}}};" - ).format(width=gate_width, op1=self._op(imin), - op2=self._op(imax, offset=2), - half_height=.5 * gate_height, - name=name) + "{half_height}cm]{op2}) node[pos=.5] {{{name}}};").format( + width=gate_width, + op1=self._op(imin), + op2=self._op(imax, offset=2), + half_height=.5 * gate_height, + name=name) for l in lines: self.pos[l] = pos + gate_width / 2. diff --git a/projectq/backends/_circuits/_to_latex_test.py b/projectq/backends/_circuits/_to_latex_test.py index bf26b9923..c993bcf2e 100755 --- a/projectq/backends/_circuits/_to_latex_test.py +++ b/projectq/backends/_circuits/_to_latex_test.py @@ -11,7 +11,6 @@ # 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. - """ Tests for projectq.backends._circuits._to_latex.py. """ @@ -22,18 +21,19 @@ from projectq import MainEngine from projectq.cengines import LastEngineException -from projectq.ops import (BasicGate, - H, - X, - CNOT, - Measure, - Z, - Swap, - SqrtX, - SqrtSwap, - C, - get_inverse, - ) +from projectq.ops import ( + BasicGate, + H, + X, + CNOT, + Measure, + Z, + Swap, + SqrtX, + SqrtSwap, + C, + get_inverse, +) from projectq.meta import Control from projectq.backends import CircuitDrawer @@ -47,7 +47,7 @@ def test_tolatex(): old_footer = _to_latex._footer _to_latex._header = lambda x: "H" - _to_latex._body = lambda x, y: x + _to_latex._body = lambda x, settings, drawing_order, draw_gates_in_parallel: x _to_latex._footer = lambda x: "F" latex = _to_latex.to_latex("B") @@ -68,11 +68,26 @@ def test_default_settings(): def test_header(): - settings = {'gate_shadow': False, 'control': {'shadow': False, 'size': 0}, - 'gates': {'MeasureGate': {'height': 0, 'width': 0}, - 'XGate': {'height': 1, 'width': .5} - }, - 'lines': {'style': 'my_style'}} + settings = { + 'gate_shadow': False, + 'control': { + 'shadow': False, + 'size': 0 + }, + 'gates': { + 'MeasureGate': { + 'height': 0, + 'width': 0 + }, + 'XGate': { + 'height': 1, + 'width': .5 + } + }, + 'lines': { + 'style': 'my_style' + } + } header = _to_latex._header(settings) assert 'minimum' in header @@ -104,7 +119,7 @@ def test_large_gates(): drawer = _drawer.CircuitDrawer() eng = MainEngine(drawer, []) old_tolatex = _drawer.to_latex - _drawer.to_latex = lambda x: x + _drawer.to_latex = lambda x, drawing_order, draw_gates_in_parallel: x qubit1 = eng.allocate_qubit() qubit2 = eng.allocate_qubit() @@ -136,7 +151,7 @@ def test_body(): drawer = _drawer.CircuitDrawer() eng = MainEngine(drawer, []) old_tolatex = _drawer.to_latex - _drawer.to_latex = lambda x: x + _drawer.to_latex = lambda x, drawing_order, draw_gates_in_parallel: x qubit1 = eng.allocate_qubit() qubit2 = eng.allocate_qubit() @@ -181,12 +196,135 @@ def test_body(): assert code.count("{{{}}}".format(str(Z))) == 1 # 1 Z gate assert code.count("{red}") == 3 +def test_body_with_drawing_order_and_gates_parallel(): + drawer = _drawer.CircuitDrawer() + eng = MainEngine(drawer, []) + old_tolatex = _drawer.to_latex + _drawer.to_latex = lambda x, drawing_order, draw_gates_in_parallel: x + + qubit1 = eng.allocate_qubit() + qubit2 = eng.allocate_qubit() + qubit3 = eng.allocate_qubit() + + H | qubit1 + H | qubit2 + H | qubit3 + CNOT | (qubit1, qubit3) + + # replicates the above order + order = [0, 1, 2, # initializations + 0, 1, 2, # H1, H3, H2 + 0 # CNOT + ] + + del qubit1 + eng.flush() + + circuit_lines = drawer.get_latex() + _drawer.to_latex = old_tolatex + + settings = _to_latex.get_default_settings() + settings['gates']['AllocateQubitGate']['draw_id'] = True + code = _to_latex._body(circuit_lines, settings, + drawing_order=order, + draw_gates_in_parallel=True) + + # there are three Hadamards in parallel + assert code.count("node[pos=.5] {H}") == 3 + + # line1_gate0 is initialisation + # line1_gate1 is empty + # line1_gate2 is for Hadamard on line1 + # line1_gate3 is empty + # XOR of CNOT is node[xstyle] (line1_gate4) + assert code.count("node[xstyle] (line2_gate4)") == 1 + + # and the CNOT is at position 1.4, because of the offsets + assert code.count("node[phase] (line0_gate4) at (1.4") == 1 + assert code.count("node[xstyle] (line2_gate4) at (1.4") == 1 + + +def test_body_with_drawing_order_and_gates_not_parallel(): + drawer = _drawer.CircuitDrawer() + eng = MainEngine(drawer, []) + old_tolatex = _drawer.to_latex + _drawer.to_latex = lambda x, drawing_order, draw_gates_in_parallel: x + + qubit1 = eng.allocate_qubit() + qubit2 = eng.allocate_qubit() + qubit3 = eng.allocate_qubit() + + H | qubit1 + H | qubit2 + H | qubit3 + CNOT | (qubit1, qubit3) + + # replicates the above order + order = [0, 1, 2, # initializations + 0, 1, 2, # H1, H3, H2 + 0 # CNOT + ] + + del qubit1 + eng.flush() + + circuit_lines = drawer.get_latex() + _drawer.to_latex = old_tolatex + + settings = _to_latex.get_default_settings() + settings['gates']['AllocateQubitGate']['draw_id'] = True + code = _to_latex._body(circuit_lines, settings, + drawing_order=order, + draw_gates_in_parallel=False) + + # and the CNOT is at position 4.0, because of the offsets + # which are 0.5 * 3 * 2 (due to three Hadamards) + the initialisations + assert code.count("node[phase] (line0_gate4) at (4.0,-0)") == 1 + assert code.count("node[xstyle] (line2_gate4) at (4.0,-2)") == 1 + +def test_body_without_drawing_order_and_gates_not_parallel(): + drawer = _drawer.CircuitDrawer() + eng = MainEngine(drawer, []) + old_tolatex = _drawer.to_latex + _drawer.to_latex = lambda x, drawing_order, draw_gates_in_parallel: x + + qubit1 = eng.allocate_qubit() + qubit2 = eng.allocate_qubit() + qubit3 = eng.allocate_qubit() + + H | qubit1 + H | qubit2 + H | qubit3 + CNOT | (qubit1, qubit3) + + # replicates the above order + order = [0, 1, 2, # initializations + 0, 1, 2, # H1, H3, H2 + 0 # CNOT + ] + + del qubit1 + eng.flush() + + circuit_lines = drawer.get_latex() + _drawer.to_latex = old_tolatex + + settings = _to_latex.get_default_settings() + settings['gates']['AllocateQubitGate']['draw_id'] = True + code = _to_latex._body(circuit_lines, settings, + draw_gates_in_parallel=False) + + # line1_gate1 is after the cnot line2_gate_4 + idx1 = code.find("node[xstyle] (line2_gate4)") + idx2 = code.find("node[none] (line1_gate1)") + assert idx1 < idx2 + def test_qubit_allocations_at_zero(): drawer = _drawer.CircuitDrawer() eng = MainEngine(drawer, []) old_tolatex = _drawer.to_latex - _drawer.to_latex = lambda x: x + _drawer.to_latex = lambda x, drawing_order, draw_gates_in_parallel: x a = eng.allocate_qureg(4) @@ -219,7 +357,7 @@ def test_qubit_lines_classicalvsquantum1(): drawer = _drawer.CircuitDrawer() eng = MainEngine(drawer, []) old_tolatex = _drawer.to_latex - _drawer.to_latex = lambda x: x + _drawer.to_latex = lambda x, drawing_order, draw_gates_in_parallel: x qubit1 = eng.allocate_qubit()