Ejemplo n.º 1
0
 def negate(k1, k2):
     v[k1] *= -1
     v[k2] *= -1
     phase[0] *= -1
     s = flippers[3 - k1 - k2]  # The other axis' flipper.
     left[1] = combinators.dot(left[1], s)
     right[1] = combinators.dot(s, right[1])
Ejemplo n.º 2
0
 def swap(k1, k2):
     v[k1], v[k2] = v[k2], v[k1]
     s = swappers[3 - k1 - k2]  # The other axis' swapper.
     left[0] = combinators.dot(left[0], s)
     left[1] = combinators.dot(left[1], s)
     right[0] = combinators.dot(s, right[0])
     right[1] = combinators.dot(s, right[1])
Ejemplo n.º 3
0
 def negate(k1, k2):
     v[k1] *= -1
     v[k2] *= -1
     phase[0] *= -1
     s = flippers[3 - k1 - k2]  # The other axis' flipper.
     left[1] = combinators.dot(left[1], s)
     right[1] = combinators.dot(s, right[1])
Ejemplo n.º 4
0
 def swap(k1, k2):
     v[k1], v[k2] = v[k2], v[k1]
     s = swappers[3 - k1 - k2]  # The other axis' swapper.
     left[0] = combinators.dot(left[0], s)
     left[1] = combinators.dot(left[1], s)
     right[0] = combinators.dot(s, right[0])
     right[1] = combinators.dot(s, right[1])
Ejemplo n.º 5
0
def random_bi_diagonalizable_pair(
        n: int,
        d1: Optional[int] = None,
        d2: Optional[int] = None) -> Tuple[np.ndarray, np.ndarray]:
    u = testing.random_orthogonal(n)
    s = random_real_diagonal_matrix(n, d1)
    z = random_real_diagonal_matrix(n, d2)
    v = testing.random_orthogonal(n)
    a = combinators.dot(u, s, v)
    b = combinators.dot(u, z, v)
    return a, b
Ejemplo n.º 6
0
def so4_to_magic_su2s(
    mat: np.ndarray, *, rtol: float = 1e-5, atol: float = 1e-8, check_preconditions: bool = True
) -> Tuple[np.ndarray, np.ndarray]:
    """Finds 2x2 special-unitaries A, B where mat = Mag.H @ kron(A, B) @ Mag.

    Mag is the magic basis matrix:

        1  0  0  i
        0  i  1  0
        0  i -1  0     (times sqrt(0.5) to normalize)
        1  0  0 -i

    Args:
        mat: A real 4x4 orthogonal matrix.
        rtol: Per-matrix-entry relative tolerance on equality.
        atol: Per-matrix-entry absolute tolerance on equality.
        check_preconditions: When set, the code verifies that the given
            matrix is from SO(4). Defaults to set.

    Returns:
        A pair (A, B) of matrices in SU(2) such that Mag.H @ kron(A, B) @ Mag
        is approximately equal to the given matrix.

    Raises:
        ValueError: Bad matrix.
    """
    if check_preconditions:
        if mat.shape != (4, 4) or not predicates.is_special_orthogonal(mat, atol=atol, rtol=rtol):
            raise ValueError('mat must be 4x4 special orthogonal.')

    ab = combinators.dot(MAGIC, mat, MAGIC_CONJ_T)
    _, a, b = kron_factor_4x4_to_2x2s(ab)

    return a, b
Ejemplo n.º 7
0
def so4_to_magic_su2s(
        mat: np.ndarray,
        tolerance: Tolerance = Tolerance.DEFAULT
) -> Tuple[np.ndarray, np.ndarray]:
    """Finds 2x2 special-unitaries A, B where mat = Mag.H @ kron(A, B) @ Mag.

    Mag is the magic basis matrix:

        1  0  0  i
        0  i  1  0
        0  i -1  0     (times sqrt(0.5) to normalize)
        1  0  0 -i

    Args:
        mat: A real 4x4 orthogonal matrix.
        tolerance: Per-matrix-entry tolerance on equality.

    Returns:
        A pair (A, B) of matrices in SU(2) such that Mag.H @ kron(A, B) @ Mag
        is approximately equal to the given matrix.

    Raises:
        ValueError: Bad matrix.
        """
    if mat.shape != (4, 4) or not predicates.is_special_orthogonal(
            mat, tolerance):
        raise ValueError('mat must be 4x4 special orthogonal.')

    ab = combinators.dot(MAGIC, mat, MAGIC_CONJ_T)
    _, a, b = kron_factor_4x4_to_2x2s(ab, tolerance)

    return a, b
Ejemplo n.º 8
0
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))
Ejemplo n.º 9
0
def so4_to_magic_su2s(
        mat: np.ndarray,
        tolerance: Tolerance = Tolerance.DEFAULT
) -> Tuple[np.ndarray, np.ndarray]:
    """Finds 2x2 special-unitaries A, B where mat = Mag.H @ kron(A, B) @ Mag.

    Mag is the magic basis matrix:

        1  0  0  i
        0  i  1  0
        0  i -1  0     (times sqrt(0.5) to normalize)
        1  0  0 -i

    Args:
        mat: A real 4x4 orthogonal matrix.
        tolerance: Per-matrix-entry tolerance on equality.

    Returns:
        A pair (A, B) of matrices in SU(2) such that Mag.H @ kron(A, B) @ Mag
        is approximately equal to the given matrix.

    Raises:
        ValueError: Bad matrix.
        ArithmeticError: Failed to perform the decomposition to desired
            tolerance.
        """
    if mat.shape != (4, 4) or not predicates.is_special_orthogonal(mat,
                                                                   tolerance):
        raise ValueError('mat must be 4x4 special orthogonal.')

    magic = np.array([[1, 0, 0, 1j],
                      [0, 1j, 1, 0],
                      [0, 1j, -1, 0],
                      [1, 0, 0, -1j]]) * np.sqrt(0.5)
    ab = combinators.dot(magic, mat, np.conj(magic.T))
    _, a, b = kron_factor_4x4_to_2x2s(ab, tolerance)

    # Check decomposition against desired tolerance.
    reconstructed = combinators.dot(np.conj(magic.T),
                                    combinators.kron(a, b),
                                    magic)
    if not tolerance.all_close(reconstructed, mat):
        raise ArithmeticError('Failed to decompose to desired tolerance.')

    return a, b
Ejemplo n.º 10
0
def so4_to_magic_su2s(
        mat: np.ndarray,
        tolerance: Tolerance = Tolerance.DEFAULT
) -> Tuple[np.ndarray, np.ndarray]:
    """Finds 2x2 special-unitaries A, B where mat = Mag.H @ kron(A, B) @ Mag.

    Mag is the magic basis matrix:

        1  0  0  i
        0  i  1  0
        0  i -1  0     (times sqrt(0.5) to normalize)
        1  0  0 -i

    Args:
        mat: A real 4x4 orthogonal matrix.
        tolerance: Per-matrix-entry tolerance on equality.

    Returns:
        A pair (A, B) of matrices in SU(2) such that Mag.H @ kron(A, B) @ Mag
        is approximately equal to the given matrix.

    Raises:
        ValueError: Bad matrix.
        ArithmeticError: Failed to perform the decomposition to desired
            tolerance.
        """
    if mat.shape != (4, 4) or not predicates.is_special_orthogonal(mat,
                                                                   tolerance):
        raise ValueError('mat must be 4x4 special orthogonal.')

    magic = np.array([[1, 0, 0, 1j],
                    [0, 1j, 1, 0],
                    [0, 1j, -1, 0],
                    [1, 0, 0, -1j]]) * np.sqrt(0.5)
    ab = combinators.dot(magic, mat, np.conj(magic.T))
    _, a, b = kron_factor_4x4_to_2x2s(ab, tolerance)

    # Check decomposition against desired tolerance.
    reconstructed = combinators.dot(np.conj(magic.T),
                                    combinators.kron(a, b),
                                    magic)
    if not tolerance.all_close(reconstructed, mat):
        raise ArithmeticError('Failed to decompose to desired tolerance.')

    return a, b
Ejemplo n.º 11
0
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)))
Ejemplo n.º 12
0
def recompose_so4(a: np.ndarray, b: np.ndarray) -> np.ndarray:
    assert a.shape == (2, 2)
    assert b.shape == (2, 2)
    assert linalg.is_special_unitary(a)
    assert linalg.is_special_unitary(b)

    magic = np.array([[1, 0, 0, 1j], [0, 1j, 1, 0], [0, 1j, -1, 0],
                      [1, 0, 0, -1j]]) * np.sqrt(0.5)
    result = np.real(
        combinators.dot(np.conj(magic.T), linalg.kron(a, b), magic))
    assert linalg.is_orthogonal(result)
    return result
def bidiagonalize_unitary_with_special_orthogonals(
    mat: np.ndarray,
    *,
    rtol: float = 1e-5,
    atol: float = 1e-8,
    check_preconditions: bool = True
) -> Tuple[np.ndarray, np.array, np.ndarray]:
    """Finds orthogonal matrices L, R such that L @ matrix @ R is diagonal.

    Args:
        mat: A unitary matrix.
        rtol: Relative numeric error threshold.
        atol: Absolute numeric error threshold.
        check_preconditions: If set, verifies that the input is a unitary matrix
            (to the given tolerances). Defaults to set.

    Returns:
        A triplet (L, d, R) such that L @ mat @ R = diag(d). Both L and R will
        be orthogonal matrices with determinant equal to 1.

    Raises:
        ValueError: Matrices don't meet preconditions (e.g. not real).
    """

    if check_preconditions:
        if not predicates.is_unitary(mat, rtol=rtol, atol=atol):
            raise ValueError('matrix must be unitary.')

    # Note: Because mat is unitary, setting A = real(mat) and B = imag(mat)
    # guarantees that both A @ B.T and A.T @ B are Hermitian.
    left, right = bidiagonalize_real_matrix_pair_with_symmetric_products(
        np.real(mat),
        np.imag(mat),
        rtol=rtol,
        atol=atol,
        check_preconditions=check_preconditions)

    # Convert to special orthogonal w/o breaking diagonalization.
    if np.linalg.det(left) < 0:
        left[0, :] *= -1
    if np.linalg.det(right) < 0:
        right[:, 0] *= -1

    diag = combinators.dot(left, mat, right)

    return left, np.diag(diag), right
Ejemplo n.º 14
0
    def _unitary_(self):
        """Returns the decomposition's two-qubit unitary matrix.

        U = g · (a1 ⊗ a0) · exp(i·(x·XX + y·YY + z·ZZ)) · (b1 ⊗ b0)
        """
        before = np.kron(*self.single_qubit_operations_before)
        after = np.kron(*self.single_qubit_operations_after)

        def interaction_matrix(m: np.ndarray, c: float) -> np.ndarray:
            return map_eigenvalues(np.kron(m, m), lambda v: np.exp(1j * v * c))

        x, y, z = self.interaction_coefficients
        x_mat = np.array([[0, 1], [1, 0]])
        y_mat = np.array([[0, -1j], [1j, 0]])
        z_mat = np.array([[1, 0], [0, -1]])

        return self.global_phase * combinators.dot(
            after, interaction_matrix(z_mat, z), interaction_matrix(y_mat, y),
            interaction_matrix(x_mat, x), before)
Ejemplo n.º 15
0
def bidiagonalize_unitary_with_special_orthogonals(
        mat: np.ndarray,
        tolerance: Tolerance = Tolerance.DEFAULT
) -> Tuple[np.ndarray, np.array, np.ndarray]:
    """Finds orthogonal matrices L, R such that L @ matrix @ R is diagonal.

    Args:
        mat: A unitary matrix.
        tolerance: Numeric error thresholds.

    Returns:
        A triplet (L, d, R) such that L @ mat @ R = diag(d). Both L and R will
        be orthogonal matrices with determinant equal to 1.

    Raises:
        ValueError: Matrices don't meet preconditions (e.g. not real).
        ArithmeticError: Failed to meet specified tolerance.
    """

    if not predicates.is_unitary(mat, tolerance):
        raise ValueError('matrix must be unitary.')

    # Note: Because mat is unitary, setting A = real(mat) and B = imag(mat)
    # guarantees that both A @ B.T and A.T @ B are Hermitian.
    left, right = bidiagonalize_real_matrix_pair_with_symmetric_products(
        np.real(mat),
        np.imag(mat),
        tolerance)

    # Convert to special orthogonal w/o breaking diagonalization.
    if np.linalg.det(left) < 0:
        left[0, :] *= -1
    if np.linalg.det(right) < 0:
        right[:, 0] *= -1

    diag = combinators.dot(left, mat, right)

    # Check acceptability vs tolerances.
    if not predicates.is_diagonal(diag, tolerance):
        raise ArithmeticError('Failed to diagonalize to specified tolerance.')

    return left, np.diag(diag), right
Ejemplo n.º 16
0
def bidiagonalize_unitary_with_special_orthogonals(
    mat: np.ndarray,
    tolerance: Tolerance = Tolerance.DEFAULT
) -> Tuple[np.ndarray, np.array, np.ndarray]:
    """Finds orthogonal matrices L, R such that L @ matrix @ R is diagonal.

    Args:
        mat: A unitary matrix.
        tolerance: Numeric error thresholds.

    Returns:
        A triplet (L, d, R) such that L @ mat @ R = diag(d). Both L and R will
        be orthogonal matrices with determinant equal to 1.

    Raises:
        ValueError: Matrices don't meet preconditions (e.g. not real).
        ArithmeticError: Failed to meet specified tolerance.
    """

    if not predicates.is_unitary(mat, tolerance):
        raise ValueError('matrix must be unitary.')

    # Note: Because mat is unitary, setting A = real(mat) and B = imag(mat)
    # guarantees that both A @ B.T and A.T @ B are Hermitian.
    left, right = bidiagonalize_real_matrix_pair_with_symmetric_products(
        np.real(mat), np.imag(mat), tolerance)

    # Convert to special orthogonal w/o breaking diagonalization.
    if np.linalg.det(left) < 0:
        left[0, :] *= -1
    if np.linalg.det(right) < 0:
        right[:, 0] *= -1

    diag = combinators.dot(left, mat, right)

    # Check acceptability vs tolerances.
    if not predicates.is_diagonal(diag, tolerance):
        raise ArithmeticError('Failed to diagonalize to specified tolerance.')

    return left, np.diag(diag), right
Ejemplo n.º 17
0
def bidiagonalize_real_matrix_pair_with_symmetric_products(
    mat1: np.ndarray,
    mat2: np.ndarray,
    *,
    rtol: float = 1e-5,
    atol: float = 1e-8,
    check_preconditions: bool = True,
) -> Tuple[np.ndarray, np.ndarray]:
    """Finds orthogonal matrices that diagonalize both mat1 and mat2.

    Requires mat1 and mat2 to be real.
    Requires mat1.T @ mat2 to be symmetric.
    Requires mat1 @ mat2.T to be symmetric.

    Args:
        mat1: One of the real matrices.
        mat2: The other real matrix.
        rtol: Relative numeric error threshold.
        atol: Absolute numeric error threshold.
        check_preconditions: If set, verifies that the inputs are real, and that
            mat1.T @ mat2 and mat1 @ mat2.T are both symmetric. Defaults to set.

    Returns:
        A tuple (L, R) of two orthogonal matrices, such that both L @ mat1 @ R
        and L @ mat2 @ R are diagonal matrices.

    Raises:
        ValueError: Matrices don't meet preconditions (e.g. not real).
    """

    if check_preconditions:
        if np.any(np.imag(mat1) != 0):
            raise ValueError('mat1 must be real.')
        if np.any(np.imag(mat2) != 0):
            raise ValueError('mat2 must be real.')
        if not predicates.is_hermitian(
                np.dot(mat1, mat2.T), rtol=rtol, atol=atol):
            raise ValueError('mat1 @ mat2.T must be symmetric.')
        if not predicates.is_hermitian(
                np.dot(mat1.T, mat2), rtol=rtol, atol=atol):
            raise ValueError('mat1.T @ mat2 must be symmetric.')

    # Use SVD to bi-diagonalize the first matrix.
    base_left, base_diag, base_right = _svd_handling_empty(np.real(mat1))
    base_diag = np.diag(base_diag)

    # Determine where we switch between diagonalization-fixup strategies.
    dim = base_diag.shape[0]
    rank = dim
    while rank > 0 and tolerance.all_near_zero(base_diag[rank - 1, rank - 1],
                                               atol=atol):
        rank -= 1
    base_diag = base_diag[:rank, :rank]

    # Try diagonalizing the second matrix with the same factors as the first.
    semi_corrected = combinators.dot(base_left.T, np.real(mat2), base_right.T)

    # Fix up the part of the second matrix's diagonalization that's matched
    # against non-zero diagonal entries in the first matrix's diagonalization
    # by performing simultaneous diagonalization.
    overlap = semi_corrected[:rank, :rank]
    overlap_adjust = diagonalize_real_symmetric_and_sorted_diagonal_matrices(
        overlap,
        base_diag,
        rtol=rtol,
        atol=atol,
        check_preconditions=check_preconditions)

    # Fix up the part of the second matrix's diagonalization that's matched
    # against zeros in the first matrix's diagonalization by performing an SVD.
    extra = semi_corrected[rank:, rank:]
    extra_left_adjust, _, extra_right_adjust = _svd_handling_empty(extra)

    # Merge the fixup factors into the initial diagonalization.
    left_adjust = combinators.block_diag(overlap_adjust, extra_left_adjust)
    right_adjust = combinators.block_diag(overlap_adjust.T, extra_right_adjust)
    left = np.dot(left_adjust.T, base_left.T)
    right = np.dot(base_right.T, right_adjust.T)

    return left, right
Ejemplo n.º 18
0
 def shift(k, step):
     v[k] += step * np.pi / 2
     phase[0] *= 1j**step
     right[0] = combinators.dot(flippers[k]**(step % 4), right[0])
     right[1] = combinators.dot(flippers[k]**(step % 4), right[1])
Ejemplo n.º 19
0
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))
    )
Ejemplo n.º 20
0
 def shift(k, step):
     v[k] += step * np.pi / 2
     phase[0] *= 1j**step
     right[0] = combinators.dot(flippers[k]**(step % 4), right[0])
     right[1] = combinators.dot(flippers[k]**(step % 4), right[1])