Skip to content

Commit

Permalink
Merge pull request #244 from boldar99/fix-multigraph-rewrites
Browse files Browse the repository at this point in the history
Fix color_change, fusion, and match_ids
  • Loading branch information
jvdwetering authored Jul 1, 2024
2 parents 7c1ee73 + f9de154 commit 27990c1
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 33 deletions.
41 changes: 21 additions & 20 deletions pyzx/basicrules.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,23 +49,18 @@
from .graph.base import BaseGraph, VT, ET
from .rules import apply_rule, w_fusion, z_to_z_box
from .utils import (EdgeType, VertexType, get_w_io, get_z_box_label, is_pauli,
set_z_box_label, vertex_is_w, vertex_is_z_like)
set_z_box_label, vertex_is_w, vertex_is_z_like, toggle_vertex, toggle_edge)

def color_change_diagram(g: BaseGraph[VT,ET]):
"""Color-change an entire diagram by applying Hadamards to the inputs and ouputs."""
for v in g.vertices():
if g.type(v) == VertexType.BOUNDARY:
if g.vertex_degree(v) != 1:
raise ValueError("Boundary should only have 1 neighbor.")
v1 = next(iter(g.neighbors(v)))
e = g.edge(v,v1)
g.set_edge_type(e, EdgeType.SIMPLE
if g.edge_type(e) == EdgeType.HADAMARD
else EdgeType.HADAMARD)
elif g.type(v) == VertexType.Z:
g.set_type(v, VertexType.X)
elif g.type(v) == VertexType.X:
g.set_type(v, VertexType.Z)
for e in g.incident_edges(v):
g.set_edge_type(e, toggle_edge(g.edge_type(e)))
elif check_color_change(g, v):
color_change(g, v)

def check_color_change(g: BaseGraph[VT,ET], v: VT) -> bool:
if not (g.type(v) == VertexType.Z or g.type(v) == VertexType.X):
Expand All @@ -77,12 +72,9 @@ def color_change(g: BaseGraph[VT,ET], v: VT) -> bool:
if not (g.type(v) == VertexType.Z or g.type(v) == VertexType.X):
return False

g.set_type(v, VertexType.Z if g.type(v) == VertexType.X else VertexType.X)
for v1 in g.neighbors(v):
e = g.edge(v,v1)
g.set_edge_type(e, EdgeType.SIMPLE
if g.edge_type(e) == EdgeType.HADAMARD
else EdgeType.HADAMARD)
g.set_type(v, toggle_vertex(g.type(v)))
for e in g.incident_edges(v):
g.set_edge_type(e, toggle_edge(g.edge_type(e)))

return True

Expand Down Expand Up @@ -196,7 +188,7 @@ def check_fuse(g: BaseGraph[VT,ET], v1: VT, v2: VT) -> bool:
if not (g.connected(v1,v2) and
((g.type(v1) == VertexType.X and g.type(v2) == VertexType.X) or
(vertex_is_z_like(g.type(v1)) and vertex_is_z_like(g.type(v2)))) and
g.edge_type(g.edge(v1,v2)) == EdgeType.SIMPLE):
EdgeType.SIMPLE in [g.edge_type(edge) for edge in g.edges(v1,v2)]):
return False
return True

Expand All @@ -212,9 +204,18 @@ def fuse(g: BaseGraph[VT,ET], v1: VT, v2: VT) -> bool:
set_z_box_label(g, v1, get_z_box_label(g, v1) * get_z_box_label(g, v2))
else:
g.add_to_phase(v1, g.phase(v2))
for v3 in g.neighbors(v2):
if v3 != v1:
g.add_edge((v1,v3), edgetype=g.edge_type(g.edge(v2,v3)))
for e in g.incident_edges(v2):
source, target = g.edge_st(e)
other_vertex = source if source != v2 else target
if source != v2:
other_vertex = source
elif target != v2:
other_vertex = target
else: #self-loop
other_vertex = v1
if other_vertex == v1 and g.edge_type(e) == EdgeType.SIMPLE:
continue
g.add_edge((v1,other_vertex), edgetype=g.edge_type(e))
g.remove_vertex(v2)
return True

Expand Down
3 changes: 1 addition & 2 deletions pyzx/editor_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ def color_change(g: BaseGraph[VT,ET], matches: List[VT]) -> rules.RewriteOutputT
for v in matches:
g.set_type(v, toggle_vertex(g.type(v)))
for e in g.incident_edges(v):
et = g.edge_type(e)
g.set_edge_type(e, toggle_edge(et))
g.set_edge_type(e, toggle_edge(g.edge_type(e)))
return ({}, [],[],False)


Expand Down
14 changes: 10 additions & 4 deletions pyzx/graph/graph_s.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,16 @@ def vertices_in_range(self, start, end):
if all(start<v2<end for v2 in self.graph[v]):
yield v

def edges(self):
for v0,adj in self.graph.items():
for v1 in adj:
if v1 > v0: yield (v0,v1)
def edges(self, s=None, t=None):
if s is not None and t is not None:
yield (s,t) if s < t else (t,s)
elif s is not None:
for t in self.graph[s]:
yield (s,t) if s < t else (t,s)
else:
for v0,adj in self.graph.items():
for v1 in adj:
if v1 > v0: yield (v0,v1)

def edges_in_range(self, start, end, safe=False):
"""like self.edges, but only returns edges that belong to vertices
Expand Down
5 changes: 4 additions & 1 deletion pyzx/graph/multigraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import itertools
from collections import Counter
from fractions import Fraction
from typing import Tuple, Dict, Set, Any
Expand Down Expand Up @@ -323,7 +324,9 @@ def vertex_degree(self, vertex):
return d

def incident_edges(self, vertex):
return [(vertex, v1) if v1 > vertex else (v1, vertex) for v1 in self.graph[vertex]]
return list(itertools.chain.from_iterable(
self.edges(vertex, v1) for v1 in self.graph[vertex]
))

def connected(self,v1,v2):
return v2 in self.graph[v1]
Expand Down
21 changes: 15 additions & 6 deletions pyzx/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ def match_spider_parallel(
"""
if matchf is not None: candidates = set([e for e in g.edges() if matchf(e)])
else: candidates = g.edge_set()
candidates = set(candidates)
types = g.types()

i = 0
Expand All @@ -176,6 +177,7 @@ def match_spider_parallel(
e = candidates.pop()
if g.edge_type(e) != EdgeType.SIMPLE: continue
v0, v1 = g.edge_st(e)
if v0 == v1: continue
v0t = types[v0]
v1t = types[v1]
if (v0t == v1t and vertex_is_zx(v0t)) or \
Expand Down Expand Up @@ -223,11 +225,18 @@ def spider(g: BaseGraph[VT,ET], matches: List[MatchSpiderType[VT]]) -> RewriteOu
rem_verts.append(v1)

# edges from the second vertex are transferred to the first
for w in g.neighbors(v1):
if v0 == w: continue
e = (v0,w)
if e not in etab: etab[e] = [0,0]
etab[e][g.edge_type(g.edge(v1,w)) - 1] += 1
for e in g.incident_edges(v1):
source, target = g.edge_st(e)
if source != v1:
other_vertex = source
elif target != v1:
other_vertex = target
else: #self-loop
other_vertex = v0
new_e = (v0, other_vertex)
if new_e not in etab: etab[new_e] = [0,0]
etab[new_e][g.edge_type(e)-1] += 1
etab[(v0,v0)][0] = 0 # remove simple edge loops
return (etab, rem_verts, [], True)

def unspider(g: BaseGraph[VT,ET], m: List[Any], qubit:FloatInt=-1, row:FloatInt=-1) -> VT:
Expand Down Expand Up @@ -780,7 +789,7 @@ def match_ids_parallel(

while (num == -1 or i < num) and len(candidates) > 0:
v = candidates.pop()
if phases[v] != 0 or not vertex_is_zx(types[v]) or g.is_ground(v):
if phases[v] != 0 or not vertex_is_zx(types[v]) or g.is_ground(v) or g.vertex_degree(v) != 2:
continue
neigh = g.neighbors(v)
if len(neigh) != 2: continue
Expand Down

0 comments on commit 27990c1

Please sign in to comment.