From 8a2bd8ccb42775eb611405df15106d64464cbd71 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sun, 22 Dec 2024 15:59:49 +0000 Subject: [PATCH] updated demo notebook --- demos/PauliWebs.ipynb | 4461 ++++++++++++++++++++++++++++++++++++++--- pyzx/pauliweb.py | 56 +- 2 files changed, 4174 insertions(+), 343 deletions(-) diff --git a/demos/PauliWebs.ipynb b/demos/PauliWebs.ipynb index 4a71e074..af93c289 100644 --- a/demos/PauliWebs.ipynb +++ b/demos/PauliWebs.ipynb @@ -33,7 +33,7 @@ "4. output vertices have two webs `zweb[v]` and `xweb[v]` corresponding to both colors at `v`\n", "5. for non-Pauli spiders `v, w`, if `v` anti-commutes with the Pauli web of `w` then `order[v] < order[w]`\n", "\n", - "Under the hood, this is using PyZX's gflow-finding algorithm to compute a focussed gflow and translate this data into Pauli webs." + "Under the hood, this is using PyZX's gflow-finding algorithm to compute a focussed Pauli flow and translate this data into Pauli webs." ] }, { @@ -51,7 +51,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" @@ -447,7 +447,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -825,7 +825,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -1203,7 +1203,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -1581,7 +1581,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -1958,7 +1958,7 @@ } ], "source": [ - "# There are 4 interesting Pauli webs corresponding to the two outputs\n", + "# There are 4 Pauli webs corresponding to the two outputs\n", "zx.draw(g, labels=True, pauli_web=xwebs[4])\n", "zx.draw(g, labels=True, pauli_web=xwebs[5])\n", "zx.draw(g, labels=True, pauli_web=zwebs[4])\n", @@ -1973,7 +1973,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -2351,7 +2351,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Pauli webs are computed in the backwards (i.e. Heisenberg-style) by default,\n", + "# propegating Paulis from outputs to inputs. Paulis can be propegated forward\n", + "# instead by passing backwards=False.\n", + "\n", + "order, zwebsf, xwebsf = compute_pauli_webs(g, backwards=False)\n", + "zx.draw(g, labels=True, pauli_web=xwebsf[0])\n", + "zx.draw(g, labels=True, pauli_web=xwebsf[1])\n", + "zx.draw(g, labels=True, pauli_web=zwebsf[0])\n", + "zx.draw(g, labels=True, pauli_web=zwebsf[1])" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Here's an example with more CNOT gates\n", + "\n", + "c = zx.qasm(\"\"\"\n", + "qreg q[3];\n", + "cx q[0], q[1];\n", + "cx q[1], q[2];\n", + "cx q[0], q[2];\n", + "cx q[2], q[1];\n", + "cx q[0], q[1];\n", + "cx q[2], q[0];\n", + "\"\"\")\n", + "g = c.to_graph()\n", + "\n", + "zx.draw(g, labels=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "order, zwebs, xwebs = compute_pauli_webs(g)\n", + "zx.draw(g, labels=True, pauli_web=xwebs[15])\n", + "zx.draw(g, labels=True, pauli_web=zwebs[15])\n", + "zx.draw(g, labels=True, pauli_web=xwebs[16])\n", + "zx.draw(g, labels=True, pauli_web=zwebs[16])\n", + "zx.draw(g, labels=True, pauli_web=xwebs[17])\n", + "zx.draw(g, labels=True, pauli_web=zwebs[17])" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "" ], @@ -2728,21 +6565,21 @@ } ], "source": [ - "# The CNOT example also has 2 more bounded Pauli webs, with boundaries on the two interior spiders.\n", - "# Since we aren't injecting magic states at these locations, we don't need these Pauli webs.\n", - "zx.draw(g, labels=True, pauli_web=zwebs[3])\n", - "zx.draw(g, labels=True, pauli_web=xwebs[2])" + "# Alternatively, we can fuse all the spiders of the same color together to get a\n", + "# more interesting diagram.\n", + "zx.spider_simp(g)\n", + "zx.draw(g, labels=True)" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -3116,34 +6953,11 @@ }, "metadata": {}, "output_type": "display_data" - } - ], - "source": [ - "# Here's an example with more CNOT gates\n", - "\n", - "c = zx.qasm(\"\"\"\n", - "qreg q[3];\n", - "cx q[0], q[1];\n", - "cx q[1], q[2];\n", - "cx q[0], q[2];\n", - "cx q[2], q[1];\n", - "cx q[0], q[1];\n", - "cx q[2], q[0];\n", - "\"\"\")\n", - "g = c.to_graph()\n", - "\n", - "zx.draw(g, labels=True)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ + }, { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -3521,7 +7335,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -3899,7 +7713,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -4277,7 +8091,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -4655,7 +8469,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -5029,11 +8843,36 @@ }, "metadata": {}, "output_type": "display_data" - }, + } + ], + "source": [ + "# Since this describes the same unitary, the 6 output webs should all have the same support on\n", + "# the inputs as before.\n", + "order, zwebs, xwebs = compute_pauli_webs(g)\n", + "zx.draw(g, labels=True, pauli_web=xwebs[15])\n", + "zx.draw(g, labels=True, pauli_web=zwebs[15])\n", + "zx.draw(g, labels=True, pauli_web=xwebs[16])\n", + "zx.draw(g, labels=True, pauli_web=zwebs[16])\n", + "zx.draw(g, labels=True, pauli_web=xwebs[17])\n", + "zx.draw(g, labels=True, pauli_web=zwebs[17])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Clifford examples" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ { "data": { "text/html": [ - "
\n", + "
\n", "" ], "text/plain": [ @@ -5407,27 +9246,11 @@ }, "metadata": {}, "output_type": "display_data" - } - ], - "source": [ - "order, zwebs, xwebs = compute_pauli_webs(g)\n", - "zx.draw(g, labels=True, pauli_web=xwebs[15])\n", - "zx.draw(g, labels=True, pauli_web=zwebs[15])\n", - "zx.draw(g, labels=True, pauli_web=xwebs[16])\n", - "zx.draw(g, labels=True, pauli_web=zwebs[16])\n", - "zx.draw(g, labels=True, pauli_web=xwebs[17])\n", - "zx.draw(g, labels=True, pauli_web=zwebs[17])" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ + }, { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -5804,21 +9627,32 @@ } ], "source": [ - "# Alternatively, we can fuse all the spiders of the same color together to get a\n", - "# more interesting diagram.\n", - "zx.spider_simp(g)\n", - "zx.draw(g, labels=True)" + "# Next, we'll look at a single H gate\n", + "c = zx.qasm(\"\"\"\n", + "qreg q[1];\n", + "h q[0];\n", + "\"\"\")\n", + "g = c.to_graph()\n", + "\n", + "# PyZX renders this as a single H-edge connected to an identity spider\n", + "zx.draw(g)\n", + "\n", + "# we don't really need this id-spider, so we can remove it with id_simp\n", + "zx.id_simp(g)\n", + "zx.draw(g, labels=True)\n", + "\n", + "order, zwebs, xwebs = compute_pauli_webs(g)" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -6196,7 +10030,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -6571,10 +10405,35 @@ "metadata": {}, "output_type": "display_data" }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{(0, 2): 'X', (2, 0): 'Z'}\n" + ] + } + ], + "source": [ + "# The single output has two Pauli webs\n", + "zx.draw(g, labels=True, pauli_web=zwebs[2])\n", + "zx.draw(g, labels=True, pauli_web=xwebs[2])\n", + "\n", + "# Note that on a hadamard edge, the color changes in the middle of the edge. To handle this, Pauli webs actually label every\n", + "# \"half-edge\" with a Pauli. (v,w) means the half of the edge touching v, whereas (w,v) means the half of the edge touching w.\n", + "\n", + "# See for example:\n", + "print(zwebs[2].half_edges())" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -6948,11 +10807,39 @@ }, "metadata": {}, "output_type": "display_data" - }, + } + ], + "source": [ + "# Here's an example mixing CNOT and H gates\n", + "\n", + "c = zx.qasm(\"\"\"\n", + "qreg q[3];\n", + "cx q[0], q[1];\n", + "h q[1];\n", + "h q[2];\n", + "cx q[1], q[2];\n", + "cx q[0], q[2];\n", + "h q[0];\n", + "cx q[2], q[1];\n", + "h q[1];\n", + "h q[1];\n", + "cx q[0], q[1];\n", + "cx q[2], q[0];\n", + "\"\"\")\n", + "g = c.to_graph()\n", + "\n", + "zx.draw(g, labels=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -7330,7 +11217,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -7708,7 +11595,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -8082,36 +11969,11 @@ }, "metadata": {}, "output_type": "display_data" - } - ], - "source": [ - "# Since this describes the same unitary, the 6 output webs should all have the same support on\n", - "# the inputs as before.\n", - "order, zwebs, xwebs = compute_pauli_webs(g)\n", - "zx.draw(g, labels=True, pauli_web=xwebs[15])\n", - "zx.draw(g, labels=True, pauli_web=zwebs[15])\n", - "zx.draw(g, labels=True, pauli_web=xwebs[16])\n", - "zx.draw(g, labels=True, pauli_web=zwebs[16])\n", - "zx.draw(g, labels=True, pauli_web=xwebs[17])\n", - "zx.draw(g, labels=True, pauli_web=zwebs[17])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Clifford examples" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ + }, { "data": { "text/html": [ - "
\n", + "
\n", "" ], "text/plain": [ @@ -8489,7 +12351,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -8863,35 +12725,11 @@ }, "metadata": {}, "output_type": "display_data" - } - ], - "source": [ - "# Next, we'll look at a single H gate\n", - "c = zx.qasm(\"\"\"\n", - "qreg q[1];\n", - "h q[0];\n", - "\"\"\")\n", - "g = c.to_graph()\n", - "\n", - "# PyZX renders this as a single H-edge connected to an identity spider\n", - "zx.draw(g)\n", - "\n", - "# we don't really need this id-spider, so we can remove it with id_simp\n", - "zx.id_simp(g)\n", - "zx.draw(g, labels=True)\n", - "\n", - "order, zwebs, xwebs = compute_pauli_webs(g)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ + }, { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -9265,11 +13103,28 @@ }, "metadata": {}, "output_type": "display_data" - }, + } + ], + "source": [ + "# Note the we get some Y-edges appear, which are shown in a third color\n", + "order, zwebs, xwebs = compute_pauli_webs(g)\n", + "zx.draw(g, labels=True, pauli_web=xwebs[20])\n", + "zx.draw(g, labels=True, pauli_web=zwebs[20])\n", + "zx.draw(g, labels=True, pauli_web=xwebs[21])\n", + "zx.draw(g, labels=True, pauli_web=zwebs[21])\n", + "zx.draw(g, labels=True, pauli_web=xwebs[22])\n", + "zx.draw(g, labels=True, pauli_web=zwebs[22])" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -9644,35 +13499,10 @@ "metadata": {}, "output_type": "display_data" }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{(2, 0): 'Z', (0, 2): 'X'}\n" - ] - } - ], - "source": [ - "# The single output has two Pauli webs\n", - "zx.draw(g, labels=True, pauli_web=zwebs[2])\n", - "zx.draw(g, labels=True, pauli_web=xwebs[2])\n", - "\n", - "# Note that on a hadamard edge, the color changes in the middle of the edge. To handle this, Pauli webs actually label every\n", - "# \"half-edge\" with a Pauli. (v,w) means the half of the edge touching v, whereas (w,v) means the half of the edge touching w.\n", - "\n", - "# See for example:\n", - "print(zwebs[2].half_edges())" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -10049,36 +13879,27 @@ } ], "source": [ - "# Here's an example mixing CNOT and H gates\n", - "\n", + "# Next, we'll look at a single S gate\n", "c = zx.qasm(\"\"\"\n", - "qreg q[3];\n", - "cx q[0], q[1];\n", - "h q[1];\n", - "h q[2];\n", - "cx q[1], q[2];\n", - "cx q[0], q[2];\n", - "h q[0];\n", - "cx q[2], q[1];\n", - "h q[1];\n", - "h q[1];\n", - "cx q[0], q[1];\n", - "cx q[2], q[0];\n", + "qreg q[1];\n", + "s q[0];\n", "\"\"\")\n", "g = c.to_graph()\n", + "zx.draw(g, labels=True)\n", "\n", - "zx.draw(g, labels=True)" + "order, zwebs, xwebs = compute_pauli_webs(g)\n", + "zx.draw(g, labels=True, pauli_web=xwebs[2])" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
\n", + "
\n", "" ], "text/plain": [ @@ -10456,7 +14277,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], "text/plain": [ @@ -10834,7 +14655,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], "text/plain": [ @@ -11208,11 +15029,31 @@ }, "metadata": {}, "output_type": "display_data" - }, + } + ], + "source": [ + "# Since we can handle CNOT, H, and S, we can now compute Pauli webs for arbitrary Clifford\n", + "# unitaries.\n", + "\n", + "random.seed(1337)\n", + "c = zx.generate.CNOT_HAD_PHASE_circuit(qubits=4, depth=25, clifford=True)\n", + "g = c.to_graph(compress_rows=False)\n", + "zx.draw(g, labels=True)\n", + "\n", + "order, zwebs, xwebs = compute_pauli_webs(g)\n", + "zx.draw(g, pauli_web=zwebs[41])\n", + "zx.draw(g, pauli_web=xwebs[41])" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -11590,7 +15431,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], "text/plain": [ @@ -11968,7 +15809,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], "text/plain": [ @@ -12345,21 +16186,22 @@ } ], "source": [ - "# Note the we get some Y-edges appear, which are shown in a third color\n", - "order, zwebs, xwebs = compute_pauli_webs(g)\n", - "zx.draw(g, labels=True, pauli_web=xwebs[20])\n", - "zx.draw(g, labels=True, pauli_web=zwebs[20])\n", - "zx.draw(g, labels=True, pauli_web=xwebs[21])\n", - "zx.draw(g, labels=True, pauli_web=zwebs[21])\n", - "zx.draw(g, labels=True, pauli_web=xwebs[22])\n", - "zx.draw(g, labels=True, pauli_web=zwebs[22])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "I'm not sure the best way to handle S gates yet. This seems to depend on how you interpret the Pauli web data. The \"MBQC\"-style interpretation, which works for arbitrary circuits, says the Pauli web tells us how to push Paulis through the circuit, but some of the phases (and hence the circuit itself) might change. This makes sense if you are choosing the angles on-the-fly, and can vary them based on previous errors. The \"stabiliser theory\" style, which only works for Clifford circuits, tells us how to push Paulis through the circuit *without* changing it. That is, it visualises the computation of the stabiliser tableau." + "# As before, we can simplify the diagram without changing how Pauli webs behave on the boundaries\n", + "g1 = g.copy()\n", + "\n", + "# Calling full_reduce will turn any Clifford circuit into GSLC form, consisting of an input row\n", + "# of Clifford Z-spiders connected to an output row of Clifford Z-spiders via Hadamard edges.\n", + "# Inputs and outputs can be Hadamard or normal edges.\n", + "zx.full_reduce(g1)\n", + "g1.normalize()\n", + "\n", + "# nudge some nodes to left, for visibility\n", + "for v in [0,1,2,3,11]: g1.set_row(v, g1.row(v)-1)\n", + "zx.draw(g1, labels=True)\n", + "\n", + "order, zwebs, xwebs = compute_pauli_webs(g1)\n", + "zx.draw(g1, pauli_web=zwebs[41])\n", + "zx.draw(g1, pauli_web=xwebs[41])" ] }, { @@ -12371,13 +16213,13 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
\n", + "
\n", "" @@ -12763,13 +16605,13 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
\n", + "
\n", "" @@ -13164,22 +17006,14 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "{(16, 5): 'Y',\n", - " (5, 16): 'Y',\n", - " (16, 43): 'Z',\n", - " (43, 16): 'Z',\n", - " (16, 58): 'Z',\n", - " (58, 16): 'X',\n", - " (16, 14): 'Y',\n", - " (14, 16): 'Y',\n", - " (3, 1): 'Z',\n", - " (1, 3): 'Z',\n", + "{(3, 1): 'Y',\n", + " (1, 3): 'Y',\n", " (3, 43): 'Z',\n", " (43, 3): 'Z',\n", " (3, 5): 'Y',\n", @@ -13190,8 +17024,10 @@ " (54, 3): 'Z',\n", " (3, 14): 'Y',\n", " (14, 3): 'Y',\n", - " (5, 2): 'X',\n", - " (2, 5): 'Z',\n", + " (5, 16): 'Y',\n", + " (16, 5): 'Y',\n", + " (5, 2): 'Y',\n", + " (2, 5): 'Y',\n", " (5, 43): 'X',\n", " (43, 5): 'Z',\n", " (5, 58): 'X',\n", @@ -13199,10 +17035,16 @@ " (5, 54): 'X',\n", " (54, 5): 'Z',\n", " (14, 58): 'X',\n", - " (58, 14): 'X'}" + " (58, 14): 'X',\n", + " (14, 16): 'Y',\n", + " (16, 14): 'Y',\n", + " (16, 43): 'Z',\n", + " (43, 16): 'Z',\n", + " (16, 58): 'Z',\n", + " (58, 16): 'X'}" ] }, - "execution_count": 15, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -13214,13 +17056,13 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -13613,13 +17455,13 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
\n", + "
\n", "" @@ -13997,7 +17839,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -14385,7 +18227,7 @@ "order, zwebs, xwebs = compute_pauli_webs(g)\n", "\n", "# highlight the web associated to the T spider\n", - "zx.draw(g, labels=True, pauli_web=None)" + "zx.draw(g, labels=True, pauli_web=zwebs[1])" ] }, { @@ -14397,13 +18239,13 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
\n", + "
\n", "" @@ -14781,7 +18623,7 @@ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -15184,6 +19026,13 @@ "zx.draw(g, labels=True, pauli_web=zwebs[15])" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": null, @@ -15194,7 +19043,7 @@ ], "metadata": { "kernelspec": { - "display_name": "venv", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -15208,7 +19057,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.3" + "version": "3.10.12" } }, "nbformat": 4, diff --git a/pyzx/pauliweb.py b/pyzx/pauliweb.py index 3b4b3529..1d85c6b5 100644 --- a/pyzx/pauliweb.py +++ b/pyzx/pauliweb.py @@ -116,10 +116,10 @@ def transpose_corrections(c: Dict[VT, Set[VT]]) -> Dict[VT, Set[VT]]: ct[v].add(k) return ct -def compute_pauli_webs(g: BaseGraph[VT,ET]) -> Tuple[Dict[VT, int], Dict[VT, PauliWeb[VT,ET]], Dict[VT, PauliWeb[VT,ET]]]: +def compute_pauli_webs(g: BaseGraph[VT,ET], backwards:bool=True) -> Tuple[Dict[VT, int], Dict[VT, PauliWeb[VT,ET]], Dict[VT, PauliWeb[VT,ET]]]: g1 = g.clone() - out_edge: Dict[VT, Tuple[VT,VT]] = dict() - interior_edge: Dict[VT, Tuple[VT,VT]] = dict() + color_edge: Dict[VT, Tuple[VT,VT]] = dict() + boundary: Dict[VT, VT] = dict() for e in g.edges(): s, t = g.edge_st(e) @@ -133,12 +133,11 @@ def compute_pauli_webs(g: BaseGraph[VT,ET]) -> Tuple[Dict[VT, int], Dict[VT, Pau g1.remove_edge(e) g1.add_edge((s, v), EdgeType.SIMPLE) g1.add_edge((v, t), et) - interior_edge[v] = (s,t) + color_edge[v] = (s,t) - - for o in g1.outputs(): - e = g1.incident_edges(o)[0] - v = next(iter(g1.neighbors(o))) + for b in g1.inputs() + g1.outputs(): + e = g1.incident_edges(b)[0] + v = next(iter(g1.neighbors(b))) vt = g1.type(v) et = g1.edge_type(e) g1.remove_edge(e) @@ -151,11 +150,12 @@ def compute_pauli_webs(g: BaseGraph[VT,ET]) -> Tuple[Dict[VT, int], Dict[VT, Pau v1 = g1.add_vertex(VertexType.X) g1.add_edge((v, v0), et) g1.add_edge((v0, v1), EdgeType.SIMPLE) - g1.add_edge((v1, o), EdgeType.SIMPLE) - out_edge[v0] = (o, v) - out_edge[v1] = (o, v) + g1.add_edge((v1, b), EdgeType.SIMPLE) + color_edge[v0] = (b, v) + boundary[v0] = b + boundary[v1] = b - gf = gflow(g1, focus=True, pauli=True) + gf = gflow(g1, focus=True, reverse=backwards, pauli=True) if not gf: raise ValueError("Graph must have gFlow") order, corr = gf @@ -163,9 +163,9 @@ def compute_pauli_webs(g: BaseGraph[VT,ET]) -> Tuple[Dict[VT, int], Dict[VT, Pau zwebs: Dict[VT, PauliWeb[VT,ET]] = dict() xwebs: Dict[VT, PauliWeb[VT,ET]] = dict() vset = g.vertex_set() - corr_t = transpose_corrections(corr) + # corr_t = transpose_corrections(corr) - for v,c in corr_t.items(): + for v,c in corr.items(): pw = PauliWeb(g) for v1 in c: if v1 in vset: @@ -175,34 +175,16 @@ def compute_pauli_webs(g: BaseGraph[VT,ET]) -> Tuple[Dict[VT, int], Dict[VT, Pau pw.add_edge((v1, v2), p) else: pw.add_half_edge((v1, v2), p) - elif v1 in out_edge: - pw.add_edge(out_edge[v1], 'Z' if g1.type(v1) == VertexType.X else 'X') - elif v1 in interior_edge: - pw.add_edge(interior_edge[v1], 'Z' if g1.type(v1) == VertexType.X else 'X') - if v in out_edge: - ref = out_edge[v][0] + elif v1 in color_edge: + pw.add_edge(color_edge[v1], 'Z' if g1.type(v1) == VertexType.X else 'X') + if v in boundary: + ref = boundary[v] else: ref = v - pw.spread_to_boundary(inputs=True, outputs=False) + # pw.spread_to_boundary(inputs=True, outputs=False) if g1.type(v) == VertexType.Z: zwebs[ref] = pw elif g1.type(v) == VertexType.X: xwebs[ref] = pw - for i in g.inputs(): - v = next(iter(g.neighbors(i))) - pw = PauliWeb(g) - if g.type(v) == VertexType.Z: - pw.add_edge((v, i), 'Z') - zwebs[v] = pw - elif g.type(v) == VertexType.X: - pw.add_edge((v, i), 'X') - xwebs[v] = pw - elif g.type(v) == VertexType.BOUNDARY: - pw.add_edge((v, i), 'X') - xwebs[v] = pw - pw1 = PauliWeb(g) - pw1.add_edge((v, i), 'Z') - zwebs[v] = pw1 - return (order, zwebs, xwebs)