Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bialgebra in the opposite direction #261

Merged
merged 13 commits into from
Aug 1, 2024
94 changes: 90 additions & 4 deletions pyzx/editor_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@


import json
from collections import defaultdict
from fractions import Fraction

from typing import Callable, Optional, List, Dict, Tuple
Expand Down Expand Up @@ -251,7 +252,7 @@ def match_bialgebra(g: BaseGraph[VT,ET],
candidates.difference_update(g.incident_edges(n))
return m

def bialgebra(g: BaseGraph[VT,ET],
def bialgebra(g: BaseGraph[VT,ET],
matches: List[Tuple[VT,VT]]
) -> rules.RewriteOutputType[VT,ET]:
rem_verts = []
Expand Down Expand Up @@ -299,6 +300,86 @@ def bialgebra(g: BaseGraph[VT,ET],
g.scalar.add_power((g.vertex_degree(v1)-2)*(g.vertex_degree(v2)-2))
return (etab, rem_verts, [], False)

def match_bialgebra_op(g: BaseGraph[VT,ET],
vertexf: Optional[Callable[[VT], bool]] = None,
vertex_type: Optional[Tuple[VertexType, VertexType]] = None,
edge_type: Optional[EdgeType] = None
) -> Optional[Tuple[List[VT], List[VT]]]:
if vertexf is not None: candidates = set([v for v in g.vertices() if vertexf(v)])
else: candidates = g.vertex_set()
if vertex_type is not None:
vtype1, vtype2 = vertex_type
else:
vtype1, vtype2 = VertexType.Z, VertexType.X
if edge_type is None:
edge_type = EdgeType.SIMPLE
type1_vertices = [v for v in candidates if g.type(v) == vtype1]
type2_vertices = [v for v in candidates if g.type(v) == vtype2]
if len(type1_vertices) <= 1 or len(type2_vertices) <= 1:
return None
# the vertices must have one external edge
for v1 in type1_vertices:
if g.vertex_degree(v1) != len(type2_vertices) + 1:
return None
for v2 in type2_vertices:
if g.vertex_degree(v2) != len(type1_vertices) + 1:
return None
# if all type1 vertices are connected to all type2 vertices with a simple edge, then they are a match
for v1 in type1_vertices:
for v2 in type2_vertices:
edges = list(g.edges(v1, v2))
if not (len(edges) == 1 and g.edge_type(edges[0]) == edge_type):
return None
return type1_vertices, type2_vertices

def bialgebra_op(g: BaseGraph[VT,ET],
matches: Tuple[List[VT], List[VT]],
edge_type: Optional[EdgeType] = EdgeType.SIMPLE
) -> rules.RewriteOutputType[VT,ET]:
def get_neighbors_and_loops(type1_vertices: List[VT], type2_vertices: List[VT]) -> Tuple[List[Tuple[VT, EdgeType]], List[EdgeType]]:
neighbors: List[Tuple[VT, EdgeType]] = []
loops: List[EdgeType] = []
for v1 in type1_vertices:
for edge in g.incident_edges(v1):
edge_st = g.edge_st(edge)
neighbor = edge_st[0] if edge_st[0] != v1 else edge_st[1]
if neighbor in type2_vertices:
continue
elif neighbor in type1_vertices:
if v1 > neighbor:
loops.append(g.edge_type(edge))
else:
neighbors.append((neighbor, g.edge_type(edge)))
return neighbors, loops

def add_vertex_with_averages(vertices, g, vtype):
average_row = sum(g.row(v) for v in vertices) / len(vertices)
average_qubit = sum(g.qubit(v) for v in vertices) / len(vertices)
return g.add_vertex(vtype, average_qubit, average_row)

def update_etab(etab, new_vertex, neighbors, loops):
for n, et in neighbors + [(new_vertex, et) for et in loops]:
etab[upair(new_vertex, n)][0 if et == EdgeType.SIMPLE else 1] += 1

type1_vertices, type2_vertices = matches
neighbors1, loops1 = get_neighbors_and_loops(type1_vertices, type2_vertices)
neighbors2, loops2 = get_neighbors_and_loops(type2_vertices, type1_vertices)

new_vertex1 = add_vertex_with_averages(type1_vertices, g, g.type(type2_vertices[0]))
new_vertex2 = add_vertex_with_averages(type2_vertices, g, g.type(type1_vertices[0]))

etab: dict = defaultdict(lambda: [0, 0])
if edge_type == EdgeType.SIMPLE:
etab[upair(new_vertex1, new_vertex2)] = [1, 0]
else:
etab[upair(new_vertex1, new_vertex2)] = [0, 1]
update_etab(etab, new_vertex1, neighbors1, loops1)
update_etab(etab, new_vertex2, neighbors2, loops2)

g.scalar.add_power(-(len(neighbors1)-1)*(len(neighbors2)-1)) #TODO: not sure if this is correct
RazinShaikh marked this conversation as resolved.
Show resolved Hide resolved

return (etab, type1_vertices + type2_vertices, [], False)


MATCHES_VERTICES = 1
MATCHES_EDGES = 2
Expand Down Expand Up @@ -364,11 +445,16 @@ def bialgebra(g: BaseGraph[VT,ET],
"matcher": pauli_matcher,
"rule": pauli_push,
"type": MATCHES_VERTICES},
"bialgebra": {"text": "bialgebra",
"bialgebra": {"text": "bialgebra",
"tooltip": "Applies the bialgebra rule to a connected pair of Z and X spiders",
"matcher": match_bialgebra,
"rule": bialgebra,
"matcher": match_bialgebra,
"rule": bialgebra,
"type": MATCHES_EDGES},
"bialgebra_op": {"text": "bialgebra (inverse)",
"tooltip": "Applies the bialgebra rule to a connected pair of Z and X spiders in the opposite direction",
"matcher": match_bialgebra_op,
"rule": bialgebra_op,
"type": MATCHES_VERTICES},
"euler": {"text": "decompose Hadamard",
"tooltip": "Expands a Hadamard-edge into its component spiders using its Euler decomposition",
"matcher": match_hadamard_edge,
Expand Down
Loading