diff --git a/demos/PauliWebs.ipynb b/demos/PauliWebs.ipynb index 6ec7d08e..c382e4a3 100644 --- a/demos/PauliWebs.ipynb +++ b/demos/PauliWebs.ipynb @@ -28,13 +28,59 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 7, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CNOT(0,1)\n", + "CNOT(1,0)\n", + "CNOT(2,0)\n", + "HAD(2)\n", + "CNOT(2,1)\n", + "HAD(2)\n", + "CNOT(1,0)\n", + "CNOT(0,2)\n", + "CNOT(1,2)\n", + "T(0)\n", + "T(1)\n", + "HAD(2)\n", + "T(2)\n", + "HAD(1)\n", + "HAD(0)\n", + "CNOT(0,2)\n", + "CNOT(0,2)\n", + "CNOT(1,0)\n", + "T(2)\n", + "HAD(2)\n", + "HAD(1)\n", + "CNOT(1,0)\n", + "CNOT(0,1)\n", + "CNOT(2,1)\n", + "T(0)\n", + "CNOT(2,0)\n", + "T(0)\n", + "T(0)\n", + "T(0)\n", + "CNOT(2,0)\n", + "CNOT(0,2)\n", + "T(2)\n", + "T(1)\n", + "T(1)\n", + "T(0)\n", + "HAD(1)\n", + "T(1)\n", + "CNOT(1,0)\n", + "CNOT(1,2)\n", + "CNOT(1,0)\n" + ] + }, { "data": { "text/html": [ - "
\n", + "
\n", "" ], "text/plain": [ @@ -413,19 +459,20 @@ "source": [ "# Generate a random CNOT, H, T circuit\n", "random.seed(1330)\n", - "c = zx.generate.CNOT_HAD_PHASE_circuit(qubits=3, depth=50)\n", + "c = zx.generate.CNOT_HAD_PHASE_circuit(qubits=3, depth=40)\n", + "for g in c.gates: print(g)\n", "zx.draw(c)" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -823,13 +870,13 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -1208,7 +1255,16 @@ "source": [ "# Once the Pauli webs have been computed, a specific web can be highlighted by `zx.draw` by passing it in as\n", "# an optional argument. Note that webs change color when they cross Hadamard edges.\n", - "zx.draw(g, labels=True, pauli_web=webs[87])" + "zx.draw(g, labels=True, pauli_web=webs[67])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We now show how this works in some simpler cases. The first is a single T gate.\n", + "\n", + "The T gate becomes a single, 1-legged phase gadget, connected to the input. This can be implemented by Z-merging a T magic state, then doing either an X or a Y measurement, depending on the parity of the Pauli web." ] }, { @@ -1219,7 +1275,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" @@ -1593,21 +1649,7 @@ }, "metadata": {}, "output_type": "display_data" - } - ], - "source": [ - "c = zx.qasm(\"\"\"\n", - "qreg q[1];\n", - "t q[0];\n", - "\"\"\")\n", - "zx.draw(c)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ + }, { "name": "stdout", "output_type": "stream", @@ -1618,7 +1660,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], "text/plain": [ @@ -1995,12 +2037,825 @@ } ], "source": [ + "c = zx.qasm(\"\"\"\n", + "qreg q[1];\n", + "t q[0];\n", + "\"\"\")\n", + "zx.draw(c)\n", + "\n", "g = c.to_graph()\n", "zx.full_reduce(g)\n", "in_circ, out_circ = preprocess(g)\n", "order, webs = compute_pauli_webs(g)\n", "print(f'{in_circ.gates} {out_circ.gates}')\n", - "zx.draw(g)" + "\n", + "# highlight the web associated to the T spider\n", + "zx.draw(g, labels=True, pauli_web=webs[3])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The next example is a circuit with some CNOT gates and a T gate, which can be simplified to a single phase gadget. I'm doing this manually here, since the automated simplifier comes up with a different answer (which is equivalent to this one, up to local Cliffords, but less clear what is going on)." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[] []\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "c = zx.qasm(\"\"\"\n", + "qreg q[3];\n", + "cx q[0], q[1];\n", + "cx q[1], q[2];\n", + "t q[2];\n", + "cx q[1], q[2];\n", + "cx q[0], q[1];\n", + "\"\"\")\n", + "zx.draw(c)\n", + "\n", + "# manual ZX simplification to get a single phase gadget\n", + "g = c.to_graph()\n", + "zx.simplify.gadgetize(g, graphlike=False)\n", + "zx.basicrules.strong_comp(g, 5, 7)\n", + "zx.simplify.spider_simp(g, quiet=True)\n", + "zx.basicrules.strong_comp(g, 3, 6)\n", + "zx.simplify.spider_simp(g, quiet=True)\n", + "zx.simplify.id_simp(g, quiet=True)\n", + "\n", + "# pauli web calculation\n", + "in_circ, out_circ = preprocess(g)\n", + "order, webs = compute_pauli_webs(g)\n", + "print(f'{in_circ.gates} {out_circ.gates}')\n", + "\n", + "# highlight the web associated to the T spider\n", + "zx.draw(g, labels=True, pauli_web=webs[15])" ] }, { diff --git a/pyzx/__init__.py b/pyzx/__init__.py index a204d297..3b49a8d8 100644 --- a/pyzx/__init__.py +++ b/pyzx/__init__.py @@ -37,6 +37,7 @@ from . import linalg from . import extract from . import rules +from . import basicrules from . import hrules from . import optimize from . import simplify diff --git a/pyzx/pauliweb.py b/pyzx/pauliweb.py index 33e5df45..18ac1356 100644 --- a/pyzx/pauliweb.py +++ b/pyzx/pauliweb.py @@ -83,8 +83,8 @@ def preprocess(g: BaseGraph[VT,ET]): #g.normalize() gadgetize(g) gadgets = set(v for v in g.vertices() if g.is_phase_gadget(v)) - boundary_spiders = set(v for v in g.vertices() if any(g.type(w) == VertexType.BOUNDARY for w in g.neighbors(v))) - to_rg(g, init_z=boundary_spiders, init_x=gadgets) + #boundary_spiders = set(v for v in g.vertices() if any(g.type(w) == VertexType.BOUNDARY for w in g.neighbors(v))) + to_rg(g, init_z=None, init_x=gadgets) in_circ = Circuit(len(g.inputs())) for j,i in enumerate(g.inputs()): diff --git a/pyzx/simplify.py b/pyzx/simplify.py index 1f3ce5da..b11c0640 100644 --- a/pyzx/simplify.py +++ b/pyzx/simplify.py @@ -367,15 +367,20 @@ def to_rg(g: BaseGraph[VT,ET], init_z: Optional[Set[VT]]=None, init_x: Optional[ for e in g.incident_edges(v): g.set_edge_type(e, toggle_edge(g.edge_type(e))) -def gadgetize(g: BaseGraph[VT,ET]): +def gadgetize(g: BaseGraph[VT,ET], graphlike:bool=True): """Convert every non-Clifford phase to a phase gadget""" for v in list(g.vertices()): p = g.phase(v) if not phase_is_clifford(p) and g.vertex_degree(v) > 1: - x = g.add_vertex(VertexType.Z, -1, g.row(v)) y = g.add_vertex(VertexType.Z, -2, g.row(v)) - g.add_edge((x, y), EdgeType.HADAMARD) - g.add_edge((v, x), EdgeType.HADAMARD) + if graphlike: + x = g.add_vertex(VertexType.Z, -1, g.row(v)) + g.add_edge((x, y), EdgeType.HADAMARD) + g.add_edge((v, x), EdgeType.HADAMARD) + else: + x = g.add_vertex(VertexType.X, -1, g.row(v)) + g.add_edge((x, y), EdgeType.SIMPLE) + g.add_edge((v, x), EdgeType.SIMPLE) g.set_phase(y, p) g.set_phase(v, 0)