1616from typing import cast , Mapping , Optional
1717
1818import cirq
19+ import numpy as np
1920
2021import qualtran .rotation_synthesis .matrix ._generation as ctg
2122import qualtran .rotation_synthesis .matrix ._su2_ct as _su2_ct
@@ -79,16 +80,72 @@ def _xz_sequence(
7980 return None
8081
8182
83+ def _matsumoto_amano_syllable (matrix : _su2_ct .SU2CliffordT ) -> list [str ]:
84+ """Computes the next syllable in the Matsumoto-Amano decomposition for matrix.
85+
86+ Returns:
87+ the next syllable as a list of gates, representing either T, HT, or SHT.
88+
89+ Raises:
90+ ValueError if the parity matrix doesn't match any of the forms in
91+ Lemma 4.10, https://arxiv.org/abs/1312.6584.
92+ """
93+ parity = matrix .bloch_form_parity ()
94+ # Parity matrix must have a 0 column, see Lemma 4.10, https://arxiv.org/abs/1312.6584.
95+ # We move it to be last.
96+ for i in range (2 ):
97+ if np .all (parity [:, i ] == 0 ):
98+ parity [:, [i , 2 ]] = parity [:, [2 , i ]]
99+ break
100+ if np .array_equal (parity , np .array ([[1 , 1 , 0 ], [1 , 1 , 0 ], [0 , 0 , 0 ]])):
101+ # Leftmost syllable is T
102+ return ['T' ]
103+ elif np .array_equal (parity , np .array ([[0 , 0 , 0 ], [1 , 1 , 0 ], [1 , 1 , 0 ]])):
104+ # Leftmost syllable is HT
105+ return ['H' , 'T' ]
106+ elif np .array_equal (parity , np .array ([[1 , 1 , 0 ], [0 , 0 , 0 ], [1 , 1 , 0 ]])):
107+ # Leftmost syllable is SHT
108+ return ['S' , 'H' , 'T' ]
109+ else :
110+ raise ValueError (f'Unexpected parity matrix:\n { parity } ' )
111+
112+
113+ def _matsumoto_amano_sequence (matrix : _su2_ct .SU2CliffordT ) -> tuple [str , ...]:
114+ r"""Represents the Clifford+T operator in the Matsumoto-Amano normal form.
115+
116+ Returns:
117+ a list of gates matching the regular expression $(T|\eps)(HT|SHT)^*C$,
118+ where C is a Clifford operator, itself represented as a list of H and S gates.
119+ The gates are returned in the order they need to be applied to generate the
120+ input matrix.
121+
122+ Raises:
123+ ValueError if during the decomposition an invalid SU2CliffordT matrix is created.
124+ """
125+ gates = []
126+ while matrix .det () != _TWO :
127+ syllable = _matsumoto_amano_syllable (matrix )
128+ gates += syllable
129+ for gate in syllable :
130+ matrix = _su2_ct .GATE_MAP [gate ].adjoint () @ matrix
131+ new = matrix .scale_down ()
132+ if new is None or not new .is_valid ():
133+ raise ValueError ('Invalid SU2CliffordT matrix' )
134+ matrix = new
135+ return clifford (matrix ) + tuple (gates [::- 1 ])
136+
137+
82138def to_sequence (matrix : _su2_ct .SU2CliffordT , fmt : str = 'xyz' ) -> tuple [str , ...]:
83139 r"""Returns a sequence of Clifford+T that produces the given matrix.
84140
85141 Allowable format strings are
86142 - 'xyz' uses Tx, Ty, Tz gates.
87143 - 'xz' uses $Tx, Tz, Tx^\dagger, Tz^\dagger$
144+ - 't' uses only Tz gates, and returns the Matsumoto-Amano normal form.
88145
89146 Args:
90147 matrix: The matrix to represent.
91- fmt: A string from the set {'xz', 'xyz'} representing the allowed T gates described above.
148+ fmt: A string from the set {'xz', 'xyz', 't' } representing the allowed T gates described above.
92149
93150 Returns:
94151 A tuple of strings representing the gates.
@@ -100,6 +157,8 @@ def to_sequence(matrix: _su2_ct.SU2CliffordT, fmt: str = 'xyz') -> tuple[str, ..
100157 return _xyz_sequence (matrix )
101158 if fmt == 'xz' :
102159 return cast (tuple [str , ...], _xz_sequence (matrix ))
160+ if fmt == 't' :
161+ return _matsumoto_amano_sequence (matrix )
103162 raise ValueError (f'{ type = } is not supported' )
104163
105164
@@ -116,6 +175,7 @@ def to_sequence(matrix: _su2_ct.SU2CliffordT, fmt: str = 'xyz') -> tuple[str, ..
116175 "Tx*" : cirq .rx (- math .pi / 4 ),
117176 "Ty*" : cirq .ry (- math .pi / 4 ),
118177 "Tz*" : cirq .rz (- math .pi / 4 ),
178+ "T" : cirq .rz (math .pi / 4 ),
119179}
120180
121181
@@ -130,6 +190,8 @@ def _to_quirk_name(name: str, allow_global_phase: bool = False) -> str:
130190 if name == "S*" :
131191 return "\" Z^-½\" "
132192 if name .startswith ("T" ):
193+ if name == "T" :
194+ return "\" Z^¼\" "
133195 if name .endswith ("*" ):
134196 return "\" " + name [1 ].upper () + "^-¼" + "\" "
135197 return "\" " + name [1 ].upper () + "^¼" + "\" "
@@ -145,6 +207,8 @@ def _to_quirk_name(name: str, allow_global_phase: bool = False) -> str:
145207 if name == "S*" :
146208 return '{"id":"Rzft","arg":"-pi/2"}'
147209 if name .startswith ("T" ):
210+ if name == "T" :
211+ return '{"id":"Rzft","arg":"pi/4"}'
148212 angle = ['pi/4' , '-pi/4' ][name .endswith ('*' )]
149213 return '{"id":"R%sft","arg":"%s"}' % (name [1 ].lower (), angle )
150214 raise ValueError (f"{ name = } is not supported" )
0 commit comments