1515from __future__ import annotations
1616
1717import abc
18- from typing import Optional , Sequence
18+ from typing import Optional , Sequence , Union
1919
2020import attrs
21+ import cirq
22+ import numpy as np
2123
2224import qualtran .rotation_synthesis ._typing as rst
2325import qualtran .rotation_synthesis .math_config as mc
26+ import qualtran .rotation_synthesis .matrix .clifford_t_repr as ctr
2427import qualtran .rotation_synthesis .rings as rings
2528from qualtran .rotation_synthesis .matrix import su2_ct
2629from qualtran .rotation_synthesis .rings import zsqrt2
@@ -33,7 +36,7 @@ def expected_num_ts(self, config: mc.MathConfig) -> rst.Real: ...
3336
3437 @abc .abstractmethod
3538 def diamond_norm_distance_to_rz (self , theta : rst .Real , config : mc .MathConfig ) -> rst .Real :
36- r"""Returns the diamond norm distance to $Rz(2 \theta) $."""
39+ r"""Returns the diamond norm distance to $e^{i \theta Z} $."""
3740
3841
3942@attrs .frozen
@@ -110,6 +113,79 @@ def from_sequence(cls, seq: Sequence[str], twirl: bool = False) -> UnitaryChanne
110113 n = sum (g .startswith ("T" ) for g in seq )
111114 return UnitaryChannel (u .matrix [0 , 0 ], u .matrix [1 , 0 ], n , twirl )
112115
116+ def to_cirq (self , fmt : str = "xz" , qs : Optional [Sequence [cirq .Qid ]] = None ) -> cirq .Circuit :
117+ """Retruns a representation of the channel as a cirq circuit.
118+
119+ Args:
120+ fmt: The gates to use (see the documentation of to_sequence).
121+ qs: Optional qubits to operate on.
122+ Returns:
123+ A cirq circuit
124+ Raises:
125+ ValueError: If twirl=True
126+ """
127+ if self .twirl :
128+ raise ValueError ("to_cirq is not supported when twirl=True" )
129+ if qs :
130+ (q ,) = qs
131+ else :
132+ q = cirq .q (0 )
133+ return cirq .Circuit (ctr .to_cirq (self .to_matrix (), fmt , q ))
134+
135+ def to_quirk (self , fmt : str = "xz" ) -> str :
136+ """Retruns a quirk link representing the channel operation.
137+
138+ Args:
139+ fmt: The gates to use (see the documentation of to_sequence).
140+ Returns:
141+ A quirk link.
142+ Raises:
143+ ValueError: If twirl=True
144+ """
145+ if self .twirl :
146+ raise ValueError ("to_quirk is not supported when twirl=True" )
147+ gates = ctr .to_quirk (self .to_matrix (), fmt )
148+ cols = '[' + ',' .join (f'[{ g } ]' for g in gates ) + ']'
149+ return "https://algassert.com/quirk#circuit={\" cols\" :%s}" % cols
150+
151+ def diamond_norm_distance_to_unitary (
152+ self , unitary : np .ndarray , config : mc .MathConfig
153+ ) -> rst .Real :
154+ r"""Returns the diamond norm distance between self and the given unitary.
155+
156+ From Theorem B.1 of arxiv:2203.10064, the diamond norm distance between two untiaries
157+ $U, V$ is $|v_1 - v_0|$ where $v_i$ are the eigen values of $V^\dagger U$. Geometrically
158+ this is the diameter of the smallest disc in the complex plane that contains both
159+ eigenvalues.
160+ """
161+ # W = V^\dagger U
162+ w = self .to_matrix ().adjoint ().numpy (config ) @ unitary
163+ # Compute the eigen values of W
164+ a = config .one
165+ b = - w [0 , 0 ] - w [1 , 1 ]
166+ c = w [0 , 0 ] * w [1 , 1 ] - w [0 , 1 ] * w [1 , 0 ]
167+ d = config .sqrt (b ** 2 - 4 * a * c )
168+ eigv0 , eigv1 = [(- b - d ) / (2 * a ), (- b + d ) / (2 * a )]
169+ # Compute the norm of the difference.
170+ diameter_vec = eigv1 - eigv0
171+ return config .sqrt (diameter_vec .real ** 2 + diameter_vec .imag ** 2 )
172+
173+ @classmethod
174+ def from_unitaries (
175+ cls , * unitaries : Union [UnitaryChannel , su2_ct .SU2CliffordT ]
176+ ) -> UnitaryChannel :
177+ if not unitaries :
178+ raise ValueError ('at least one unitary should be provided' )
179+
180+ unitary = su2_ct .ISqrt2
181+ for u in unitaries :
182+ if isinstance (u , UnitaryChannel ):
183+ unitary = unitary @ u .to_matrix ()
184+ else :
185+ unitary = unitary @ u
186+ unitary = unitary .rescale ()
187+ return UnitaryChannel (unitary .matrix [0 , 0 ], unitary .matrix [1 , 0 ], unitary .num_t_gates ())
188+
113189
114190@attrs .frozen
115191class ProjectiveChannel (Channel ):
@@ -173,6 +249,71 @@ def diamond_norm_distance_to_rz(self, theta: rst.Real, config: mc.MathConfig) ->
173249 theta - self .failure_angle (config ), config
174250 )
175251
252+ def to_cirq (self , fmt : str = "xz" , qs : Optional [Sequence [cirq .Qid ]] = None ) -> cirq .Circuit :
253+ """Retruns a representation of the channel as a cirq circuit.
254+
255+ Args:
256+ fmt: The gates to use (see the documentation of to_sequence).
257+ qs: Optional qubits to operate on.
258+ Returns:
259+ A cirq circuit
260+ Raises:
261+ ValueError: If the correction channel is not a UnitaryChannel.
262+ """
263+ if qs :
264+ q0 , q1 = qs
265+ else :
266+ q0 , q1 = cirq .LineQubit .range (2 )
267+ correction = self .correction
268+ if not isinstance (correction , UnitaryChannel ):
269+ raise ValueError ('to_cirq does not support a non unitary correction' )
270+ return cirq .Circuit (
271+ cirq .CNOT (q0 , q1 ),
272+ cirq .CircuitOperation (self .rotation .to_cirq (fmt , (q0 ,)).freeze ()),
273+ cirq .CNOT (q0 , q1 ),
274+ cirq .measure (q1 , key = 'm' ),
275+ cirq .CircuitOperation (correction .to_cirq (fmt , (q0 ,)).freeze ()).with_classical_controls (
276+ 'm'
277+ ),
278+ )
279+
280+ def to_quirk (self , fmt : str = "xz" ) -> str :
281+ """Retruns a quirk link representing the channel operation.
282+
283+ Args:
284+ fmt: The gates to use (see the documentation of to_sequence).
285+ Returns:
286+ A quirk link.
287+ """
288+ correction = self .correction
289+ if not isinstance (correction , UnitaryChannel ):
290+ raise ValueError (f"to_quirk is not supported for correction of type { type (correction )} " )
291+ rot = ctr .to_quirk (self .rotation .to_matrix (), fmt )
292+ cor = ctr .to_quirk (correction .to_matrix (), fmt )
293+ first_row = []
294+ second_row = []
295+ # CNOT
296+ first_row .append ("\" •\" " )
297+ second_row .append ("\" X\" " )
298+ # rotation
299+ first_row .extend (rot )
300+ second_row .extend ("1" for _ in rot )
301+ # CNOT
302+ first_row .append ("\" •\" " )
303+ second_row .append ("\" X\" " )
304+ # measure
305+ first_row .append ("1" )
306+ second_row .append ("\" Measure\" " )
307+ # correction
308+ first_row .extend (cor )
309+ second_row .extend ("\" •\" " for _ in cor )
310+ cols = (
311+ '['
312+ + ',' .join (f'[{ g1 } ,{ g2 } ]' for g1 , g2 in zip (first_row , second_row , strict = True ))
313+ + ']'
314+ )
315+ return "https://algassert.com/quirk#circuit={\" cols\" :%s}" % cols
316+
176317
177318@attrs .frozen
178319class ProbabilisticChannel (Channel ):
0 commit comments