Skip to content

Fix #179: added copy_simp and supplementarity_simp to full_reduce #332

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

Merged
merged 8 commits into from
Aug 19, 2025
Merged
Show file tree
Hide file tree
Changes from 7 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
15 changes: 9 additions & 6 deletions pyzx/simplify.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def clifford_simp(g: BaseGraph[VT,ET], matchf: Optional[Callable[[Union[VT, ET]]

def reduce_scalar(g: BaseGraph[VT,ET], quiet:bool=True, stats:Optional[Stats]=None) -> int:
"""Modification of ``full_reduce`` that is tailered for scalar ZX-diagrams.
It skips the boundary pivots, and it additionally does ``supplementarity_simp``."""
It skips the boundary pivots."""
i = 0
while True:
i1 = id_simp(g, quiet=quiet, stats=stats)
Expand All @@ -213,18 +213,19 @@ def reduce_scalar(g: BaseGraph[VT,ET], quiet:bool=True, stats:Optional[Stats]=No
continue
i5 = pivot_gadget_simp(g,quiet=quiet, stats=stats)
i6 = gadget_simp(g, quiet=quiet, stats=stats)
if i5 + i6:
i7 = copy_simp(g, quiet=quiet, stats=stats)
if i5 + i6 + i7:
i += 1
continue
i7 = supplementarity_simp(g,quiet=quiet, stats=stats)
if not i7: break
i8 = supplementarity_simp(g,quiet=quiet, stats=stats)
if not i8: break
i += 1
return i


def full_reduce(g: BaseGraph[VT,ET], matchf: Optional[Callable[[Union[VT, ET]],bool]]=None, quiet:bool=True, stats:Optional[Stats]=None) -> None:
"""The main simplification routine of PyZX. It uses a combination of :func:`clifford_simp` and
the gadgetization strategies :func:`pivot_gadget_simp` and :func:`gadget_simp`."""
the gadgetization strategies :func:`pivot_gadget_simp` and :func:`gadget_simp`. It also attempts to run supplementarity_simp and copy_simp."""
if any(g.types()[h] == VertexType.H_BOX for h in g.vertices()):
raise ValueError("Input graph is not a ZX-diagram as it contains an H-box. "
"Maybe call pyzx.hsimplify.from_hypergraph_form(g) first?")
Expand All @@ -234,8 +235,10 @@ def full_reduce(g: BaseGraph[VT,ET], matchf: Optional[Callable[[Union[VT, ET]],b
clifford_simp(g, matchf=matchf, quiet=quiet, stats=stats)
i = gadget_simp(g, matchf=matchf, quiet=quiet, stats=stats)
interior_clifford_simp(g, matchf=matchf, quiet=quiet, stats=stats)
k = copy_simp(g, quiet=quiet, stats=stats)
l = supplementarity_simp(g,quiet=True, stats=stats)
j = pivot_gadget_simp(g, matchf=matchf, quiet=quiet, stats=stats)
if i+j == 0:
if i+j+k+l == 0:
g.remove_isolated_vertices()
break

Expand Down
3 changes: 2 additions & 1 deletion tests/test_extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import sys
from types import ModuleType
from typing import Optional
from fractions import Fraction

if __name__ == '__main__':
sys.path.append('..')
Expand Down Expand Up @@ -48,7 +49,7 @@ class TestExtract(unittest.TestCase):
def test_simple_extract(self):
c = Circuit(1)
c.add_gate("HAD", 0)
c.add_gate("ZPhase", 0, phase=1/4)
c.add_gate("ZPhase", 0, phase= Fraction(1,4))

g = c.to_graph()

Expand Down
48 changes: 47 additions & 1 deletion tests/test_simplify.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from fractions import Fraction
from pyzx.generate import cliffordT
from pyzx.simplify import *
from pyzx.simplify import supplementarity_simp, to_clifford_normal_form_graph
from pyzx.simplify import supplementarity_simp, to_clifford_normal_form_graph, copy_simp
from pyzx import compare_tensors
from pyzx.generate import cliffordT

Expand Down Expand Up @@ -159,6 +159,52 @@ def test_to_clifford_normal_form_graph(self):
g0 = g.copy()
to_clifford_normal_form_graph(g)
self.assertTrue(compare_tensors(g0, g, preserve_scalar=True))

def test_copy_simp(self):
g = Graph()

v0 = g.add_vertex(VertexType.Z, 0, 0)
v1 = g.add_vertex(VertexType.X, 0, 1)
g.add_edge((v0, v1))
v2 = g.add_vertex(VertexType.BOUNDARY, 0, 3)
g.add_edge((v1, v2))
v3 = g.add_vertex(VertexType.BOUNDARY, 1, 3)
g.add_edge((v1, v3))

g1 = g.copy()
to_gh(g1)
copy_simp(g1)
Copy link
Collaborator

Choose a reason for hiding this comment

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

You should probably also assert that copy_simp actually simplified the graph, and that this is not a 'noop'.


g1.auto_detect_io()
g.auto_detect_io()

self.assertFalse(g.num_vertices() != g1.num_vertices())
self.assertTrue(g1.num_vertices() == 4)
self.assertTrue(compare_tensors(g1.to_tensor(),g.to_tensor()))


def test_copy_simp_full_reduce(self):
g = Graph()

v0 = g.add_vertex(VertexType.Z, 0, 0)
v1 = g.add_vertex(VertexType.X, 0, 1)
g.add_edge((v0, v1))
v2 = g.add_vertex(VertexType.BOUNDARY, 0, 3)
g.add_edge((v1, v2))
v3 = g.add_vertex(VertexType.BOUNDARY, 1, 3)
g.add_edge((v1, v3))

g1 = g.copy()
to_gh(g)
copy_simp(g)

full_reduce(g1)
g.auto_detect_io()
g1.auto_detect_io()

self.assertTrue(g.num_vertices() == g1.num_vertices())
self.assertTrue(compare_tensors(g1.to_tensor(),g.to_tensor()))



qasm_1 = """OPENQASM 2.0;
Expand Down