<jupyter_start><jupyter_text>Getting StartedWe start by importing the library and configuring matplotlib to display the figures in a nice way.<jupyter_code>import sys; sys.path.insert(0, '..')
import random
import pyzx as zx<jupyter_output><empty_output><jupyter_text>Then we can create a new random Clifford circuit and visualise it.<jupyter_code>qubit_amount = 4
depth = 15
random.seed(1337)
circ = zx.generate.cliffords(qubit_amount, depth)
zx.draw_matplotlib(circ,labels=True,h_edge_draw='box')<jupyter_output><empty_output><jupyter_text>The green and red nodes represent Z- and X-phase gates respectively, the yellow boxes are Hadamard gates, and the vertical lines going between two different colored nodes are CNOT gates.Internally this circuit is represented as a graph:<jupyter_code>print(circ)
print("All edges: ", list(circ.edges()))
print("\nThe neighbors of a particular vertex: ", list(circ.neighbors(14)))<jupyter_output><empty_output><jupyter_text>Using this graph representation we can use the rules of the ZX-calculus to simplify it:<jupyter_code>g = circ.copy()
zx.clifford_simp(g)
g.normalize() # Reposition nodes horizontally to look nicer
zx.draw(g)<jupyter_output><empty_output><jupyter_text>The blue lines represent edges that have a Hadamard gate on them. Let's try to see in a bit more detail what goes into rewriting this circuit.<jupyter_code>from ipywidgets import widgets
from IPython.display import display, Markdown

from pyzx.simplify import clifford_iter

graph = circ.copy()
graphs = [zx.draw_matplotlib(graph)]
names = ["start"]
for gs, n in clifford_iter(graph):
    graphs.append(zx.draw_matplotlib(gs))
    names.append(n)
zx.drawing.pack_circuit_nf(gs,'grg')
graphs.append(zx.draw_matplotlib(gs))
names.append("reposition")

def plotter(rewrite):
    display(Markdown("Rewrite step: " + names[rewrite]))
    display(graphs[rewrite])

w = widgets.interactive(plotter, rewrite=(0,len(graphs)-1))
slider = w.children[0]
slider.layout.width = "{!s}px".format(min(800,50*len(graphs)))
output = w.children[-1]
output.layout.height = "{!s}px".format(200+qubit_amount*20)
slider.value = 0
w<jupyter_output><empty_output><jupyter_text>As you can see, the first few steps are **spider fusion**, where adjacent red nodes and adjacent green nodes are fused together (and their phases added). The next step is to convert all red nodes to green nodes by changing regular edges to hadamard edges.Now come the steps that do most of the work. These steps are a round of **pivots** followed by **local complementations** and then a round of pivots again. The local complementation rules looks for green nodes with a phase of $\frac\pi2$ or $\frac{3\pi}2$. By doing a local complementation on this node we can remove the node from the graph. This process is done until no more suitable candidates are found. A pivot consists of a series of three local complementations that is done on two neighboring vertices that have a zero or $\pi$ phase. A pivot move allows us to remove these two nodes.Finally we do a round of **identity** simplification which removes phaseless nodes that are connected to precisely two neighbors.**Note**: Depending on the circuit that was generated, any of these steps might not have been done. Even though this graph is a lot compacter than the one we started out with, it no longer looks like a circuit. To fix this we need to be clever and *extract* a circuit from the ZX-diagram:<jupyter_code>circ2 = g.copy()
circ2.normalize()
circ2 = zx.extract_circuit(circ2)
zx.draw(circ2)<jupyter_output><empty_output><jupyter_text>To verify that this circuit is still equal to the original circuit, we can transform them into numpy tensors and compare these tensors for equality:<jupyter_code>t1 = circ.to_tensor()
t2 = circ2.to_tensor()
# This checks whether t1 and t2 are equal up to some number: t1 == z*t2 for some complex number z
zx.compare_tensors(t1,t2,preserve_scalar=False)<jupyter_output><empty_output><jupyter_text>We can represent the circuit in one of several quantum circuit description languages, such as that of QUIPPER:<jupyter_code>print(circ2.to_quipper())<jupyter_output><empty_output><jupyter_text>Now lets try the same thing with a more complicated Clifford+T circuit.<jupyter_code>qubit_amount = 6
depth = 70
random.seed(1338)
circ = zx.generate.cliffordT(qubit_amount, depth,p_t=0.2)
zx.draw(circ)
g = circ.copy()
zx.clifford_simp(g, quiet=True)
g.normalize()
zx.draw(g)<jupyter_output><empty_output><jupyter_text>Again, let us extract a circuit from this diagram:<jupyter_code>g2 = g.copy()
c = zx.extract_circuit(g2)
print(c.stats())<jupyter_output><empty_output><jupyter_text>We can convert this back into a PyZX-graph:<jupyter_code>zx.draw(c.to_graph())<jupyter_output><empty_output><jupyter_text>And verify that it is still equal to the original graph:<jupyter_code># We can just feed the Circuit objects directly to compare_tensors
zx.compare_tensors(c, circ)<jupyter_output><empty_output><jupyter_text>Now lets represent this circuit in the QASM circuit description language:<jupyter_code>print(c.to_basic_gates().to_qasm())<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09305501365587203, 'fraction_numerical': 0.010534529847834569, 'mean_word_length': 6.376978417266187, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '113859', 'license': 'Apache-2.0', 'n_tokens_mistral': 1685, 'n_tokens_neox': 1616, 'n_words': 552, 'path': 'demos/gettingstarted.ipynb', 'repo_name': 'paultirlisan/pyzx', 'size': '5126'}