Skip to content

Commit

Permalink
Merge pull request #248 from RazinShaikh/multigraph-rules-fixes
Browse files Browse the repository at this point in the history
Multigraph rules fixes: bialgebra
  • Loading branch information
jvdwetering authored Jul 9, 2024
2 parents 34e14fc + 98e2180 commit 716c3e9
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 69 deletions.
32 changes: 6 additions & 26 deletions pyzx/basicrules.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
'remove_id']

from typing import Tuple, List
from .editor_actions import bialgebra
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,
Expand Down Expand Up @@ -84,38 +85,17 @@ def check_strong_comp(g: BaseGraph[VT,ET], v1: VT, v2: VT) -> bool:
is_pauli(g.phase(v1)) and
is_pauli(g.phase(v2)) and
g.connected(v1,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

def strong_comp(g: BaseGraph[VT,ET], v1: VT, v2: VT) -> bool:
if not check_strong_comp(g, v1, v2): return False

nhd: Tuple[List[VT],List[VT]] = ([],[])
v = (v1,v2)

for i in range(2):
j = (i + 1) % 2
for vn in g.neighbors(v[i]):
if vn != v[j]:
q = 0.4*g.qubit(vn) + 0.6*g.qubit(v[i])
r = 0.4*g.row(vn) + 0.6*g.row(v[i])
newv = g.add_vertex(g.type(v[j]), qubit=q, row=r)
g.add_edge((newv,vn), edgetype=g.edge_type(g.edge(v[i],vn)))
g.set_phase(newv, g.phase(v[j]))
nhd[i].append(newv)

for n1 in nhd[0]:
for n2 in nhd[1]:
g.add_edge((n1,n2))

g.scalar.add_power((len(nhd[0]) - 1) * (len(nhd[1]) - 1))
if g.phase(v1) == 1 and g.phase(v2) == 1:
g.scalar.add_phase(1)

g.remove_vertex(v1)
g.remove_vertex(v2)

etab, rem_verts, rem_edges, check_isolated_vertices = bialgebra(g, [(v1, v2)])
g.remove_edges(rem_edges)
g.remove_vertices(rem_verts)
g.add_edge_table(etab)
return True

def check_copy_X(g: BaseGraph[VT,ET], v: VT) -> bool:
Expand Down
65 changes: 40 additions & 25 deletions pyzx/editor_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,35 +255,50 @@ def bialgebra(g: BaseGraph[VT,ET],
) -> rules.RewriteOutputType[VT,ET]:
rem_verts = []
etab = {}
for v,w in matches:
rem_verts.append(v)
rem_verts.append(w)
new_verts = []
# v is an X-spider, but w is either a Z-spider or an H-box
t = g.type(w)
for n in g.neighbors(v):
if n == w: continue
r = 0.6*g.row(v) + 0.4*g.row(n)
q = 0.6*g.qubit(v) + 0.4*g.qubit(n)
v2 = g.add_vertex(t,q,r)
etab[upair(n,v2)] = [1,0] if g.edge_type(g.edge(n,v)) == EdgeType.SIMPLE else [0,1]
new_verts.append(v2)
if g.type(w) == VertexType.Z:
for v1, v2 in matches:
rem_verts.append(v1)
rem_verts.append(v2)
v = (v1,v2)
new_verts: Tuple[List[VT],List[VT]] = ([],[]) # new vertices for v1 and v2

for i, j in [(0, 1), (1, 0)]:
multi_edge_found = False
for e in g.incident_edges(v[i]):
source, target = g.edge_st(e)
other_vertex = source if source != v[i] else target
if other_vertex != v[j] or multi_edge_found:
q = 0.4*g.qubit(other_vertex) + 0.6*g.qubit(v[i])
r = 0.4*g.row(other_vertex) + 0.6*g.row(v[i])
newv = g.add_vertex(g.type(v[j]), qubit=q, row=r)
g.set_phase(newv, g.phase(v[j]))
new_verts[i].append(newv)
if other_vertex == v[j]:
q = 0.4*g.qubit(v[i]) + 0.6*g.qubit(other_vertex)
r = 0.4*g.row(v[i]) + 0.6*g.row(other_vertex)
newv2 = g.add_vertex(g.type(v[i]), qubit=q, row=r)
new_verts[j].append(newv2)
other_vertex = newv2
if upair(newv, other_vertex) not in etab:
etab[upair(newv, other_vertex)] = [0, 0]
type_index = 0 if g.edge_type(e) == EdgeType.SIMPLE else 1
etab[upair(newv, other_vertex)][type_index] += 1
elif i == 0: # only add new vertex once
multi_edge_found = True

for n1 in new_verts[0]:
for n2 in new_verts[1]:
if upair(n1,n2) not in etab:
etab[upair(n1,n2)] = [0, 0]
etab[upair(n1,n2)][0] += 1

if g.type(v2) == VertexType.Z:
t = VertexType.X
g.scalar.add_power((g.vertex_degree(v)-2)*(g.vertex_degree(w)-2))
g.scalar.add_power((g.vertex_degree(v1)-2)*(g.vertex_degree(v2)-2))
else: #g.type(w) == VertexType.H_BOX
t = VertexType.Z
g.scalar.add_power(g.vertex_degree(v)-2)
for n in g.neighbors(w):
if n == v: continue
r = 0.6*g.row(w) + 0.4*g.row(n)
q = 0.6*g.qubit(w) + 0.4*g.qubit(n)
w2 = g.add_vertex(t,q,r)
etab[upair(n,w2)] = [1,0] if g.edge_type(g.edge(n,w)) == EdgeType.SIMPLE else [0,1]
for v2 in new_verts:
etab[upair(w2,v2)] = [1,0]
g.scalar.add_power(g.vertex_degree(v1)-2)
return (etab, rem_verts, [], False)


MATCHES_VERTICES = 1
MATCHES_EDGES = 2
Expand Down
49 changes: 31 additions & 18 deletions pyzx/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
from typing import Any, Callable, TypeVar, Optional, Union
from typing_extensions import Literal

from collections import Counter
from fractions import Fraction
import itertools

Expand Down Expand Up @@ -99,8 +100,9 @@ def match_bialg_parallel(
tries to find as many as possible.
:rtype: List of 4-tuples ``(v1, v2, neighbors_of_v1,neighbors_of_v2)``
"""
if matchf is not None: candidates = set([e for e in g.edges() if matchf(e)])
else: candidates = g.edge_set()
if matchf is not None: candidates_set = set([e for e in g.edges() if matchf(e)])
else: candidates_set = g.edge_set()
candidates = list(Counter(candidates_set).elements())
phases = g.phases()
types = g.types()

Expand All @@ -118,14 +120,17 @@ def match_bialg_parallel(
((v0t == VertexType.Z and v1t == VertexType.X) or (v0t == VertexType.X and v1t == VertexType.Z))):
v0n = [n for n in g.neighbors(v0) if not n == v1]
v1n = [n for n in g.neighbors(v1) if not n == v0]
if (
all([types[n] == v1t and phases[n] == 0 for n in v0n]) and
all([types[n] == v0t and phases[n] == 0 for n in v1n])):
if (all([types[n] == v1t and phases[n] == 0 for n in v0n]) and # all neighbors of v0 are of the same type as v1
all([types[n] == v0t and phases[n] == 0 for n in v1n]) and # all neighbors of v1 are of the same type as v0
len(g.edges(v0, v1)) == 1 and # there is exactly one edge between v0 and v1
len(g.edges(v0, v0)) == 0 and # there are no self-loops on v0
len(g.edges(v1, v1)) == 0): # there are no self-loops on v1
i += 1
for v in v0n:
for c in g.incident_edges(v): candidates.discard(c)
for v in v1n:
for c in g.incident_edges(v): candidates.discard(c)
for vn in [v0n, v1n]:
for v in vn:
for c in g.incident_edges(v):
if c in candidates:
candidates.remove(c)
m.append((v0,v1,v0n,v1n))
return m

Expand Down Expand Up @@ -401,8 +406,9 @@ def match_pivot_parallel(
consider all edges.
:rtype: List of 4-tuples. See :func:`pivot` for the details.
"""
if matchf is not None: candidates = set([e for e in g.edges() if matchf(e)])
else: candidates = g.edge_set()
if matchf is not None: candidates_set = set([e for e in g.edges() if matchf(e)])
else: candidates_set = g.edge_set()
candidates = list(Counter(candidates_set).elements())
types = g.types()
phases = g.phases()

Expand All @@ -426,6 +432,9 @@ def match_pivot_parallel(
v0n = list(g.neighbors(v0))
v0b: List[VT] = []
for n in v0n:
if len(list(g.edges(v0,n))) != 1:
invalid_edge = True
break
et = g.edge_type(g.edge(v0,n))
if types[n] == VertexType.Z and et == EdgeType.HADAMARD: pass
elif types[n] == VertexType.BOUNDARY: v0b.append(n)
Expand All @@ -449,10 +458,11 @@ def match_pivot_parallel(
if len(v0b) + len(v1b) > 1: continue

i += 1
for v in v0n:
for c in g.incident_edges(v): candidates.discard(c)
for v in v1n:
for c in g.incident_edges(v): candidates.discard(c)
for vn in [v0n, v1n]:
for v in vn:
for c in g.incident_edges(v):
if c in candidates:
candidates.remove(c)
b0 = list(v0b)
b1 = list(v1b)
m.append(((v0,v1),(b0,b1)))
Expand All @@ -465,8 +475,9 @@ def match_pivot_gadget(
"""Like :func:`match_pivot_parallel`, but except for pairings of
Pauli vertices, it looks for a pair of an interior Pauli vertex and an
interior non-Clifford vertex in order to gadgetize the non-Clifford vertex."""
if matchf is not None: candidates = set([e for e in g.edges() if matchf(e)])
else: candidates = g.edge_set()
if matchf is not None: candidates_set = set([e for e in g.edges() if matchf(e)])
else: candidates_set = g.edge_set()
candidates = list(Counter(candidates_set).elements())
types = g.types()
phases = g.phases()
rs = g.rows()
Expand Down Expand Up @@ -524,7 +535,9 @@ def match_pivot_gadget(

m.append(((v0,v1),([],[v])))
i += 1
for c in discard_edges: candidates.discard(c)
for c in discard_edges:
if c in candidates:
candidates.remove(c)
g.add_edges(edge_list,EdgeType.SIMPLE)
return m

Expand Down

0 comments on commit 716c3e9

Please sign in to comment.