def test_schmidt_decomp_dim_list_pure_state(): """Schmidt decomposition of a pure state with a dimension list.""" pure_vec = -1 / np.sqrt(2) * np.array([[1], [0], [1], [0]]) # Test when dimension default and k_param is default (0): singular_vals, vt_mat, u_mat = schmidt_decomposition(pure_vec) expected_singular_vals = np.array([[1]]) bool_mat = np.isclose(expected_singular_vals, singular_vals) np.testing.assert_equal(np.all(bool_mat), True) expected_vt_mat = 1 / np.sqrt(2) * np.array([[-1], [-1]]) bool_mat = np.isclose(expected_vt_mat, vt_mat) np.testing.assert_equal(np.all(bool_mat), True) expected_u_mat = np.array([[1], [0]]) bool_mat = np.isclose(expected_u_mat, u_mat) np.testing.assert_equal(np.all(bool_mat), True) # Test when dimension [2, 2] and k_param is 1: singular_vals, vt_mat, u_mat = schmidt_decomposition(pure_vec, [2, 2], 1) expected_singular_vals = np.array([[1]]) bool_mat = np.isclose(expected_singular_vals, singular_vals) np.testing.assert_equal(np.all(bool_mat), True) expected_vt_mat = 1 / np.sqrt(2) * np.array([[-1], [-1]]) bool_mat = np.isclose(expected_vt_mat, vt_mat) np.testing.assert_equal(np.all(bool_mat), True) expected_u_mat = np.array([[1], [0]]) bool_mat = np.isclose(expected_u_mat, u_mat) np.testing.assert_equal(np.all(bool_mat), True) # Test when dimension [2, 2] and k_param is 2: singular_vals, vt_mat, u_mat = schmidt_decomposition(pure_vec, [2, 2], 2) expected_singular_vals = np.array([[1], [0]]) bool_mat = np.isclose(expected_singular_vals, singular_vals) np.testing.assert_equal(np.all(bool_mat), True) expected_vt_mat = 1 / np.sqrt(2) * np.array([[-1, -1], [-1, 1]]) bool_mat = np.isclose(expected_vt_mat, vt_mat) np.testing.assert_equal(np.all(bool_mat), True) expected_u_mat = np.identity(2) bool_mat = np.isclose(expected_u_mat, u_mat) np.testing.assert_equal(np.all(bool_mat), True)
def test_schmidt_decomp_two_qubit_3(): """ Schmidt decomposition of two-qubit state. The Schmidt decomposition of 1/2* (|00> + |11>) has Schmidt coefficients equal to 1/2[1, 1] """ e_0, e_1 = basis(2, 0), basis(2, 1) phi = 1 / 2 * (np.kron(e_0, e_0) + np.kron(e_1, e_1)) singular_vals, vt_mat, u_mat = schmidt_decomposition(phi) expected_singular_vals = 1 / 2 * np.array([[1], [1]]) bool_mat = np.isclose(expected_singular_vals, singular_vals) np.testing.assert_equal(np.all(bool_mat), True) expected_vt_mat = np.array([[1, 0], [0, 1]]) bool_mat = np.isclose(expected_vt_mat, vt_mat) np.testing.assert_equal(np.all(bool_mat), True) expected_u_mat = np.array([[1, 0], [0, 1]]) bool_mat = np.isclose(expected_u_mat, u_mat) np.testing.assert_equal(np.all(bool_mat), True) s_decomp = ( singular_vals[0] * np.atleast_2d(np.kron(vt_mat[:, 0], u_mat[:, 0])).T + singular_vals[1] * np.atleast_2d(np.kron(vt_mat[:, 1], u_mat[:, 1])).T) np.testing.assert_equal(np.isclose(np.linalg.norm(phi - s_decomp), 0), True)
def test_schmidt_decomp_two_qubit_2(): """ Schmidt decomposition of two-qubit state. The Schmidt decomposition of | phi > = 1/2(|00> + |01> + |10> - |11>) is the state 1/sqrt(2) * (|0>|+> + |1>|->). """ e_0, e_1 = basis(2, 0), basis(2, 1) phi = 1 / 2 * (np.kron(e_0, e_0) + np.kron(e_0, e_1) + np.kron(e_1, e_0) - np.kron(e_1, e_1)) singular_vals, vt_mat, u_mat = schmidt_decomposition(phi) expected_singular_vals = 1 / np.sqrt(2) * np.array([[1], [1]]) bool_mat = np.isclose(expected_singular_vals, singular_vals) np.testing.assert_equal(np.all(bool_mat), True) expected_vt_mat = np.array([[-1, 0], [0, -1]]) bool_mat = np.isclose(expected_vt_mat, vt_mat) np.testing.assert_equal(np.all(bool_mat), True) expected_u_mat = 1 / np.sqrt(2) * np.array([[-1, -1], [-1, 1]]) bool_mat = np.isclose(expected_u_mat, u_mat) np.testing.assert_equal(np.all(bool_mat), True) s_decomp = ( singular_vals[0] * np.atleast_2d(np.kron(vt_mat[:, 0], u_mat[:, 0])).T + singular_vals[1] * np.atleast_2d(np.kron(vt_mat[:, 1], u_mat[:, 1])).T) np.testing.assert_equal(np.isclose(np.linalg.norm(phi - s_decomp), 0), True)
def _is_product(rho: np.ndarray, dim: Union[int, List[int]] = None) -> [int, bool]: """ Determine if input is a product state recursive helper. :param rho: The vector or matrix to check. :param dim: The dimension of the input. :return: :code:`True` if :code:`rho` is a product vector and :code:`False` otherwise. """ # If the input is provided as a matrix, compute the operator Schmidt rank. if len(rho.shape) == 2: if rho.shape[0] != 1 and rho.shape[1] != 1: return _operator_is_product(rho, dim) if dim is None: dim = np.round(np.sqrt(len(rho))) if isinstance(dim, list): dim = np.array(dim) # Allow the user to enter a single number for dim. if isinstance(dim, float): num_sys = 1 else: num_sys = len(dim) if num_sys == 1: dim = np.array([dim, len(rho) // dim]) dim[1] = np.round(dim[1]) num_sys = 2 dec = None # If there are only two subsystems, just use the Schmidt decomposition. if num_sys == 2: singular_vals, u_mat, vt_mat = schmidt_decomposition(rho, dim, 2) ipv = singular_vals[1] <= np.prod(dim) * np.spacing(singular_vals[0]) # Provide this even if not requested, since it is needed if this # function was called as part of its recursive algorithm (see below) if ipv: u_mat = u_mat * np.sqrt(singular_vals[0]) vt_mat = vt_mat * np.sqrt(singular_vals[0]) dec = [u_mat[:, 0], vt_mat[:, 0]] else: new_dim = [dim[0] * dim[1]] new_dim.extend(dim[2:]) ipv, dec = _is_product(rho, new_dim) if ipv: ipv, tdec = _is_product(dec[0], [dim[0], dim[1]]) if ipv: dec = [tdec, dec[1:]] return ipv, dec
def test_schmidt_decomp_max_ent(): """Schmidt decomposition of the 3-D maximally entangled state.""" singular_vals, u_mat, vt_mat = schmidt_decomposition(max_entangled(3)) expected_u_mat = np.identity(3) expected_vt_mat = np.identity(3) expected_singular_vals = 1 / np.sqrt(3) * np.array([[1], [1], [1]]) bool_mat = np.isclose(expected_u_mat, u_mat) np.testing.assert_equal(np.all(bool_mat), True) bool_mat = np.isclose(expected_vt_mat, vt_mat) np.testing.assert_equal(np.all(bool_mat), True) bool_mat = np.isclose(expected_singular_vals, singular_vals) np.testing.assert_equal(np.all(bool_mat), True)
def test_schmidt_decomp_standard_basis(): """Test on standard basis vectors.""" e_1 = basis(2, 1) singular_vals, vt_mat, u_mat = schmidt_decomposition(np.kron(e_1, e_1)) expected_singular_vals = np.array([[1]]) bool_mat = np.isclose(expected_singular_vals, singular_vals) np.testing.assert_equal(np.all(bool_mat), True) expected_vt_mat = np.array([[0], [1]]) bool_mat = np.isclose(expected_vt_mat, vt_mat) np.testing.assert_equal(np.all(bool_mat), True) expected_u_mat = np.array([[0], [1]]) bool_mat = np.isclose(expected_u_mat, u_mat) np.testing.assert_equal(np.all(bool_mat), True)
def test_schmidt_decomp_dim_list(): """Schmidt decomposition with list specifying dimension.""" singular_vals, u_mat, vt_mat = schmidt_decomposition(max_entangled(3), dim=[3, 3]) expected_u_mat = np.identity(3) expected_vt_mat = np.identity(3) expected_singular_vals = 1 / np.sqrt(3) * np.array([[1], [1], [1]]) bool_mat = np.isclose(expected_u_mat, u_mat) np.testing.assert_equal(np.all(bool_mat), True) bool_mat = np.isclose(expected_vt_mat, vt_mat) np.testing.assert_equal(np.all(bool_mat), True) bool_mat = np.isclose(expected_singular_vals, singular_vals) np.testing.assert_equal(np.all(bool_mat), True)
def test_schmidt_decomp_example(): """Test for example Schmidt decomposition.""" e_0, e_1 = basis(2, 0), basis(2, 1) phi = ((1 + np.sqrt(6)) / (2 * np.sqrt(6)) * np.kron(e_0, e_0) + (1 - np.sqrt(6)) / (2 * np.sqrt(6)) * np.kron(e_0, e_1) + (np.sqrt(2) - np.sqrt(3)) / (2 * np.sqrt(6)) * np.kron(e_1, e_0) + (np.sqrt(2) + np.sqrt(3)) / (2 * np.sqrt(6)) * np.kron(e_1, e_1)) singular_vals, vt_mat, u_mat = schmidt_decomposition(phi) expected_singular_vals = np.array([[np.sqrt(3 / 4)], [np.sqrt(1 / 4)]]) bool_mat = np.isclose(expected_singular_vals, singular_vals) np.testing.assert_equal(np.all(bool_mat), True) expected_vt_mat = np.array([[-0.81649658, 0.57735027], [0.57735027, 0.81649658]]) bool_mat = np.isclose(expected_vt_mat, vt_mat) np.testing.assert_equal(np.all(bool_mat), True) expected_u_mat = 1 / np.sqrt(2) * np.array([[-1, 1], [1, 1]]) bool_mat = np.isclose(expected_u_mat, u_mat) np.testing.assert_equal(np.all(bool_mat), True)
def test_schmidt_decomp_two_qubit_4(): """ Schmidt decomposition of two-qubit state. The Schmidt decomposition of 1/2 * (|00> - |01> + |10> + |11>) has Schmidt coefficients equal to [1, 1] """ e_0, e_1 = basis(2, 0), basis(2, 1) phi = 1 / 2 * (np.kron(e_0, e_0) - np.kron(e_0, e_1) + np.kron(e_1, e_0) + np.kron(e_1, e_1)) singular_vals, vt_mat, u_mat = schmidt_decomposition(phi) expected_singular_vals = 1 / np.sqrt(2) * np.array([[1], [1]]) bool_mat = np.isclose(expected_singular_vals, singular_vals) np.testing.assert_equal(np.all(bool_mat), True) expected_vt_mat = np.array([[-1, 0], [0, 1]]) bool_mat = np.isclose(expected_vt_mat, vt_mat) np.testing.assert_equal(np.all(bool_mat), True) expected_u_mat = 1 / np.sqrt(2) * np.array([[-1, 1], [1, 1]]) bool_mat = np.isclose(expected_u_mat, u_mat) np.testing.assert_equal(np.all(bool_mat), True)
def test_schmidt_decomp_two_qubit_1(): """ Schmidt decomposition of two-qubit state. The Schmidt decomposition of | phi > = 1/2(|00> + |01> + |10> + |11>) is the state |+>|+> where |+> = 1/sqrt(2) * (|0> + |1>). """ e_0, e_1 = basis(2, 0), basis(2, 1) phi = 1 / 2 * (np.kron(e_0, e_0) + np.kron(e_0, e_1) + np.kron(e_1, e_0) + np.kron(e_1, e_1)) singular_vals, vt_mat, u_mat = schmidt_decomposition(phi) expected_singular_vals = np.array([[1]]) bool_mat = np.isclose(expected_singular_vals, singular_vals) np.testing.assert_equal(np.all(bool_mat), True) expected_vt_mat = 1 / np.sqrt(2) * np.array([[-1], [-1]]) bool_mat = np.isclose(expected_vt_mat, vt_mat) np.testing.assert_equal(np.all(bool_mat), True) expected_u_mat = 1 / np.sqrt(2) * np.array([[-1], [-1]]) bool_mat = np.isclose(expected_u_mat, u_mat) np.testing.assert_equal(np.all(bool_mat), True)