15
15
# See the License for the specific language governing permissions and
16
16
# limitations under the License.
17
17
18
+ from pyzx .graph .multigraph import Edge
18
19
from .utils import EdgeType , VertexType
19
20
from .graph .base import BaseGraph , VT , ET
20
21
from itertools import combinations
22
+ from fractions import Fraction
21
23
22
24
def check_fourier (g : BaseGraph [VT ,ET ], v : VT ) -> bool :
23
25
"""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:
29
31
else :
30
32
return False
31
33
32
- def fourier (g : BaseGraph [VT , ET ], v : VT ) -> bool :
34
+ def fourier (g : BaseGraph [VT , ET ], v : VT , graph_like : bool = False ) -> bool :
33
35
"""Applies the graphical fourier transform, replacing the given H-box of arity k with
34
36
2**k phase gadgets."""
35
37
if not check_fourier (g , v ): return False
36
38
37
39
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 ))
40
42
g .remove_vertex (v )
41
- pos = r - 0.25 * (2 ** len (nhd ))
43
+ pos = r - 0.5 * (2 ** len ( nhd ) - len (nhd ))
42
44
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 ):
44
55
for ws in combinations (nhd , weight ):
45
56
w1 = g .add_vertex (VertexType .Z , qubit = q - 1 , row = pos )
46
57
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 )
49
60
for w3 in ws :
50
- g .add_edge ((w2 , w3 ))
61
+ g .add_edge ((w2 , w3 ), et )
51
62
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
52
110
return True
0 commit comments