def kak_decomposition( mat: np.ndarray, rtol: float = DEFAULT_RTOL, atol: float = DEFAULT_ATOL) -> KakDecomposition: """Decomposes a 2-qubit unitary into 1-qubit ops and XX/YY/ZZ interactions. Args: mat: The 4x4 unitary matrix to decompose. rtol: Per-matrix-entry relative tolerance on equality. atol: Per-matrix-entry absolute tolerance on equality. Returns: A `cirq.KakDecomposition` canonicalized such that the interaction coefficients x, y, z satisfy: 0 ≤ abs(z) ≤ y ≤ x ≤ π/4 z ≠ -π/4 Raises: ValueError: Bad matrix. ArithmeticError: Failed to perform the decomposition. References: 'An Introduction to Cartan's KAK Decomposition for QC Programmers' https://arxiv.org/abs/quant-ph/0507171 """ magic = np.array([[1, 0, 0, 1j], [0, 1j, 1, 0], [0, 1j, -1, 0], [1, 0, 0, -1j]]) * np.sqrt(0.5) gamma = np.array([[1, 1, 1, 1], [1, 1, -1, -1], [-1, 1, -1, 1], [1, -1, -1, 1]]) * 0.25 # Diagonalize in magic basis. left, d, right = ( diagonalize.bidiagonalize_unitary_with_special_orthogonals( combinators.dot(np.conj(magic.T), mat, magic), atol=atol, rtol=rtol)) # Recover pieces. a1, a0 = so4_to_magic_su2s(left.T, atol=atol, rtol=rtol) b1, b0 = so4_to_magic_su2s(right.T, atol=atol, rtol=rtol) w, x, y, z = gamma.dot(np.vstack(np.angle(d))).flatten() g = np.exp(1j * w) # Canonicalize. inner_cannon = kak_canonicalize_vector(x, y, z) b1 = np.dot(inner_cannon.single_qubit_operations_before[0], b1) b0 = np.dot(inner_cannon.single_qubit_operations_before[1], b0) a1 = np.dot(a1, inner_cannon.single_qubit_operations_after[0]) a0 = np.dot(a0, inner_cannon.single_qubit_operations_after[1]) return KakDecomposition( interaction_coefficients=inner_cannon.interaction_coefficients, global_phase=g * inner_cannon.global_phase, single_qubit_operations_before=(b1, b0), single_qubit_operations_after=(a1, a0))
def kak_decomposition( mat: np.ndarray, tolerance: Tolerance = Tolerance.DEFAULT ) -> Tuple[complex, Tuple[np.ndarray, np.ndarray], Tuple[float, float, float], Tuple[np.ndarray, np.ndarray]]: """Decomposes a 2-qubit unitary into 1-qubit ops and XX/YY/ZZ interactions. Args: mat: The 4x4 unitary matrix to decompose. tolerance: Per-matrix-entry tolerance on equality. Returns: A nested tuple (g, (a1, a0), (x, y, z), (b1, b0)) containing: 0. A global phase factor. 1. The pre-operation matrices to apply to the second/firs qubit. 2. The XX/YY/ZZ weights of the non-local operation. 3. The post-operation matrices to apply to the second/firs qubit. Guarantees that the x2, y2, z2 are canonicalized to satisfy: 0 ≤ abs(z) ≤ y ≤ x ≤ π/4 z ≠ -π/4 Guarantees that the input matrix should approximately equal: g · (a1 ⊗ a0) · exp(i·(x·XX + y·YY + z·ZZ)) · (b1 ⊗ b0) Raises: ValueError: Bad matrix. ArithmeticError: Failed to perform the decomposition. References: 'An Introduction to Cartan's KAK Decomposition for QC Programmers' https://arxiv.org/abs/quant-ph/0507171 """ magic = np.array([[1, 0, 0, 1j], [0, 1j, 1, 0], [0, 1j, -1, 0], [1, 0, 0, -1j]]) * np.sqrt(0.5) gamma = np.array([[1, 1, 1, 1], [1, 1, -1, -1], [-1, 1, -1, 1], [1, -1, -1, 1]]) * 0.25 # Diagonalize in magic basis. left, d, right = ( diagonalize.bidiagonalize_unitary_with_special_orthogonals( combinators.dot(np.conj(magic.T), mat, magic), tolerance)) # Recover pieces. a1, a0 = so4_to_magic_su2s(left.T, tolerance) b1, b0 = so4_to_magic_su2s(right.T, tolerance) w, x, y, z = gamma.dot(np.vstack(np.angle(d))).flatten() g = np.exp(1j * w) # Canonicalize. g2, (c1, c0), (x2, y2, z2), (d1, d0) = kak_canonicalize_vector(x, y, z) return (g * g2, (a1.dot(c1), a0.dot(c0)), (x2, y2, z2), (d1.dot(b1), d0.dot(b0)))
def kak_decomposition(mat: np.ndarray, rtol: float = 1e-5, atol: float = 1e-8) -> KakDecomposition: """Decomposes a 2-qubit unitary into 1-qubit ops and XX/YY/ZZ interactions. Args: mat: The 4x4 unitary matrix to decompose. rtol: Per-matrix-entry relative tolerance on equality. atol: Per-matrix-entry absolute tolerance on equality. Returns: A `cirq.KakDecomposition` canonicalized such that the interaction coefficients x, y, z satisfy: 0 ≤ abs(z2) ≤ y2 ≤ x2 ≤ π/4 if x2 = π/4, z2 >= 0 Raises: ValueError: Bad matrix. ArithmeticError: Failed to perform the decomposition. References: 'An Introduction to Cartan's KAK Decomposition for QC Programmers' https://arxiv.org/abs/quant-ph/0507171 """ # Diagonalize in magic basis. left, d, right = diagonalize.bidiagonalize_unitary_with_special_orthogonals( KAK_MAGIC_DAG @ mat @ KAK_MAGIC, atol=atol, rtol=rtol, check_preconditions=False) # Recover pieces. a1, a0 = so4_to_magic_su2s(left.T, atol=atol, rtol=rtol, check_preconditions=False) b1, b0 = so4_to_magic_su2s(right.T, atol=atol, rtol=rtol, check_preconditions=False) w, x, y, z = (KAK_GAMMA @ np.vstack(np.angle(d))).flatten() g = np.exp(1j * w) # Canonicalize. inner_cannon = kak_canonicalize_vector(x, y, z) b1 = np.dot(inner_cannon.single_qubit_operations_before[0], b1) b0 = np.dot(inner_cannon.single_qubit_operations_before[1], b0) a1 = np.dot(a1, inner_cannon.single_qubit_operations_after[0]) a0 = np.dot(a0, inner_cannon.single_qubit_operations_after[1]) return KakDecomposition( interaction_coefficients=inner_cannon.interaction_coefficients, global_phase=g * inner_cannon.global_phase, single_qubit_operations_before=(b1, b0), single_qubit_operations_after=(a1, a0))
def kak_decomposition( unitary_object: Union[np.ndarray, 'cirq.SupportsUnitary'], *, rtol: float = 1e-5, atol: float = 1e-8, check_preconditions: bool = True, ) -> KakDecomposition: """Decomposes a 2-qubit unitary into 1-qubit ops and XX/YY/ZZ interactions. Args: unitary_object: The value to decompose. Can either be a 4x4 unitary matrix, or an object that has a 4x4 unitary matrix (via the `cirq.SupportsUnitary` protocol). rtol: Per-matrix-entry relative tolerance on equality. atol: Per-matrix-entry absolute tolerance on equality. check_preconditions: If set, verifies that the input corresponds to a 4x4 unitary before decomposing. Returns: A `cirq.KakDecomposition` canonicalized such that the interaction coefficients x, y, z satisfy: 0 ≤ abs(z2) ≤ y2 ≤ x2 ≤ π/4 if x2 = π/4, z2 >= 0 Raises: ValueError: Bad matrix. ArithmeticError: Failed to perform the decomposition. References: 'An Introduction to Cartan's KAK Decomposition for QC Programmers' https://arxiv.org/abs/quant-ph/0507171 """ if isinstance(unitary_object, KakDecomposition): return unitary_object if isinstance(unitary_object, np.ndarray): mat = unitary_object else: mat = protocols.unitary(unitary_object) if check_preconditions and ( mat.shape != (4, 4) or not predicates.is_unitary(mat, rtol=rtol, atol=atol)): raise ValueError( 'Input must correspond to a 4x4 unitary matrix. Received matrix:\n' + str(mat)) # Diagonalize in magic basis. left, d, right = diagonalize.bidiagonalize_unitary_with_special_orthogonals( KAK_MAGIC_DAG @ mat @ KAK_MAGIC, atol=atol, rtol=rtol, check_preconditions=False) # Recover pieces. a1, a0 = so4_to_magic_su2s(left.T, atol=atol, rtol=rtol, check_preconditions=False) b1, b0 = so4_to_magic_su2s(right.T, atol=atol, rtol=rtol, check_preconditions=False) w, x, y, z = (KAK_GAMMA @ np.vstack(np.angle(d))).flatten() g = np.exp(1j * w) # Canonicalize. inner_cannon = kak_canonicalize_vector(x, y, z) b1 = np.dot(inner_cannon.single_qubit_operations_before[0], b1) b0 = np.dot(inner_cannon.single_qubit_operations_before[1], b0) a1 = np.dot(a1, inner_cannon.single_qubit_operations_after[0]) a0 = np.dot(a0, inner_cannon.single_qubit_operations_after[1]) return KakDecomposition( interaction_coefficients=inner_cannon.interaction_coefficients, global_phase=g * inner_cannon.global_phase, single_qubit_operations_before=(b1, b0), single_qubit_operations_after=(a1, a0), )
def kak_decomposition( mat: np.ndarray, tolerance: Tolerance = Tolerance.DEFAULT ) -> Tuple[complex, Tuple[np.ndarray, np.ndarray], Tuple[float, float, float], Tuple[np.ndarray, np.ndarray]]: """Decomposes a 2-qubit unitary into 1-qubit ops and XX/YY/ZZ interactions. Args: mat: The 4x4 unitary matrix to decompose. tolerance: Per-matrix-entry tolerance on equality. Returns: A nested tuple (g, (a1, a0), (x, y, z), (b1, b0)) containing: 0. A global phase factor. 1. The pre-operation matrices to apply to the second/firs qubit. 2. The XX/YY/ZZ weights of the non-local operation. 3. The post-operation matrices to apply to the second/firs qubit. Guarantees that the x2, y2, z2 are canonicalized to satisfy: 0 ≤ abs(z) ≤ y ≤ x ≤ π/4 z ≠ -π/4 Guarantees that the input matrix should approximately equal: g · (a1 ⊗ a0) · exp(i·(x·XX + y·YY + z·ZZ)) · (b1 ⊗ b0) Raises: ValueError: Bad matrix. ArithmeticError: Failed to perform the decomposition. References: 'An Introduction to Cartan's KAK Decomposition for QC Programmers' https://arxiv.org/abs/quant-ph/0507171 """ magic = np.array([[1, 0, 0, 1j], [0, 1j, 1, 0], [0, 1j, -1, 0], [1, 0, 0, -1j]]) * np.sqrt(0.5) gamma = np.array([[1, 1, 1, 1], [1, 1, -1, -1], [-1, 1, -1, 1], [1, -1, -1, 1]]) * 0.25 # Diagonalize in magic basis. left, d, right = ( diagonalize.bidiagonalize_unitary_with_special_orthogonals( combinators.dot(np.conj(magic.T), mat, magic), tolerance)) # Recover pieces. a1, a0 = so4_to_magic_su2s(left.T, tolerance) b1, b0 = so4_to_magic_su2s(right.T, tolerance) w, x, y, z = gamma.dot(np.vstack(np.angle(d))).flatten() g = np.exp(1j * w) # Canonicalize. g2, (c1, c0), (x2, y2, z2), (d1, d0) = kak_canonicalize_vector(x, y, z) return ( g * g2, (a1.dot(c1), a0.dot(c0)), (x2, y2, z2), (d1.dot(b1), d0.dot(b0)) )
def test_bidiagonalize_unitary_fails(mat): with pytest.raises(ValueError): diagonalize.bidiagonalize_unitary_with_special_orthogonals(mat)
def test_bidiagonalize_unitary_with_special_orthogonals(mat): p, d, q = diagonalize.bidiagonalize_unitary_with_special_orthogonals(mat) assert predicates.is_special_orthogonal(p) assert predicates.is_special_orthogonal(q) assert np.allclose(p.dot(mat).dot(q), np.diag(d)) assert_bidiagonalized_by(mat, p, q)