diff --git a/pyzx/pauliwebs.py b/pyzx/pauliwebs.py new file mode 100644 index 00000000..0a63bd2c --- /dev/null +++ b/pyzx/pauliwebs.py @@ -0,0 +1,55 @@ +# PyZX - Python library for quantum circuit rewriting +# and optimization using the ZX-calculus +# Copyright (C) 2024 - 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 .circuit import Circuit +from .utils import EdgeType, VertexType +from .simplify import gadgetize, to_rg +from .graph.base import BaseGraph + + +def preprocess(g: BaseGraph): + gadgetize(g) + to_rg(g) # TODO: i/o should stay Z + in_circ = Circuit() + out_circ = Circuit() + for j,i in enumerate(g.inputs()): + e = g.incident_edges(i)[0] + v = g.neighbors(i)[0] + p = g.phase(v) + if g.edge_type(e) == EdgeType.HADAMARD: + in_circ.add_gate('H', j) + g.set_edge_type(e, EdgeType.SIMPLE) + if p != 0: + g.set_phase(v, 0) + in_circ.add_gate("ZPhase", j, phase=p) + + for j,o in enumerate(g.outputs()): + r = g.get_row(o) + g.set_row(o, r + 1) + e = g.incident_edges(o)[0] + v = g.neighbors(o)[0] + p = g.phase(v) + if p != 0: + g.set_phase(v, 0) + out_circ.add_gate("ZPhase", j, phase=p) + + if g.edge_type(e) == EdgeType.HADAMARD: + out_circ.add_gate('H', j) + g.remove_edge(e) + + v1 = g.add_vertex(VertexType.Z, qubit=g.qubit(o), row=r) + g.add_edge((v,v1), EdgeType.HADAMARD) + g.add_edge((v1,o), EdgeType.HADAMARD) \ No newline at end of file diff --git a/pyzx/simplify.py b/pyzx/simplify.py index 582580ae..77f3c5ba 100644 --- a/pyzx/simplify.py +++ b/pyzx/simplify.py @@ -32,7 +32,7 @@ from optparse import Option from typing import List, Callable, Optional, Union, Generic, Tuple, Dict, Iterator, cast -from .utils import EdgeType, VertexType, toggle_edge, vertex_is_zx, toggle_vertex +from .utils import EdgeType, VertexType, phase_is_clifford, toggle_edge, vertex_is_zx, toggle_vertex from .rules import * from .graph.base import BaseGraph, VT, ET from .graph.multigraph import Multigraph @@ -361,6 +361,17 @@ def to_rg(g: BaseGraph[VT,ET], select:Optional[Callable[[VT],bool]]=None, change for e in g.incident_edges(v): g.set_edge_type(e, toggle_edge(g.edge_type(e))) +def gadgetize(g: BaseGraph): + """Convert every non-Clifford phase to a phase gadget""" + for v in list(g.vertices()): + p = g.phase(v) + if not phase_is_clifford(p) and g.vertex_degree(v) > 1: + x = g.add_vertex(VertexType.Z, -1, g.row(v)) + y = g.add_vertex(VertexType.Z, -2, g.row(v)) + g.add_edge((x, y), EdgeType.HADAMARD) + g.add_edge((v, x), EdgeType.HADAMARD) + g.set_phase(y, p) + g.set_phase(v, 0) def tcount(g: Union[BaseGraph[VT,ET], Circuit]) -> int: """Returns the amount of nodes in g that have a non-Clifford phase.""" @@ -369,7 +380,7 @@ def tcount(g: Union[BaseGraph[VT,ET], Circuit]) -> int: count = 0 phases = g.phases() for v in g.vertices(): - if phases[v]!=0 and phases[v].denominator > 2: + if not phase_is_clifford(phases[v]): count += 1 return count