Exemple #1
0
def group_commutator(
    U: qtypes.UnitaryMatrix
) -> typing.Tuple[qtypes.SU2Matrix, qtypes.SU2Matrix]:
    """Finds :math:`V, W \\in U(2) \\mid U = V W V^\\dagger W^\\dagger`.

    :param U: The unitary matrix in :math:`U(2)` to decompose.
    :return: a tuple containing (:math:`V`, :math:`W`).
    """
    # unitary is a rotation of a unknown angle $\theta$ about some unknown
    # axis. Here, we find the angle $\theta$.
    so3_unitary = su2trans.su2_to_so3(U)
    theta = numpy.linalg.norm(so3_unitary, 2)

    # Then, we construct the matrix that consist of a rotation of $\theta$
    # about the X-axis.
    X_unitary = su2trans.so3_to_su2(numpy.array([theta, 0.0, 0.0]))
    # We find the similarity matrix between the original unitary and the
    # rotation about the X-axis we just created.
    S = sim_matrix.similarity_matrix(U, X_unitary)

    # Now we perform the real computations to find V and W, but we perform
    # them on the unitary rotating about the X-axis and not on the
    # original unitary.
    A, B = _X_axis_su2_group_commutator_decompose(X_unitary)

    # Compute the real V and W from A and B.
    V, W = S @ A @ S.T.conj(), S @ B @ S.T.conj()

    return V, W
Exemple #2
0
def similarity_matrix(A: qtypes.SU2Matrix,
                      B: qtypes.SU2Matrix) -> qtypes.SU2Matrix:
    """Find :math:`S \\in SU(2) \\mid A = S B S^\\dagger`.

    :param A: First :math:`SU(2)` matrix.
    :param B: Second :math:`SU(2)` matrix.
    :return: the :math:`SU(2)` matrix :math:`S`.
    """
    a, b = su2trans.su2_to_so3(A), su2trans.su2_to_so3(B)
    norm_a, norm_b = numpy.linalg.norm(a, 2), numpy.linalg.norm(b, 2)

    s = numpy.cross(b, a)
    norm_s = numpy.linalg.norm(s, 2)

    if norm_s == 0:
        # The representative vectors are too close to each other, this
        # means that the original matrices are also very close, and so
        # returning the identity matrix is fine.
        return numpy.identity(2)

    angle_between_a_and_b = numpy.arccos(numpy.inner(a, b) / (norm_a * norm_b))
    s *= angle_between_a_and_b / norm_s

    S = su2trans.so3_to_su2(s)
    return S
Exemple #3
0
 def test_su2_to_so3_to_su2_random(self) -> None:
     """Tests X == so3_to_su2(su2_to_so3(X)) for random X."""
     # Abort if we don't want random tests
     if not other_consts.USE_RANDOM_TESTS:
         return
     for idx in range(other_consts.RANDOM_SAMPLES):
         local_su2 = gen_su2.generate_random_SU2_matrix()
         self.assert2NormClose(
             trans.so3_to_su2(trans.su2_to_so3(local_su2)), local_su2)
Exemple #4
0
 def test_so3_to_su2_to_so3_random(self) -> None:
     """Tests X == su2_to_so3(so3_to_su2(X)) for random X."""
     # Abort if we don't want random tests
     if not other_consts.USE_RANDOM_TESTS:
         return
     for idx in range(other_consts.RANDOM_SAMPLES):
         local_coefficients = numpy.random.rand(3)
         self.assert2NormClose(
             trans.su2_to_so3(trans.so3_to_su2(local_coefficients)),
             local_coefficients)
Exemple #5
0
 def test_so3_to_su2_pauli_z(self) -> None:
     """Tests so3_to_su2 for a vector representing the Pauli Z matrix."""
     matrix = trans.so3_to_su2(numpy.array([0.0, 0.0, 1.0]))
     self.assert2NormClose(matrix,
                           scipy.linalg.expm(-1.j * mconsts.P_Z / 2))
Exemple #6
0
 def test_so3_to_su2_all_zero(self) -> None:
     """Tests so3_to_su2 for a zero SO(3) vector."""
     matrix = trans.so3_to_su2(numpy.array([0.0, 0.0, 0.0]))
     self.assert2NormClose(matrix, matrix[0, 0] * numpy.identity(2))
Exemple #7
0
def _X_axis_su2_group_commutator_decompose(
        Ux: qtypes.SU2Matrix
) -> typing.Tuple[qtypes.SU2Matrix, qtypes.SU2Matrix]:
    """Finds :math:`A, B \\in U(d) \\mid Ux = A B A^\\dagger B^\\dagger`.

    This method is restricted to matrices Ux that are rotations around the
    X-axis.
    From the analysis performed in http://arXiv.org/abs/quant-ph/0505030v2
    section 4.1, A and B can be seen as rotations.

    :param Ux: The unitary matrix in :math:`U(d)` to decompose.
    :return: a tuple containing (:math:`A`, :math:`B`).
    """
    # In the following code, theta is the angle of the given Ux, phi is the
    # angle of A and B.

    # We transform the input matrix as a vector of 4 real numbers because
    # these numbers are directly related to the cosinus and the sinus of
    # phi.
    unitary_cart4_coefficients = su2trans.su2_to_H(Ux)
    # From these coefficients, we have directly the value of cos(phi/2).
    cos_theta_2 = unitary_cart4_coefficients[0]

    # We know from http://arXiv.org/abs/quant-ph/0505030v2, Equation (10)
    # that theta and phi are linked.
    # Equation (10) can be reformulated as
    #    4*X^2 - 4*X + sin^2(theta/2) = 0
    # where X = sin^4(phi/2).
    # Solving this equation (which is easy because polynomial of degree 2)
    # gives X = (1 +/- cos(theta/2)) / 2 which can be rewritten as
    # sin^4(phi/2) = (1 +/- cos(theta/2)) / 2
    # The '+' version gives wrong results, but I don't know why. This should
    # be investigated when possible.
    sin_phi_2 = numpy.sqrt(numpy.sqrt((1 - cos_theta_2) / 2))
    cos_phi_2 = numpy.sqrt(1 - sin_phi_2 * sin_phi_2)

    # Compute the spherical coordinates of the vector representing the 3D
    # rotation.
    phi = 2 * numpy.arcsin(sin_phi_2)  # theta in the spherical system
    alpha = numpy.arctan(sin_phi_2)  # phi in the spherical system

    # Create the vector in cartesian coordinates representing the rotation
    # in 3D.
    # [spherical coordinate system] = [variables in this function].
    #                             r = phi
    #                         theta = phi/2
    #                           phi = alpha
    # We construct a and w such that the corresponding SU(2) matrices A
    # and W satisfy unitary_x == A @ W.
    w = numpy.array([
        phi * sin_phi_2 * numpy.cos(alpha),
        phi * sin_phi_2 * numpy.sin(alpha),
        phi * cos_phi_2,
    ])
    a = w.copy()
    a[2] = -w[2]

    # Construct the matrices A and W from a and w.
    A, W = su2trans.so3_to_su2(a), su2trans.so3_to_su2(w)

    # Finds B such that W = B @ A.T.conj() @ B.T.conj()
    # With such a B, A @ W == unitary_x == A @ B @ A.T.conj() @ B.T.conj()
    # which is exactly what we are searching for!
    B = sim_matrix.similarity_matrix(W, A.T.conj())

    # Return the matrices we were searching for.
    return A, B