Skip to content

Commit

Permalink
Merge pull request #128 from RazinShaikh/master
Browse files Browse the repository at this point in the history
W node support
  • Loading branch information
jvdwetering authored Aug 18, 2023
2 parents 93ed3dd + f3fde52 commit 41b8be2
Show file tree
Hide file tree
Showing 13 changed files with 1,484 additions and 178 deletions.
1,095 changes: 1,095 additions & 0 deletions demos/ZXW_demo.ipynb

Large diffs are not rendered by default.

48 changes: 36 additions & 12 deletions pyzx/basicrules.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@

from typing import Tuple, List
from .graph.base import BaseGraph, VT, ET
from .utils import VertexType, EdgeType, is_pauli
from .rules import apply_rule, w_fusion
from .utils import VertexType, EdgeType, get_w_io, is_pauli, vertex_is_w

def color_change_diagram(g: BaseGraph[VT,ET]):
"""Color-change an entire diagram by applying Hadamards to the inputs and ouputs."""
Expand Down Expand Up @@ -95,8 +96,8 @@ def check_strong_comp(g: BaseGraph[VT,ET], v1: VT, v2: VT) -> bool:
return True

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

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

Expand All @@ -121,7 +122,7 @@ def strong_comp(g: BaseGraph[VT,ET], v1: VT, v2: VT) -> bool:

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

return True

def check_copy_X(g: BaseGraph[VT,ET], v: VT) -> bool:
Expand All @@ -136,10 +137,10 @@ def check_copy_X(g: BaseGraph[VT,ET], v: VT) -> bool:
return True

def copy_X(g: BaseGraph[VT,ET], v: VT) -> bool:
if not check_copy_X(g, v): return False
if not check_copy_X(g, v): return False
nv = next(iter(g.neighbors(v)))
strong_comp(g, v, nv)

return True

def check_pi_commute_Z(g: BaseGraph[VT, ET], v: VT) -> bool:
Expand All @@ -163,7 +164,7 @@ def pi_commute_Z(g: BaseGraph[VT, ET], v: VT) -> bool:
g.add_edge(g.edge(v, c))
g.add_edge(g.edge(c, w), edgetype=et)
return True

def check_pi_commute_X(g: BaseGraph[VT,ET], v: VT) -> bool:
color_change_diagram(g)
b = check_pi_commute_Z(g, v)
Expand All @@ -189,24 +190,47 @@ def copy_Z(g: BaseGraph, v: VT) -> bool:
return b

def check_fuse(g: BaseGraph[VT,ET], v1: VT, v2: VT) -> bool:
if check_fuse_w(g, v1, v2):
return True
if not (g.connected(v1,v2) and
((g.type(v1) == VertexType.Z and g.type(v2) == VertexType.Z) or
(g.type(v1) == VertexType.X and g.type(v2) == VertexType.X)) and
g.edge_type(g.edge(v1,v2)) == EdgeType.SIMPLE):
return False
else:
return True
return True

def fuse(g: BaseGraph[VT,ET], v1: VT, v2: VT) -> bool:
if not check_fuse(g, v1, v2): return False
if vertex_is_w(g.type(v1)):
return fuse_w(g, v1, v2)
g.add_to_phase(v1, g.phase(v2))
for v3 in g.neighbors(v2):
if v3 != v1:
g.add_edge_smart(g.edge(v1,v3), edgetype=g.edge_type(g.edge(v2,v3)))

g.remove_vertex(v2)
return True

def check_fuse_w(g: BaseGraph[VT,ET], v1: VT, v2: VT) -> bool:
if vertex_is_w(g.type(v1)) and vertex_is_w(g.type(v2)):
v1_in, v1_out = get_w_io(g, v1)
v2_in, v2_out = get_w_io(g, v2)
if g.edge_type(g.edge(v1_in, v2_out)) == EdgeType.SIMPLE or \
g.edge_type(g.edge(v2_in, v1_out)) == EdgeType.SIMPLE:
return True
return False

def fuse_w(g: BaseGraph[VT,ET], v1: VT, v2: VT) -> bool:
if not check_fuse_w(g, v1, v2): return False
v1_in, v1_out = get_w_io(g, v1)
v2_in, v2_out = get_w_io(g, v2)
if g.edge_type(g.edge(v1_out, v2_in)) == EdgeType.SIMPLE:
apply_rule(g, w_fusion, [(v1, v2)])
else:
g.set_position(v2_in, g.qubit(v1_in), g.row(v1_in))
g.set_position(v2_out, g.qubit(v1_out), g.row(v1_out))
apply_rule(g, w_fusion, [(v2, v1)])
return True

def check_remove_id(g: BaseGraph[VT,ET], v: VT) -> bool:
if not (g.vertex_degree(v) == 2 and g.phase(v) == 0):
return False
Expand All @@ -216,13 +240,13 @@ def check_remove_id(g: BaseGraph[VT,ET], v: VT) -> bool:
def remove_id(g: BaseGraph[VT,ET], v: VT) -> bool:
if not check_remove_id(g, v):
return False

v1, v2 = tuple(g.neighbors(v))
g.add_edge_smart(g.edge(v1,v2), edgetype=EdgeType.SIMPLE
if g.edge_type(g.edge(v,v1)) == g.edge_type(g.edge(v,v2))
else EdgeType.HADAMARD)
g.remove_vertex(v)

return True


20 changes: 14 additions & 6 deletions pyzx/drawing.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def draw_matplotlib(
vs_on_row: Dict[FloatInt, int] = {} # count the vertices on each row
for v in g.vertices():
vs_on_row[g.row(v)] = vs_on_row.get(g.row(v), 0) + 1

#Dict[VT,Tuple[FloatInt,FloatInt]]
layout = {v:(g.row(v),-g.qubit(v)) for v in g.vertices()}

Expand All @@ -195,18 +195,22 @@ def draw_matplotlib(
else:
vertices = g.vertices()
edges = g.edges()

for e in edges:
sp = layout[g.edge_s(e)]
tp = layout[g.edge_t(e)]
et = g.edge_type(e)
n_row = vs_on_row.get(g.row(g.edge_s(e)), 0)


dx = tp[0] - sp[0]
dy = tp[1] - sp[1]
bend_wire = (dx == 0) and h_edge_draw == 'blue' and n_row > 2
ecol = '#0099ff' if h_edge_draw == 'blue' and et == 2 else 'black'
if et == 2 and h_edge_draw == 'blue':
ecol = '#0099ff'
elif et == 3:
ecol = 'gray'
else:
ecol = 'black'

if bend_wire:
bend = 0.25
Expand All @@ -231,7 +235,7 @@ def draw_matplotlib(
ax.add_patch(patches.Rectangle(centre,w,h,angle=angle/math.pi*180,facecolor='yellow',edgecolor='black'))

#plt.plot([sp[0],tp[0]],[sp[1],tp[1]], 'k', zorder=0, linewidth=0.8)

for v in vertices:
p = layout[v]
t = g.type(v)
Expand All @@ -245,12 +249,16 @@ def draw_matplotlib(
elif t == VertexType.H_BOX:
ax.add_patch(patches.Rectangle((p[0]-0.1, p[1]-0.1), 0.2, 0.2, facecolor='yellow', edgecolor='black'))
a_offset = 0.25
elif t == VertexType.W_INPUT:
ax.add_patch(patches.Circle(p, 0.05, facecolor='black', edgecolor='black', zorder=1))
elif t == VertexType.W_OUTPUT:
ax.add_patch(patches.Polygon([(p[0]-0.15, p[1]), (p[0]+0.15, p[1]+0.2), (p[0]+0.15, p[1]-0.2)], facecolor='black', edgecolor='black'))
else:
ax.add_patch(patches.Circle(p, 0.1, facecolor='black', edgecolor='black', zorder=1))

if labels: plt.text(p[0]+0.25, p[1]+0.25, str(v), ha='center', color='gray', fontsize=5)
if a: plt.text(p[0], p[1]-a_offset, phase_to_s(a, t), ha='center', color='blue', fontsize=8)

if show_scalar:
x = min((g.row(v) for v in g.vertices()), default = 0)
y = -sum((g.qubit(v) for v in g.vertices()))/(g.num_vertices()+1)
Expand Down
5 changes: 5 additions & 0 deletions pyzx/editor_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,11 @@ def bialgebra(g: BaseGraph[VT,ET],
"matcher": hrules.match_par_hbox,
"rule": hrules.par_hbox,
"type": MATCHES_VERTICES},
"fuse_w": {"text": "fuse W nodes",
"tooltip": "Merges two connected W nodes together",
"matcher": rules.match_w_fusion_parallel,
"rule": rules.w_fusion,
"type": MATCHES_EDGES},
"copy": {"text": "copy 0/pi spider",
"tooltip": "Copies a single-legged spider with a 0/pi phase through its neighbor",
"matcher": hrules.match_copy,
Expand Down
31 changes: 20 additions & 11 deletions pyzx/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
from pyzx.routing.parity_maps import CNOT_tracker, Parity
from pyzx.routing.phase_poly import PhasePoly, mat22partition

from .utils import EdgeType, VertexType, FloatInt, FractionLike
from .utils import EdgeType, VertexType, FloatInt, FractionLike, vertex_is_w
from .graph import Graph
from .graph.base import BaseGraph
from .circuit import Circuit
Expand Down Expand Up @@ -70,7 +70,7 @@ def identity(qubits: int, depth: FloatInt=1,backend:Optional[str]=None) -> BaseG
return g

def spider(
typ:Union[Literal["Z"],Literal["X"],Literal["H"],VertexType.Type],
typ:Union[Literal["Z"], Literal["X"], Literal["H"], Literal["W"], VertexType.Type],
inputs: int,
outputs: int,
phase:FractionLike=0
Expand All @@ -80,10 +80,20 @@ def spider(
if typ == "Z": typ = VertexType.Z
elif typ == "X": typ = VertexType.X
elif typ == "H": typ = VertexType.H_BOX
elif typ == "W": typ = VertexType.W_OUTPUT
else:
if not isinstance(typ,int):
if not isinstance(typ, int):
raise TypeError("Wrong type for spider type: " + str(typ))
g = Graph()
if vertex_is_w(typ):
if inputs != 1:
raise ValueError("Wrong number of inputs for W node: " + str(inputs))
v_in = g.add_vertex(VertexType.W_INPUT, (outputs-1)/2, 0.8)
v_out = g.add_vertex(VertexType.W_OUTPUT, (outputs-1)/2, 1)
g.add_edge(g.edge(v_in, v_out), EdgeType.W_IO)
else:
v_in = g.add_vertex(typ, (inputs-1)/2, 1, phase)
v_out = v_in
inp = []
outp = []
for i in range(inputs):
Expand All @@ -92,11 +102,10 @@ def spider(
for i in range(outputs):
v = g.add_vertex(VertexType.BOUNDARY,i,2)
outp.append(v)
v = g.add_vertex(typ,(inputs-1)/2,1,phase)
for w in inp:
g.add_edge(g.edge(v,w))
g.add_edge(g.edge(v_in, w))
for w in outp:
g.add_edge(g.edge(v,w))
g.add_edge(g.edge(v_out, w))

g.set_inputs(tuple(inp))
g.set_outputs(tuple(outp))
Expand All @@ -105,13 +114,13 @@ def spider(


def CNOT_HAD_PHASE_circuit(
qubits: int,
depth: int,
p_had: float = 0.2,
p_t: float = 0.2,
qubits: int,
depth: int,
p_had: float = 0.2,
p_t: float = 0.2,
clifford:bool=False
) -> Circuit:
"""Construct a Circuit consisting of CNOT, HAD and phase gates.
"""Construct a Circuit consisting of CNOT, HAD and phase gates.
The default phase gate is the T gate, but if ``clifford=True``\ , then
this is replaced by the S gate.
Expand Down
Loading

0 comments on commit 41b8be2

Please sign in to comment.