Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pyzx/drawing.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ def draw_matplotlib(
ecol = '#0099ff'
elif et == 3:
ecol = 'gray'
elif et == 4:
ecol = '#8B0000' # Dark red for FAULT_EDGE
else:
ecol = 'black'

Expand Down
7 changes: 6 additions & 1 deletion pyzx/editor_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,12 @@ def pauli_push(g: BaseGraph[VT,ET],
rem_edges.append(edge)
w2 = g.add_vertex(t,q,r,p)
etab[upair(v,w2)] = [1,0]
etab[upair(n,w2)] = [1,0] if et == EdgeType.SIMPLE else [0,1]
if et == EdgeType.SIMPLE:
etab[upair(n,w2)] = [1,0]
elif et == EdgeType.HADAMARD:
etab[upair(n,w2)] = [0,1]
else:
raise ValueError(f"Cannot apply Pauli commutation through {et} edge")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Raising the ValueError here breaks the rewrite. Consider the following diagram where we want to push the pauli. Since the pauli matcher has not been modified, the rewrite will be matched but when you apply the rewrite, it will fail. So you need to either add the correct edge type when pushing pauli or modify the pauli matcher to not match when fault edges are involved.
Image

new_verts.append(w2)
if not vertex_is_zx(g.type(v)): # v is H_BOX
if len(new_verts) == 2:
Expand Down
6 changes: 6 additions & 0 deletions pyzx/graph/jsonparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ def json_to_graph_old(js: Union[str,Dict[str,Any]], backend:Optional[str]=None)
if 'type' in edge and edge['type'] == 'w_io':
g.add_edge((names[n1],names[n2]), EdgeType.W_IO)
continue
if 'type' in edge and edge['type'] == 'fault_edge':
g.add_edge((names[n1],names[n2]), EdgeType.FAULT_EDGE)
continue

amount = edges.get((names[n1],names[n2]),[0,0])
amount[0] += 1
Expand Down Expand Up @@ -321,6 +324,9 @@ def graph_to_dict_old(g: BaseGraph[VT,ET], include_scalar: bool=True) -> Dict[st
elif et == EdgeType.W_IO:
edges["e"+str(i)] = {"src": names[src],"tgt": names[tgt], "type": "w_io"}
i += 1
elif et == EdgeType.FAULT_EDGE:
edges["e"+str(i)] = {"src": names[src],"tgt": names[tgt], "type": "fault_edge"}
i += 1
else:
raise TypeError("Edge of type 0")

Expand Down
46 changes: 33 additions & 13 deletions pyzx/graph/multigraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,33 +29,38 @@ class Edge:
s: int
h: int
w_io: int
fault_edge: int

def __init__(self, s: int=0, h: int=0, w_io: int=0):
def __init__(self, s: int=0, h: int=0, w_io: int=0, fault_edge: int=0):
self.s = s
self.h = h
self.w_io = w_io
self.fault_edge = fault_edge

def add(self, s: int=0, h: int=0, w_io: int=0):
def add(self, s: int=0, h: int=0, w_io: int=0, fault_edge: int=0):
self.s += s
self.h += h
self.w_io += w_io
if self.s < 0 or self.h < 0:
self.fault_edge += fault_edge
if self.s < 0 or self.h < 0 or self.w_io < 0 or self.fault_edge < 0:
raise ValueError('Cannot have negative edges')
if self.w_io not in (0,1):
raise ValueError('Invalid number of W-IO edges')
if self.w_io == 1 and self.s + self.h > 0:
if self.w_io == 1 and self.s + self.h + self.fault_edge > 0:
raise ValueError('Cannot have W-IO edge and other edges')

def remove(self, s: int=0, h: int=0, w_io: int=0):
self.add(s=-s, h=-h, w_io=-w_io)
def remove(self, s: int=0, h: int=0, w_io: int=0, fault_edge: int=0):
self.add(s=-s, h=-h, w_io=-w_io, fault_edge=-fault_edge)

def is_empty(self) -> bool:
return self.s == 0 and self.h == 0 and self.w_io == 0
return self.s == 0 and self.h == 0 and self.w_io == 0 and self.fault_edge == 0

def get_edge_count(self, ty: EdgeType) -> int:
if ty == EdgeType.SIMPLE: return self.s
elif ty == EdgeType.HADAMARD: return self.h
else: return self.w_io
elif ty == EdgeType.W_IO: return self.w_io
elif ty == EdgeType.FAULT_EDGE: return self.fault_edge
else: return 0

class Multigraph(BaseGraph[int,Tuple[int,int,EdgeType]]):
"""Purely Pythonic multigraph implementation of :class:`~graph.base.BaseGraph`."""
Expand Down Expand Up @@ -177,7 +182,9 @@ def add_edge(self, edge_pair, edgetype=EdgeType.SIMPLE):

if edgetype == EdgeType.SIMPLE: e.add(s=1)
elif edgetype == EdgeType.HADAMARD: e.add(h=1)
else: e.add(w_io=1)
elif edgetype == EdgeType.W_IO: e.add(w_io=1)
elif edgetype == EdgeType.FAULT_EDGE: e.add(fault_edge=1)
else: raise ValueError(f"Unknown edge type: {edgetype}")

if self._auto_simplify: # This currently can keep a parallel regular and Hadamard edge
t1 = self.ty[s]
Expand All @@ -198,6 +205,9 @@ def add_edge(self, edge_pair, edgetype=EdgeType.SIMPLE):
if e.s > 0:
self.nedges = self.nedges - e.s + 1
e.s = 1
if e.fault_edge > 0:
self.nedges = self.nedges - e.fault_edge + 1
e.fault_edge = 1
if e.h > 0:
self.nedges = self.nedges - (e.h - e.h % 2)
self.scalar.add_power(-2 * (e.h - (e.h % 2)))
Expand All @@ -224,7 +234,7 @@ def remove_vertices(self, vertices):
# remove all edges
for v1 in vs:
e = self.graph[v][v1]
self.nedges -= e.s + e.h
self.nedges -= e.s + e.h + e.w_io + e.fault_edge
# Remove all edata for all edge types between v and v1
for ty in EdgeType:
self._edata.pop((min(v, v1), max(v, v1), ty), None)
Expand Down Expand Up @@ -260,7 +270,9 @@ def remove_edge(self, edge):
e = self.graph[s][t]
if ty == EdgeType.SIMPLE: e.remove(s=1)
elif ty == EdgeType.HADAMARD: e.remove(h=1)
else: e.remove(w_io=1)
elif ty == EdgeType.W_IO: e.remove(w_io=1)
elif ty == EdgeType.FAULT_EDGE: e.remove(fault_edge=1)
else: raise ValueError(f"Unknown edge type: {ty}")

if e.is_empty():
del self.graph[s][t]
Expand All @@ -284,6 +296,8 @@ def num_edges(self, s=None, t=None, et=None):
return self.graph[s][t].h
elif et == EdgeType.W_IO:
return self.graph[s][t].w_io
elif et == EdgeType.FAULT_EDGE:
return self.graph[s][t].fault_edge
else:
raise ValueError("Unkown EdgeType: %s" % repr(et))
else:
Expand All @@ -308,6 +322,7 @@ def edges(self, s=None, t=None):
for _ in range(e.s): yield (v0, v1, EdgeType.SIMPLE)
for _ in range(e.h): yield (v0, v1, EdgeType.HADAMARD)
for _ in range(e.w_io): yield (v0, v1, EdgeType.W_IO)
for _ in range(e.fault_edge): yield (v0, v1, EdgeType.FAULT_EDGE)
elif t != None:
s, t = (s, t) if s < t else (t, s)
if t not in self.graph[s]:
Expand All @@ -316,6 +331,7 @@ def edges(self, s=None, t=None):
for _ in range(e.s): yield (s, t, EdgeType.SIMPLE)
for _ in range(e.h): yield (s, t, EdgeType.HADAMARD)
for _ in range(e.w_io): yield (s, t, EdgeType.W_IO)
for _ in range(e.fault_edge): yield (s, t, EdgeType.FAULT_EDGE)

# def edges_in_range(self, start, end, safe=False):
# """like self.edges, but only returns edges that belong to vertices
Expand Down Expand Up @@ -392,10 +408,14 @@ def set_edge_type(self, edge, t):
# decrement the old type and increment the new type
if ty == EdgeType.SIMPLE: e.add(s=-1)
elif ty == EdgeType.HADAMARD: e.add(h=-1)
else: e.add(w_io=-1)
elif ty == EdgeType.W_IO: e.add(w_io=-1)
elif ty == EdgeType.FAULT_EDGE: e.add(fault_edge=-1)
else: raise ValueError(f"Unknown edge type: {ty}")
if t == EdgeType.SIMPLE: e.add(s=1)
elif t == EdgeType.HADAMARD: e.add(h=1)
else: e.add(w_io=1)
elif t == EdgeType.W_IO: e.add(w_io=1)
elif t == EdgeType.FAULT_EDGE: e.add(fault_edge=1)
else: raise ValueError(f"Unknown edge type: {t}")

def type(self, vertex):
return self.ty[vertex]
Expand Down
1 change: 1 addition & 0 deletions pyzx/js/zx_viewer.inline.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ function edgeColor(t) {
if (t == 1) return _settings_colors['edge']; //"black";
else if (t == 2) return _settings_colors['Hedge']; // "#08f";
else if (t == 3) return _settings_colors['Xedge']; // "gray";
else if (t == 4) return _settings_colors['FaultEdge']; // "dark red";
}

function webColor(t) {
Expand Down
1 change: 1 addition & 0 deletions pyzx/js/zx_viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ function edgeColor(t) {
if (t == 1) return _settings_colors['edge']; //"black";
else if (t == 2) return _settings_colors['Hedge']; // "#08f";
else if (t == 3) return _settings_colors['Xedge']; // "gray";
else if (t == 4) return _settings_colors['FaultEdge']; // "dark red";
}

function webColor(t) {
Expand Down
14 changes: 12 additions & 2 deletions pyzx/rewrite_rules/editor_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,12 @@ def pauli_push(g: BaseGraph[VT,ET],
rem_edges.append(edge)
w2 = g.add_vertex(t,q,r,p)
etab[upair(v,w2)] = [1,0]
etab[upair(n,w2)] = [1,0] if et == EdgeType.SIMPLE else [0,1]
if et == EdgeType.SIMPLE:
etab[upair(n,w2)] = [1,0]
elif et == EdgeType.HADAMARD:
etab[upair(n,w2)] = [0,1]
else:
raise ValueError(f"Cannot apply Pauli commutation through {et} edge")
new_verts.append(w2)
if not vertex_is_zx(g.type(v)): # v is H_BOX
if len(new_verts) == 2:
Expand Down Expand Up @@ -219,7 +224,12 @@ def add_Z_identity(g: BaseGraph[VT,ET],
r = 0.5*(g.row(v1) + g.row(v2))
q = 0.5*(g.qubit(v1) + g.qubit(v2))
w = g.add_vertex(VertexType.Z, q,r, 0)
etab[upair(v1,w)] = [1,0] if et == EdgeType.SIMPLE else [0,1]
if et == EdgeType.SIMPLE:
etab[upair(v1,w)] = [1,0]
elif et == EdgeType.HADAMARD:
etab[upair(v1,w)] = [0,1]
else:
raise ValueError(f"Cannot add Z identity on {et} edge")
etab[upair(v2,w)] = [1,0]
return (etab, [], rem_edges, False)

Expand Down
3 changes: 2 additions & 1 deletion pyzx/rewrite_rules/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,8 @@ def remove_ids(g: BaseGraph[VT,ET], matches: List[MatchIdType[VT]]) -> RewriteOu
e = (v0,v1)
if not e in etab: etab[e] = [0,0]
if et == EdgeType.SIMPLE: etab[e][0] += 1
else: etab[e][1] += 1
elif et == EdgeType.HADAMARD: etab[e][1] += 1
else: raise ValueError(f"Invalid edge type: {et}")
return (etab, rem, [], False)


Expand Down
4 changes: 4 additions & 0 deletions pyzx/tensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ def tensorfy(g: 'BaseGraph[VT,ET]', preserve_scalar:bool=True) -> np.ndarray:
t = np.tensordot(t,had)
elif g.edge_type(sl) == EdgeType.SIMPLE:
t = np.trace(t)
elif g.edge_type(sl) == EdgeType.W_IO:
raise NotImplementedError(f"Tensor contraction with W_IO self-loops is not implemented.")
elif g.edge_type(sl) == EdgeType.FAULT_EDGE:
raise NotImplementedError(f"Tensor contraction with FAULT_EDGE self-loops is not implemented.")
else:
raise NotImplementedError(f"Tensor contraction with {repr(sl)} self-loops is not implemented.")
nn = list(filter(lambda n: rows[n]<r or (rows[n]==r and n<v), neigh)) # TODO: allow ordering on vertex indices?
Expand Down
6 changes: 6 additions & 0 deletions pyzx/tikz.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ def _to_tikz(g: BaseGraph[VT,ET], draw_scalar:bool = False,
elif et == EdgeType.W_IO:
style = settings.tikz_classes['W-io-edge']
if style: s += "[style={:s}] ".format(style)
elif et == EdgeType.FAULT_EDGE:
style = settings.tikz_classes['Fault-edge']
if style: s += "[style={:s}] ".format(style)
else:
style = settings.tikz_classes['edge']
if style: s += "[style={:s}] ".format(style)
Expand Down Expand Up @@ -206,6 +209,7 @@ def tikzit(g: Union[BaseGraph[VT,ET],Circuit,str], draw_scalar:bool=False) -> No
synonyms_edge = ['empty', 'simple', 'none']
synonyms_hedge = ['hadamard edge']
synonyms_wedge = ['w edge', 'w io edge']
synonyms_fault_edge = ['fault edge']

tikz_error_message = "Not a valid tikz picture. Please use Tikzit to generate correct output."
def tikz_to_graph(
Expand Down Expand Up @@ -396,6 +400,8 @@ def tikz_to_graph(
etab[e] = [0,1]
elif style.lower() in synonyms_wedge:
g.add_edge(e, EdgeType.W_IO)
elif style.lower() in synonyms_fault_edge:
g.add_edge(e, EdgeType.FAULT_EDGE)
else:
if ignore_nonzx:
if e in etab:
Expand Down
8 changes: 7 additions & 1 deletion pyzx/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,12 @@ class EdgeType(IntEnum):
SIMPLE = 1
HADAMARD = 2
W_IO = 3
FAULT_EDGE = 4

def toggle_edge(ty: EdgeType) -> EdgeType:
"""Swap the regular and Hadamard edge types."""
if ty not in (EdgeType.SIMPLE, EdgeType.HADAMARD):
raise ValueError(f"Unknown edge type: {ty}")
return EdgeType.HADAMARD if ty == EdgeType.SIMPLE else EdgeType.SIMPLE

def phase_to_s(a: FractionLike, t:VertexType=VertexType.Z, poly_with_pi:bool=False) -> str:
Expand Down Expand Up @@ -138,13 +141,15 @@ def phase_is_pauli(phase: FractionLike):
'dummy': 'text',
'edge': '',
'H-edge': 'hadamard edge',
'W-io-edge': 'W io edge'
'W-io-edge': 'W io edge',
'Fault-edge': 'fault edge'
}

original_colors = {
'edge': '#000000',
'Hedge': '#0088ff',
'Xedge': '#999999',
'FaultEdge': '#8B0000',
'boundary': '#000000',
'X': '#ff8888',
'Y': '#aabbff',
Expand All @@ -169,6 +174,7 @@ def phase_is_pauli(phase: FractionLike):
'edge': '#000000',
'Hedge': '#888888',
'Xedge': '#dddddd',
'FaultEdge': '#8B0000',
'boundary': '#000000',
'X': '#666666',
'Y': '#9999dd',
Expand Down