From de899266942d1bf7eacab5a7ad485c5e6a675c43 Mon Sep 17 00:00:00 2001 From: Razin Shaikh Date: Thu, 13 Jun 2024 00:09:04 +0100 Subject: [PATCH 1/7] changed_edge_types is necessary in diff for backwards compatibility with simple graph --- pyzx/graph/diff.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pyzx/graph/diff.py b/pyzx/graph/diff.py index e708958c..5f22057a 100644 --- a/pyzx/graph/diff.py +++ b/pyzx/graph/diff.py @@ -84,13 +84,13 @@ def calculate_diff(self, g1: BaseGraph[VT,ET], g2: BaseGraph[VT,ET]) -> None: pos2 = g2.qubit(v), g2.row(v) self.changed_pos[v] = pos2 - # for e in new_edges: - # if e in old_edges: - # if g1.edge_type(e) != g2.edge_type(e): - # self.changed_edge_types[e] = g2.edge_type(e) - # else: - # if g2.edge_type(e) != EdgeType.HADAMARD: # We take Hadamard edges to be the default - # self.changed_edge_types[e] = g2.edge_type(e) + for e in new_edges: + if e in old_edges: + if g1.edge_type(e) != g2.edge_type(e): + self.changed_edge_types[e] = g2.edge_type(e) + else: + if g2.edge_type(e) != EdgeType.HADAMARD: # We take Hadamard edges to be the default + self.changed_edge_types[e] = g2.edge_type(e) def apply_diff(self,g: BaseGraph[VT,ET]) -> BaseGraph[VT,ET]: g = copy.deepcopy(g) From 72367941ca8f56a6cbdb4ec69a6d758438ef317b Mon Sep 17 00:00:00 2001 From: Razin Shaikh Date: Thu, 13 Jun 2024 17:32:52 +0100 Subject: [PATCH 2/7] supporting w_io edge type in multigraph --- pyzx/graph/multigraph.py | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/pyzx/graph/multigraph.py b/pyzx/graph/multigraph.py index 1635e43f..d1f2b75f 100644 --- a/pyzx/graph/multigraph.py +++ b/pyzx/graph/multigraph.py @@ -26,21 +26,29 @@ class Edge: between two vertices""" s: int h: int - def __init__(self, s: int=0, h: int=0): + w_io: int + + def __init__(self, s: int=0, h: int=0, w_io: int=0): self.s = s self.h = h + self.w_io = w_io - def add(self, s: int=0, h: int=0): + def add(self, s: int=0, h: int=0, w_io: int=0): self.s += s self.h += h + self.w_io += w_io if self.s < 0 or self.h < 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: + 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): - self.add(s=-s, h=-h) - def is_empty(self) -> bool: - return self.s == 0 and self.h == 0 + return self.s == 0 and self.h == 0 and self.w_io == 0 class Multigraph(BaseGraph[int,Tuple[int,int,EdgeType.Type]]): """Purely Pythonic multigraph implementation of :class:`~graph.base.BaseGraph`.""" @@ -156,7 +164,8 @@ def add_edge(self, edge_pair, edgetype=EdgeType.SIMPLE): e = self.graph[s][t] if edgetype == EdgeType.SIMPLE: e.add(s=1) - else: e.add(h=1) + elif edgetype == EdgeType.HADAMARD: e.add(h=1) + else: e.add(w_io=1) if self._auto_simplify: t1 = self.ty[s] @@ -224,9 +233,10 @@ def remove_edge(self, edge): s,t,ty = edge e = self.graph[s][t] if ty == EdgeType.SIMPLE: e.remove(s=1) - else: e.remove(h=1) + elif ty == EdgeType.HADAMARD: e.remove(h=1) + else: e.remove(w_io=1) - if e.s == 0 and e.h == 0: + if e.is_empty(): del self.graph[s][t] del self.graph[t][s] @@ -257,11 +267,13 @@ def edges(self, s=None, t=None): if v1 > v0: 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) elif t != None: s, t = (s, t) if s < t else (t, s) e = self.graph[s][t] 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) # def edges_in_range(self, start, end, safe=False): # """like self.edges, but only returns edges that belong to vertices @@ -315,8 +327,13 @@ def set_edge_type(self, edge, t): v1,v2,ty = edge if ty != t: e = self.graph[v1][v2] - if t == EdgeType.SIMPLE: e.add(s=1,h=-1) - else: e.add(s=-1,h=1) + # 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) + if t == EdgeType.SIMPLE: e.add(s=1) + elif t == EdgeType.HADAMARD: e.add(h=1) + else: e.add(w_io=1) def type(self, vertex): return self.ty[vertex] From eac8cfdc87ddedc066cff3a7b3a72df57f32e19d Mon Sep 17 00:00:00 2001 From: Razin Shaikh Date: Thu, 13 Jun 2024 17:35:45 +0100 Subject: [PATCH 3/7] multigraph edge_type works with two inputs --- pyzx/graph/multigraph.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyzx/graph/multigraph.py b/pyzx/graph/multigraph.py index d1f2b75f..4b602405 100644 --- a/pyzx/graph/multigraph.py +++ b/pyzx/graph/multigraph.py @@ -321,6 +321,11 @@ def connected(self,v1,v2): return v2 in self.graph[v1] def edge_type(self, e): + if len(e) == 2: + edges = list(self.edges(e[0],e[1])) + if len(edges) != 1: + raise ValueError('Cannot determine edge type') + e = edges[0] return e[2] def set_edge_type(self, edge, t): From 84bd8d7fe8c6cdf8766ddfd22e8aaea52d166816 Mon Sep 17 00:00:00 2001 From: Razin Shaikh Date: Thu, 13 Jun 2024 20:08:18 +0100 Subject: [PATCH 4/7] the diff was removing duplicate edges, which is needed for multigraphs --- pyzx/graph/diff.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pyzx/graph/diff.py b/pyzx/graph/diff.py index 5f22057a..a190368a 100644 --- a/pyzx/graph/diff.py +++ b/pyzx/graph/diff.py @@ -51,15 +51,17 @@ def calculate_diff(self, g1: BaseGraph[VT,ET], g2: BaseGraph[VT,ET]) -> None: new_verts = g2.vertex_set() self.removed_verts = list(old_verts - new_verts) self.new_verts = list(new_verts - old_verts) - old_edges = g1.edge_set() - new_edges = g2.edge_set() self.new_edges = [] self.removed_edges = [] - for e in (new_edges - old_edges): + g1_edges = list(g1.edges()) + new_edges = [i for i in g2.edges() if not i in g1_edges or g1_edges.remove(i)] + for e in (new_edges): self.new_edges.append((g2.edge_st(e), g2.edge_type(e))) - for e in (old_edges - new_edges): + g2_edges = list(g2.edges()) + removed_edges = [i for i in g1.edges() if not i in g2_edges] + for e in (removed_edges): s,t = g1.edge_st(e) if s in self.removed_verts or t in self.removed_verts: continue self.removed_edges.append(e) @@ -84,8 +86,8 @@ def calculate_diff(self, g1: BaseGraph[VT,ET], g2: BaseGraph[VT,ET]) -> None: pos2 = g2.qubit(v), g2.row(v) self.changed_pos[v] = pos2 - for e in new_edges: - if e in old_edges: + for e in g2.edges(): + if e in g1.edges(): if g1.edge_type(e) != g2.edge_type(e): self.changed_edge_types[e] = g2.edge_type(e) else: From 348fe152740779fa69ea2343c1ef84daff73a236 Mon Sep 17 00:00:00 2001 From: Razin Shaikh Date: Thu, 13 Jun 2024 20:45:17 +0100 Subject: [PATCH 5/7] mistake from last commit --- pyzx/graph/diff.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyzx/graph/diff.py b/pyzx/graph/diff.py index a190368a..b838e6fd 100644 --- a/pyzx/graph/diff.py +++ b/pyzx/graph/diff.py @@ -60,7 +60,7 @@ def calculate_diff(self, g1: BaseGraph[VT,ET], g2: BaseGraph[VT,ET]) -> None: self.new_edges.append((g2.edge_st(e), g2.edge_type(e))) g2_edges = list(g2.edges()) - removed_edges = [i for i in g1.edges() if not i in g2_edges] + removed_edges = [i for i in g1.edges() if not i in g2_edges or g2_edges.remove(i)] for e in (removed_edges): s,t = g1.edge_st(e) if s in self.removed_verts or t in self.removed_verts: continue From 33e045f60e3ee827501db126fe6c9b8d4e914079 Mon Sep 17 00:00:00 2001 From: Razin Shaikh Date: Thu, 13 Jun 2024 20:45:35 +0100 Subject: [PATCH 6/7] utility method to get edge count based on the edge type --- pyzx/graph/multigraph.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyzx/graph/multigraph.py b/pyzx/graph/multigraph.py index 4b602405..5d0f3a14 100644 --- a/pyzx/graph/multigraph.py +++ b/pyzx/graph/multigraph.py @@ -50,6 +50,11 @@ def remove(self, s: int=0, h: int=0, w_io: int=0): def is_empty(self) -> bool: return self.s == 0 and self.h == 0 and self.w_io == 0 + def get_edge_count(self, ty: EdgeType.Type) -> int: + if ty == EdgeType.SIMPLE: return self.s + elif ty == EdgeType.HADAMARD: return self.h + else: return self.w_io + class Multigraph(BaseGraph[int,Tuple[int,int,EdgeType.Type]]): """Purely Pythonic multigraph implementation of :class:`~graph.base.BaseGraph`.""" backend = 'multigraph' From 80c7d19b798d9ad1e1c355fb9a4906f94ead372b Mon Sep 17 00:00:00 2001 From: Razin Shaikh Date: Tue, 18 Jun 2024 12:25:10 +0100 Subject: [PATCH 7/7] use Counter multiset for edge_set --- pyzx/graph/diff.py | 14 ++++++-------- pyzx/graph/multigraph.py | 5 ++++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/pyzx/graph/diff.py b/pyzx/graph/diff.py index b838e6fd..5f22057a 100644 --- a/pyzx/graph/diff.py +++ b/pyzx/graph/diff.py @@ -51,17 +51,15 @@ def calculate_diff(self, g1: BaseGraph[VT,ET], g2: BaseGraph[VT,ET]) -> None: new_verts = g2.vertex_set() self.removed_verts = list(old_verts - new_verts) self.new_verts = list(new_verts - old_verts) + old_edges = g1.edge_set() + new_edges = g2.edge_set() self.new_edges = [] self.removed_edges = [] - g1_edges = list(g1.edges()) - new_edges = [i for i in g2.edges() if not i in g1_edges or g1_edges.remove(i)] - for e in (new_edges): + for e in (new_edges - old_edges): self.new_edges.append((g2.edge_st(e), g2.edge_type(e))) - g2_edges = list(g2.edges()) - removed_edges = [i for i in g1.edges() if not i in g2_edges or g2_edges.remove(i)] - for e in (removed_edges): + for e in (old_edges - new_edges): s,t = g1.edge_st(e) if s in self.removed_verts or t in self.removed_verts: continue self.removed_edges.append(e) @@ -86,8 +84,8 @@ def calculate_diff(self, g1: BaseGraph[VT,ET], g2: BaseGraph[VT,ET]) -> None: pos2 = g2.qubit(v), g2.row(v) self.changed_pos[v] = pos2 - for e in g2.edges(): - if e in g1.edges(): + for e in new_edges: + if e in old_edges: if g1.edge_type(e) != g2.edge_type(e): self.changed_edge_types[e] = g2.edge_type(e) else: diff --git a/pyzx/graph/multigraph.py b/pyzx/graph/multigraph.py index 5d0f3a14..7a2541ce 100644 --- a/pyzx/graph/multigraph.py +++ b/pyzx/graph/multigraph.py @@ -14,6 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from collections import Counter from fractions import Fraction from typing import Tuple, Dict, Set, Any @@ -304,8 +305,10 @@ def edges(self, s=None, t=None): def edge(self, s, t): return (s,t) if s < t else (t,s) + def edge_set(self): - return set(self.edges()) + return Counter(self.edges()) + def edge_st(self, edge): return (edge[0], edge[1])