Example #1
0
def test_kron_spreads_values():
    u = np.array([[2, 3], [5, 7]])

    assert np.allclose(
        combinators.kron(np.eye(2), u),
        np.array([[2, 3, 0, 0], [5, 7, 0, 0], [0, 0, 2, 3], [0, 0, 5, 7]]))

    assert np.allclose(
        combinators.kron(u, np.eye(2)),
        np.array([[2, 0, 3, 0], [0, 2, 0, 3], [5, 0, 7, 0], [0, 5, 0, 7]]))

    assert np.allclose(
        combinators.kron(u, u),
        np.array([[4, 6, 6, 9], [10, 14, 15, 21], [10, 15, 14, 21],
                [25, 35, 35, 49]]))
Example #2
0
def test_kron_spreads_values():
    u = np.array([[2, 3], [5, 7]])

    assert np.allclose(
        combinators.kron(np.eye(2), u),
        np.array([[2, 3, 0, 0], [5, 7, 0, 0], [0, 0, 2, 3], [0, 0, 5, 7]]))

    assert np.allclose(
        combinators.kron(u, np.eye(2)),
        np.array([[2, 0, 3, 0], [0, 2, 0, 3], [5, 0, 7, 0], [0, 5, 0, 7]]))

    assert np.allclose(
        combinators.kron(u, u),
        np.array([[4, 6, 6, 9], [10, 14, 15, 21], [10, 15, 14, 21],
                  [25, 35, 35, 49]]))
Example #3
0
def test_kron_multiplies_sizes():
    assert np.allclose(combinators.kron(), np.eye(1))
    assert np.allclose(combinators.kron(np.eye(1)), np.eye(1))
    assert np.allclose(combinators.kron(np.eye(2)), np.eye(2))
    assert np.allclose(combinators.kron(np.eye(1), np.eye(1)), np.eye(1))
    assert np.allclose(combinators.kron(np.eye(1), np.eye(2)), np.eye(2))
    assert np.allclose(combinators.kron(np.eye(2), np.eye(3)), np.eye(6))
    assert np.allclose(combinators.kron(np.eye(2), np.eye(3), np.eye(4)),
                       np.eye(24))
Example #4
0
def test_kron_multiplies_sizes():
    assert np.allclose(combinators.kron(), np.eye(1))
    assert np.allclose(combinators.kron(np.eye(1)), np.eye(1))
    assert np.allclose(combinators.kron(np.eye(2)), np.eye(2))
    assert np.allclose(combinators.kron(np.eye(1), np.eye(1)), np.eye(1))
    assert np.allclose(combinators.kron(np.eye(1), np.eye(2)), np.eye(2))
    assert np.allclose(combinators.kron(np.eye(2), np.eye(3)), np.eye(6))
    assert np.allclose(combinators.kron(np.eye(2), np.eye(3), np.eye(4)),
                       np.eye(24))
Example #5
0
def kron_factor_4x4_to_2x2s(
        matrix: np.ndarray,
        tolerance: Tolerance = Tolerance.DEFAULT
) -> Tuple[complex, np.ndarray, np.ndarray]:
    """Splits a 4x4 matrix U = kron(A, B) into A, B, and a global factor.

    Requires the matrix to be the kronecker product of two 2x2 unitaries.
    Requires the matrix to have a non-zero determinant.

    Args:
        matrix: The 4x4 unitary matrix to factor.
        tolerance: Acceptable numeric error thresholds.

    Returns:
        A scalar factor and a pair of 2x2 unit-determinant matrices. The
        kronecker product of all three is equal to the given matrix.

    Raises:
        ValueError:
            The given matrix can't be tensor-factored into 2x2 pieces.
    """

    # Use the entry with the largest magnitude as a reference point.
    a, b = max(
        ((i, j) for i in range(4) for j in range(4)),
        key=lambda t: abs(matrix[t]))

    # Extract sub-factors touching the reference cell.
    f1 = np.zeros((2, 2), dtype=np.complex128)
    f2 = np.zeros((2, 2), dtype=np.complex128)
    for i in range(2):
        for j in range(2):
            f1[(a >> 1) ^ i, (b >> 1) ^ j] = matrix[a ^ (i << 1), b ^ (j << 1)]
            f2[(a & 1) ^ i, (b & 1) ^ j] = matrix[a ^ i, b ^ j]

    # Rescale factors to have unit determinants.
    f1 /= (np.sqrt(np.linalg.det(f1)) or 1)
    f2 /= (np.sqrt(np.linalg.det(f2)) or 1)

    # Determine global phase.
    g = matrix[a, b] / (f1[a >> 1, b >> 1] * f2[a & 1, b & 1])
    if np.real(g) < 0:
        f1 *= -1
        g = -g

    restored = g * combinators.kron(f1, f2)
    if np.any(np.isnan(restored)) or not tolerance.all_close(restored, matrix):
        raise ValueError("Can't factor into kronecker product.")

    return g, f1, f2
Example #6
0
def kron_factor_4x4_to_2x2s(
        matrix: np.ndarray,
        tolerance: Tolerance = Tolerance.DEFAULT
) -> Tuple[complex, np.ndarray, np.ndarray]:
    """Splits a 4x4 matrix U = kron(A, B) into A, B, and a global factor.

    Requires the matrix to be the kronecker product of two 2x2 unitaries.
    Requires the matrix to have a non-zero determinant.

    Args:
        matrix: The 4x4 unitary matrix to factor.
        tolerance: Acceptable numeric error thresholds.

    Returns:
        A scalar factor and a pair of 2x2 unit-determinant matrices. The
        kronecker product of all three is equal to the given matrix.

    Raises:
        ValueError:
            The given matrix can't be tensor-factored into 2x2 pieces.
    """

    # Use the entry with the largest magnitude as a reference point.
    a, b = max(
        ((i, j) for i in range(4) for j in range(4)),
        key=lambda t: abs(matrix[t]))

    # Extract sub-factors touching the reference cell.
    f1 = np.zeros((2, 2), dtype=np.complex128)
    f2 = np.zeros((2, 2), dtype=np.complex128)
    for i in range(2):
        for j in range(2):
            f1[(a >> 1) ^ i, (b >> 1) ^ j] = matrix[a ^ (i << 1), b ^ (j << 1)]
            f2[(a & 1) ^ i, (b & 1) ^ j] = matrix[a ^ i, b ^ j]

    # Rescale factors to have unit determinants.
    f1 /= (np.sqrt(np.linalg.det(f1)) or 1)
    f2 /= (np.sqrt(np.linalg.det(f2)) or 1)

    # Determine global phase.
    g = matrix[a, b] / (f1[a >> 1, b >> 1] * f2[a & 1, b & 1])
    if np.real(g) < 0:
        f1 *= -1
        g = -g

    restored = g * combinators.kron(f1, f2)
    if np.any(np.isnan(restored)) or not tolerance.all_close(restored, matrix):
        raise ValueError("Can't factor into kronecker product.")

    return g, f1, f2
Example #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.
        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
Example #8
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
Example #9
0
        s, m):
    m = np.array(m)
    s = np.diag(s)
    with pytest.raises(ValueError):
        diagonalize.diagonalize_real_symmetric_and_sorted_diagonal_matrices(
            m, s)


@pytest.mark.parametrize(
    'a,b', [
        (np.zeros((0, 0)), np.zeros((0, 0))),
        (np.eye(2), np.eye(2)),
        (np.eye(4), np.eye(4)),
        (np.eye(4), np.zeros((4, 4))),
        (H, H),
        (combinators.kron(np.eye(2), H), combinators.kron(H, np.eye(2))),
        (combinators.kron(np.eye(2), Z), combinators.kron(X, np.eye(2))),
    ] + [random_bi_diagonalizable_pair(2) for _ in range(10)] +
    [random_bi_diagonalizable_pair(4) for _ in range(10)] + [
        random_bi_diagonalizable_pair(4, d1, d2) for _ in range(10)
        for d1 in range(4) for d2 in range(4)
    ] + [random_bi_diagonalizable_pair(k) for k in range(1, 10)])
def test_bidiagonalize_real_matrix_pair_with_symmetric_products(a, b):
    a = np.array(a)
    b = np.array(b)
    p, q = diagonalize.bidiagonalize_real_matrix_pair_with_symmetric_products(
        a, b)
    assert_bidiagonalized_by(a, p, q)
    assert_bidiagonalized_by(b, p, q)