Skip to content

Commit

Permalink
Added function GraphDiff that calculates what actions are needed to b…
Browse files Browse the repository at this point in the history
…ring one graph to another graph. This is used in ZXLive.
  • Loading branch information
jvdwetering committed Jul 8, 2023
1 parent 93f9a6b commit fa801fc
Show file tree
Hide file tree
Showing 8 changed files with 4,644 additions and 116 deletions.
9 changes: 9 additions & 0 deletions Instructions for uploading to PyPI.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Update version numbers in setup.py, doc/conf.py, pyzx/__init__.py
Delete dist folder
python -m build
(this require pip install build)
twine check dist/*
(this requires twine to be installed: pip install twine)
twine upload dist/*
username: jmmwetering
password:
1 change: 1 addition & 0 deletions pyzx/graph/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@
from .graph import Graph
from .scalar import Scalar
from .base import EdgeType, VertexType, toggle_edge, vertex_is_zx, toggle_vertex
from .diff import GraphDiff
7 changes: 7 additions & 0 deletions pyzx/graph/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,13 @@ def add_vertex(self,
self.phase_mult[self.max_phase_index] = 1
return v

def add_vertex_indexed(self,v:VT) -> None:
"""Adds a vertex that is guaranteed to have the chosen index (i.e. 'name').
If the index isn't available, raises a ValueError.
This method is used in the editor and ZXLive to support undo,
which requires vertices to preserve their index."""
raise NotImplementedError("Not implemented on backend " + type(self).backend)

def add_edges(self, edges: Iterable[ET], edgetype:EdgeType.Type=EdgeType.SIMPLE) -> None:
"""Adds a list of edges to the graph."""
raise NotImplementedError("Not implemented on backend " + type(self).backend)
Expand Down
117 changes: 117 additions & 0 deletions pyzx/graph/diff.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# PyZX - Python library for quantum circuit rewriting
# and optimization using the ZX-calculus
# Copyright (C) 2018 - Aleks Kissinger and John van de Wetering

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Generic,Optional, List, Dict, Tuple
import copy

from ..utils import VertexType, EdgeType, FractionLike, FloatInt
from .base import BaseGraph, VT, ET
from .graph_s import GraphS

class GraphDiff(Generic[VT, ET]):
removed_verts: List[VT]
new_verts: List[VT]
removed_edges: List[ET]
new_edges: List[ET]
changed_vertex_types: Dict[VT,VertexType.Type]
changed_edge_types: Dict[ET, EdgeType.Type]
changed_phases: Dict[VT, FractionLike]
changed_pos: Dict[VT, Tuple[FloatInt,FloatInt]]

def __init__(self, g1: BaseGraph[VT,ET], g2: BaseGraph[VT,ET]) -> None:
self.calculate_diff(g1,g2)

def calculate_diff(self, g1: BaseGraph[VT,ET], g2: BaseGraph[VT,ET]) -> None:
self.changed_vertex_types = {}
self.changed_edge_types = {}
self.changed_phases = {}
self.changed_pos = {}

old_verts = g1.vertex_set()
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 = list(new_edges - old_edges)
self.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)

for v in new_verts:
if v in old_verts:
if g1.type(v) != g2.type(v):
self.changed_vertex_types[v] = g2.type(v)
if g1.phase(v) != g2.phase(v):
self.changed_phases[v] = g2.phase(v)
pos1 = g1.qubit(v), g1.row(v)
pos2 = g2.qubit(v), g2.row(v)
if pos1 != pos2:
self.changed_pos[v] = pos2
else: # It is a new vertex
if g2.type(v) != VertexType.Z: # We are taking the Z type to be the default
self.changed_vertex_types[v] = g2.type(v)
if g2.phase(v) != 0:
self.changed_phases[v] = g2.phase(v)
pos2 = g2.row(v), g2.qubit(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)

def apply_diff(self,g: BaseGraph[VT,ET]) -> BaseGraph[VT,ET]:
g = copy.deepcopy(g)
g.remove_vertices(self.removed_verts)
g.remove_edges(self.removed_edges)
for v in self.new_verts:
g.add_vertex_indexed(v)
g.set_position(v,*self.changed_pos[v])
if v in self.changed_vertex_types:
g.set_type(v,self.changed_vertex_types[v])
else:
g.set_type(v,VertexType.Z)
if v in self.changed_phases:
g.set_phase(v,self.changed_phases[v])
for e in self.new_edges:
ty:EdgeType.Type = EdgeType.HADAMARD
if e in self.changed_edge_types:
ty = self.changed_edge_types[e]
g.add_edge(e,ty)

for v in self.changed_pos:
if v in self.new_verts: continue
g.set_position(v,*self.changed_pos[v])

for v in self.changed_vertex_types:
if v in self.new_verts: continue
g.set_type(v,self.changed_vertex_types[v])

for v in self.changed_phases:
if v in self.new_verts: continue
g.set_phase(v,self.changed_phases[v])

for e in self.changed_edge_types:
if e in self.new_edges: continue
g.set_edge_type(e,self.changed_edge_types[e])

return g
12 changes: 6 additions & 6 deletions pyzx/graph/graph_s.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,16 @@ def add_vertices(self, amount):
self._phase[i] = 0
self._vindex += amount
return range(self._vindex - amount, self._vindex)
def add_vertex_indexed(self, index):
def add_vertex_indexed(self, v):
"""Adds a vertex that is guaranteed to have the chosen index (i.e. 'name').
If the index isn't available, raises a ValueError.
This method is used in the editor to support undo, which requires vertices
to preserve their index."""
if index in self.graph: raise ValueError("Vertex with this index already exists")
if index >= self._vindex: self._vindex = index+1
self.graph[index] = dict()
self.ty[index] = VertexType.BOUNDARY
self._phase[index] = 0
if v in self.graph: raise ValueError("Vertex with this index already exists")
if v >= self._vindex: self._vindex = index+1
self.graph[v] = dict()
self.ty[v] = VertexType.BOUNDARY
self._phase[v] = 0

def add_edges(self, edges, edgetype=EdgeType.SIMPLE, smart=False):
for s,t in edges:
Expand Down
1 change: 1 addition & 0 deletions run jupyter.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
jupyter notebook
Loading

0 comments on commit fa801fc

Please sign in to comment.