|
26 | 26 |
|
27 | 27 | from cirq import value, protocols |
28 | 28 | from cirq._compat import proper_repr |
29 | | -from cirq.linalg import combinators, diagonalize, predicates |
| 29 | +from cirq.linalg import combinators, diagonalize, predicates, transformations |
30 | 30 |
|
31 | 31 | if TYPE_CHECKING: |
32 | 32 | import cirq |
|
36 | 36 | [0, 1j, 1, 0], |
37 | 37 | [0, 1j, -1, 0], |
38 | 38 | [1, 0, 0, -1j]]) * np.sqrt(0.5) |
| 39 | + |
39 | 40 | MAGIC_CONJ_T = np.conj(MAGIC.T) |
40 | 41 |
|
| 42 | +# yapf: disable |
| 43 | +YY = np.array([[0, 0, 0, -1], |
| 44 | + [0, 0, 1, 0], |
| 45 | + [0, 1, 0, 0], |
| 46 | + [-1, 0, 0, 0]]) |
| 47 | +# yapf: enable |
| 48 | + |
41 | 49 |
|
42 | 50 | def _phase_matrix(angle: float) -> np.ndarray: |
43 | 51 | return np.diag([1, np.exp(1j * angle)]) |
@@ -992,3 +1000,48 @@ def _canonicalize_kak_vector(k_vec: np.ndarray, atol: float) -> np.ndarray: |
992 | 1000 | k_vec[need_diff, 2] *= -1 |
993 | 1001 |
|
994 | 1002 | return k_vec |
| 1003 | + |
| 1004 | + |
| 1005 | +def num_cnots_required(u: np.ndarray, atol: float = 1e-8) -> int: |
| 1006 | + """Returns the min number of CNOT/CZ gates required by a two-qubit unitary. |
| 1007 | +
|
| 1008 | + See Proposition III.1, III.2, III.3 in Shende et al. “Recognizing Small- |
| 1009 | + Circuit Structure in Two-Qubit Operators and Timing Hamiltonians to Compute |
| 1010 | + Controlled-Not Gates”. https://arxiv.org/abs/quant-ph/0308045 |
| 1011 | +
|
| 1012 | + Args: |
| 1013 | + u: a two-qubit unitary |
| 1014 | + Returns: |
| 1015 | + the number of CNOT or CZ gates required to implement the unitary |
| 1016 | + """ |
| 1017 | + if u.shape != (4, 4): |
| 1018 | + raise ValueError(f"Expected unitary of shape (4,4), instead " |
| 1019 | + f"got {u.shape}") |
| 1020 | + g = _gamma(transformations.to_special(u)) |
| 1021 | + # see Fadeev-LeVerrier formula |
| 1022 | + a3 = -np.trace(g) |
| 1023 | + # no need to check a2 = 6, as a3 = +-4 only happens if the eigenvalues are |
| 1024 | + # either all +1 or -1, which unambiguously implies that a2 = 6 |
| 1025 | + if np.abs(a3 - 4) < atol or np.abs(a3 + 4) < atol: |
| 1026 | + return 0 |
| 1027 | + # see Fadeev-LeVerrier formula |
| 1028 | + a2 = (a3 * a3 - np.trace(g @ g)) / 2 |
| 1029 | + if np.abs(a3) < atol and np.abs(a2 - 2) < atol: |
| 1030 | + return 1 |
| 1031 | + if np.abs(a3.imag) < atol: |
| 1032 | + return 2 |
| 1033 | + return 3 |
| 1034 | + |
| 1035 | + |
| 1036 | +def _gamma(u: np.ndarray) -> np.ndarray: |
| 1037 | + """Gamma function to convert u to the magic basis. |
| 1038 | +
|
| 1039 | + See Definition IV.1 in Shende et al. "Minimal Universal Two-Qubit CNOT-based |
| 1040 | + Circuits." https://arxiv.org/abs/quant-ph/0308033 |
| 1041 | +
|
| 1042 | + Args: |
| 1043 | + u: a member of SU(4) |
| 1044 | + Returns: |
| 1045 | + u @ yy @ u.T @ yy, where yy = Y ⊗ Y |
| 1046 | + """ |
| 1047 | + return u @ YY @ u.T @ YY |
0 commit comments