diff --git a/demos/PauliWebs.ipynb b/demos/PauliWebs.ipynb
index c382e4a3..84236966 100644
--- a/demos/PauliWebs.ipynb
+++ b/demos/PauliWebs.ipynb
@@ -28,7 +28,7 @@
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": 2,
"metadata": {},
"outputs": [
{
@@ -80,7 +80,7 @@
{
"data": {
"text/html": [
- "
\n",
+ "\n",
""
@@ -466,386 +466,21 @@
},
{
"cell_type": "code",
- "execution_count": 8,
+ "execution_count": 3,
"metadata": {},
"outputs": [
{
- "data": {
- "text/html": [
- "\n",
- ""
- ],
- "text/plain": [
- ""
- ]
- },
- "metadata": {},
- "output_type": "display_data"
+ "ename": "KeyError",
+ "evalue": "58",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)",
+ "Cell \u001b[0;32mIn[3], line 10\u001b[0m\n\u001b[1;32m 6\u001b[0m g\u001b[38;5;241m.\u001b[39mnormalize()\n\u001b[1;32m 8\u001b[0m \u001b[38;5;66;03m# Compute the time-ordering on nodes (which is only important for the non-Clifford nodes) and compute the Pauli\u001b[39;00m\n\u001b[1;32m 9\u001b[0m \u001b[38;5;66;03m# webs for every node.\u001b[39;00m\n\u001b[0;32m---> 10\u001b[0m order, zwebs, xwebs \u001b[38;5;241m=\u001b[39m \u001b[43mcompute_pauli_webs\u001b[49m\u001b[43m(\u001b[49m\u001b[43mg\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 12\u001b[0m \u001b[38;5;66;03m# Draw the simplified ZX diagram. Note blue edges correspond to edges with Hadamard gates\u001b[39;00m\n\u001b[1;32m 13\u001b[0m zx\u001b[38;5;241m.\u001b[39mdraw(g, labels\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m)\n",
+ "File \u001b[0;32m~/git/pyzx/pyzx/pauliweb.py:191\u001b[0m, in \u001b[0;36mcompute_pauli_webs\u001b[0;34m(g)\u001b[0m\n\u001b[1;32m 188\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 189\u001b[0m ref \u001b[38;5;241m=\u001b[39m v\n\u001b[0;32m--> 191\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[43mg1\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtype\u001b[49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;241m==\u001b[39m VertexType\u001b[38;5;241m.\u001b[39mZ: zwebs[ref] \u001b[38;5;241m=\u001b[39m pw\n\u001b[1;32m 192\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m g1\u001b[38;5;241m.\u001b[39mtype(v) \u001b[38;5;241m==\u001b[39m VertexType\u001b[38;5;241m.\u001b[39mX: xwebs[ref] \u001b[38;5;241m=\u001b[39m pw\n\u001b[1;32m 195\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m (order, zwebs, xwebs)\n",
+ "File \u001b[0;32m~/git/pyzx/pyzx/graph/graph_s.py:293\u001b[0m, in \u001b[0;36mGraphS.type\u001b[0;34m(self, vertex)\u001b[0m\n\u001b[1;32m 292\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mtype\u001b[39m(\u001b[38;5;28mself\u001b[39m, vertex):\n\u001b[0;32m--> 293\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mty\u001b[49m\u001b[43m[\u001b[49m\u001b[43mvertex\u001b[49m\u001b[43m]\u001b[49m\n",
+ "\u001b[0;31mKeyError\u001b[0m: 58"
+ ]
}
],
"source": [
@@ -856,13 +491,9 @@
"# Normalise compacts the circuit visually and ensures every input/output is connected to a Z spider\n",
"g.normalize()\n",
"\n",
- "# For simplicity, preprocess strips off any local Clifford unitaries and saves them in in_circ and out_circ,\n",
- "# then introduces a dummy Z and X node at every output, so we can compute Z- and X-bounded Pauli webs there\n",
- "in_circ, out_circ = preprocess(g)\n",
- "\n",
"# Compute the time-ordering on nodes (which is only important for the non-Clifford nodes) and compute the Pauli\n",
"# webs for every node.\n",
- "order, webs = compute_pauli_webs(g)\n",
+ "order, zwebs, xwebs = compute_pauli_webs(g)\n",
"\n",
"# Draw the simplified ZX diagram. Note blue edges correspond to edges with Hadamard gates\n",
"zx.draw(g, labels=True)"
@@ -2045,9 +1676,7 @@
"\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",
+ "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=webs[3])"
@@ -2850,9 +2479,7 @@
"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",
+ "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=webs[15])"
diff --git a/pyzx/graph/base.py b/pyzx/graph/base.py
index 0c8f2ad3..39782671 100644
--- a/pyzx/graph/base.py
+++ b/pyzx/graph/base.py
@@ -196,13 +196,11 @@ def adjoint(self) -> 'BaseGraph':
def clone(self) -> 'BaseGraph':
"""
- This method should return an identical copy of the graph, without any relabeling
+ This method should return an identical copy of the graph, without any relabeling.
- FIXME: this currently *does* change lables.
-
- Used in lookahead extraction.
+ Note this needs to be implemented in the backend, since different backends deal with names differently.
"""
- return self.copy()
+ raise NotImplementedError()
def map_qubits(self, qubit_map:Mapping[int,Tuple[float,float]]) -> None:
for v in self.vertices():
diff --git a/pyzx/pauliweb.py b/pyzx/pauliweb.py
index e0531509..551245c9 100644
--- a/pyzx/pauliweb.py
+++ b/pyzx/pauliweb.py
@@ -31,6 +31,11 @@ def multiply_paulis(p1: str, p2: str) -> str:
elif p1 != 'Z' and p2 != 'Z': return 'Z'
else: raise ValueError('Expected: I, X, Y, or Z')
+def h_pauli(p: str) -> str:
+ if p == 'I': return 'I'
+ elif p == 'X': return 'Z'
+ elif p == 'Y': return 'Y'
+ else: return 'X'
class PauliWeb(Generic[VT, ET]):
@@ -40,51 +45,42 @@ class PauliWeb(Generic[VT, ET]):
all the edges incident to the vertices in `c`. To account for Hadamard edges, edge typing is
assigned to "half-edges".
"""
- def __init__(self, g: BaseGraph[VT,ET], c: Set[VT]):
+ def __init__(self, g: BaseGraph[VT,ET]):
self.g = g
- self.c = c
-
- def vertices(self) -> Set[VT]:
- vs = self.c.copy()
- for v in self.c:
- vs |= set(self.g.neighbors(v))
- return vs
+ self.es: Dict[Tuple[VT,VT], str] = dict()
+ self.vs: Set[VT] = set()
+
+ def add_edge(self, v_pair: Tuple[VT, VT], pauli: str):
+ s, t = v_pair
+ self.vs.add(s)
+ self.vs.add(t)
+ et = self.g.edge_type(self.g.edge(s, t))
+ t1 = self.es.get((s,t), 'I')
+ t2 = self.es.get((t,s), 'I')
+ self.es[(s,t)] = multiply_paulis(t1, pauli)
+ self.es[(t,s)] = multiply_paulis(t2, pauli if et == EdgeType.SIMPLE else h_pauli(pauli))
+
+ def add_vertex(self, v: VT):
+ p = 'X' if self.g.type(v) == VertexType.Z else 'Z'
+ for v1 in self.g.neighbors(v):
+ self.add_edge((v, v1), p)
+
+ def vertices(self):
+ return self.vs
def half_edges(self) -> Dict[Tuple[VT,VT],str]:
- es: Dict[Tuple[VT,VT],str] = dict()
- for v in self.c:
- for e in self.g.incident_edges(v):
- s, t = self.g.edge_st(e)
- v1 = s if s != v else t
- ty = 'Z' if self.g.type(v) == VertexType.Z else 'X'
- oty = 'X' if ty == 'Z' else 'Z'
- if self.g.edge_type(e) == EdgeType.SIMPLE:
- ty = oty
-
- t1 = es.get((v,v1), 'I')
- t2 = es.get((v1,v), 'I')
- es[(v,v1)] = multiply_paulis(t1, oty)
- es[(v1,v)] = multiply_paulis(t2, ty)
- return es
-
- def boundary(self) -> Set[VT]:
- b: Dict[VT, int] = dict()
- for v in self.c:
- for n in self.g.neighbors(v):
- if n in b: b[n] += 1
- else: b[n] = 1
- return set(n for n,k in b.items() if k%2 == 1)
+ return self.es
def __repr__(self):
- return 'PauliWeb' + repr(self.c)
+ return 'PauliWeb' + str(self.vs)
def preprocess(g: BaseGraph[VT,ET]):
#g.normalize()
- gadgetize(g)
- gadgets = set(v for v in g.vertices() if g.is_phase_gadget(v))
+ #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=None, init_x=gadgets)
+ #to_rg(g, init_z=None, init_x=gadgets)
in_circ = Circuit(len(g.inputs()))
for j,i in enumerate(g.inputs()):
@@ -144,10 +140,57 @@ 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]]]:
- gf = gflow(g, focus=True, pauli=True)
+def compute_pauli_webs(g: BaseGraph[VT,ET]) -> 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()
+ for j,o in enumerate(g1.outputs()):
+ e = g1.incident_edges(o)[0]
+ v = next(iter(g1.neighbors(o)))
+ vt = g1.type(v)
+ et = g1.edge_type(e)
+ g1.remove_edge(e)
+ if ((vt == VertexType.Z and et == EdgeType.SIMPLE) or
+ (vt == VertexType.X and et == EdgeType.HADAMARD)):
+ v0 = g1.add_vertex(VertexType.X)
+ v1 = g1.add_vertex(VertexType.Z)
+ if ((vt == VertexType.X and et == EdgeType.SIMPLE) or
+ (vt == VertexType.Z and et == EdgeType.HADAMARD)):
+ v0 = g1.add_vertex(VertexType.Z)
+ 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)
+
+ gf = gflow(g1, focus=True, pauli=True)
if not gf:
raise ValueError("Graph must have gFlow")
- order, c = gf
+ order, corr = gf
+
+ zwebs: Dict[VT, PauliWeb[VT,ET]] = dict()
+ xwebs: Dict[VT, PauliWeb[VT,ET]] = dict()
+ vset = g.vertex_set()
+ corr_t = transpose_corrections(corr)
+ print(corr_t)
+
+ for v,c in corr_t.items():
+ pw = PauliWeb(g)
+ for v1 in c:
+ if v1 in vset:
+ pw.add_vertex(v1)
+ elif v1 in out_edge:
+ o, vo = out_edge[v1]
+ pw.add_edge((o,vo), 'Z' if g1.type(v1) == VertexType.X else 'X')
+ if v in out_edge:
+ # o, vo = out_edge[v]
+ # pw.add_edge((o,vo), 'Z' if g1.type(v) == VertexType.Z else 'X')
+ ref = out_edge[v][0]
+ else:
+ ref = v
+
+ if g1.type(v) == VertexType.Z: zwebs[ref] = pw
+ elif g1.type(v) == VertexType.X: xwebs[ref] = pw
+
- return (order, { v: PauliWeb(g, c) for v,c in transpose_corrections(c).items() })
\ No newline at end of file
+ return (order, zwebs, xwebs)
\ No newline at end of file