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]]))
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))
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
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
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)