Skip to content

Commit 82edf6f

Browse files
committed
implement FT and inverse FT and added notebook
1 parent 58909b5 commit 82edf6f

File tree

2 files changed

+66
-8
lines changed

2 files changed

+66
-8
lines changed

demos/Fourier.ipynb

Whitespace-only changes.

pyzx/fourier.py

+66-8
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
# See the License for the specific language governing permissions and
1616
# limitations under the License.
1717

18+
from pyzx.graph.multigraph import Edge
1819
from .utils import EdgeType, VertexType
1920
from .graph.base import BaseGraph, VT, ET
2021
from itertools import combinations
22+
from fractions import Fraction
2123

2224
def check_fourier(g: BaseGraph[VT,ET], v: VT) -> bool:
2325
"""Returns True if the given node is an H-box adjacent to Z spiders"""
@@ -29,24 +31,80 @@ def check_fourier(g: BaseGraph[VT,ET], v: VT) -> bool:
2931
else:
3032
return False
3133

32-
def fourier(g: BaseGraph[VT, ET], v: VT) -> bool:
34+
def fourier(g: BaseGraph[VT, ET], v: VT, graph_like: bool=False) -> bool:
3335
"""Applies the graphical fourier transform, replacing the given H-box of arity k with
3436
2**k phase gadgets."""
3537
if not check_fourier(g, v): return False
3638

3739
q,r = g.qubit(v), g.row(v)
38-
nhd = tuple(g.neighbors(g))
39-
ph = g.phase(v) / (2**(len(nhd)-1))
40+
nhd = tuple(g.neighbors(v))
41+
ph = g.phase(v) * Fraction(1,2**(len(nhd)-1))
4042
g.remove_vertex(v)
41-
pos = r - 0.25*(2**len(nhd))
43+
pos = r - 0.5*(2**len(nhd) - len(nhd))
4244

43-
for weight in range(1, len(nhd)+1):
45+
if graph_like:
46+
et = EdgeType.HADAMARD
47+
vt = VertexType.Z
48+
else:
49+
et = EdgeType.SIMPLE
50+
vt = VertexType.X
51+
52+
for w in nhd:
53+
g.add_to_phase(w, ph)
54+
for weight in range(2, len(nhd)+1):
4455
for ws in combinations(nhd, weight):
4556
w1 = g.add_vertex(VertexType.Z, qubit=q-1, row=pos)
4657
g.set_phase(w1, (-1)**(weight-1) * ph)
47-
w2 = g.add_vertex(VertexType.X, qubit=q, row=pos)
48-
g.add_edge((w1,w2))
58+
w2 = g.add_vertex(vt, qubit=q, row=pos)
59+
g.add_edge((w1,w2), et)
4960
for w3 in ws:
50-
g.add_edge((w2, w3))
61+
g.add_edge((w2, w3), et)
5162
g.scalar.add_power(weight-1)
63+
pos += 1
64+
return True
65+
66+
def check_ifourier(g: BaseGraph[VT,ET], v: VT) -> bool:
67+
"""Returns True if the given node is the phase part of a phase gadget, and the gadget is
68+
connected to all Z spiders.
69+
70+
Note this accepts phase gadgets either in graph-like form, with Hadamard edges, or in the standard
71+
form of a Z-spider connected to a X-spider, with normal edges."""
72+
ty = g.types()
73+
if (ty[v] != VertexType.Z or g.vertex_degree(v) != 1):
74+
return False
75+
w = next(iter(g.neighbors(v)))
76+
77+
if not (
78+
(ty[w] == VertexType.X and all(g.edge_type(e) == EdgeType.SIMPLE for e in g.incident_edges(w))) or
79+
(ty[w] == VertexType.Z and all(g.edge_type(e) == EdgeType.HADAMARD for e in g.incident_edges(w)))
80+
): return False
81+
82+
return all(ty[w1] == VertexType.Z for w1 in g.neighbors(w))
83+
84+
def ifourier(g: BaseGraph[VT, ET], v: VT) -> bool:
85+
"""Applies the inverse graphical fourier transform, replacing the given phase gadget of arity k with
86+
2**k H-boxes.
87+
88+
To avoid ambiguity, the phase gadget should be specified as the arity-1 spider with the phase on it."""
89+
if not check_ifourier(g, v): return False
90+
91+
w = next(iter(g.neighbors(v)))
92+
93+
q,r = g.qubit(w), g.row(w)
94+
nhd = tuple(w for w in g.neighbors(w) if w != v)
95+
ph = g.phase(v)
96+
g.remove_vertex(v)
97+
g.remove_vertex(w)
98+
g.scalar.add_power(1-len(nhd))
99+
pos = r - 0.5*(2**len(nhd) - len(nhd))
100+
101+
for w in nhd:
102+
g.add_to_phase(w, ph)
103+
for weight in range(2, len(nhd)+1):
104+
for ws in combinations(nhd, weight):
105+
w1 = g.add_vertex(VertexType.H_BOX, qubit=q, row=pos)
106+
g.set_phase(w1, (-2)**(weight-1) * ph)
107+
for w2 in ws:
108+
g.add_edge((w1, w2))
109+
pos += 1
52110
return True

0 commit comments

Comments
 (0)