Ejemplo n.º 1
0
    def test_full_both(self):
        # contraction over both full
        T = np.random.random((3, 4, 5))
        U = np.random.random((3, 4, 5))
        TU = np.tensordot(T, U, axes=([0, 1, 2], [0, 1, 2]))

        T_tt = TT(np.reshape(T, (3, 4, 5, 1, 1, 1)))
        U_tt = TT(np.reshape(U, (3, 4, 5, 1, 1, 1)))
        T_tt.tensordot(U_tt, 3, overwrite=True)

        TU_back = np.squeeze(T_tt.full())
        error = np.linalg.norm(TU_back - TU)
        self.assertLess(error, self.tol)

        # operator
        T_tt = rand([2, 3, 4], [5, 6, 7], ranks=2)
        U_tt = rand([2, 3, 4], [5, 6, 7], ranks=3)
        T = T_tt.full()
        U = U_tt.full()

        TU = np.tensordot(T, U, axes=([0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]))
        T_tt.tensordot(U_tt, 3, overwrite=True)
        TU_back = T_tt.full()
        error = np.linalg.norm(TU_back - TU)
        self.assertLess(error, self.tol)
Ejemplo n.º 2
0
    def test_pinv(self):
        """test pinv"""

        # construct non-operator tensor train
        cores = [self.cores[i][:, :, 0:1, :] for i in range(self.order)]
        t = TT(cores)

        # compute pseudoinverse
        t_pinv = TT.pinv(t, self.order - 1)

        # matricize tensor trains
        t = t.full().reshape([np.prod(self.row_dims[:-1]), self.row_dims[-1]])
        t_pinv = t_pinv.full().reshape(
            [np.prod(self.row_dims[:-1]), self.row_dims[-1]]).transpose()

        # compute relative errors
        rel_err_1 = np.linalg.norm(t.dot(t_pinv).dot(t) -
                                   t) / np.linalg.norm(t)
        rel_err_2 = np.linalg.norm(t_pinv.dot(t).dot(t_pinv) -
                                   t_pinv) / np.linalg.norm(t_pinv)
        rel_err_3 = np.linalg.norm((t.dot(t_pinv)).transpose() -
                                   t.dot(t_pinv)) / np.linalg.norm(
                                       t.dot(t_pinv))
        rel_err_4 = np.linalg.norm((t_pinv.dot(t)).transpose() -
                                   t_pinv.dot(t)) / np.linalg.norm(
                                       t_pinv.dot(t))

        # check if relative errors are smaller than tolerance
        self.assertLess(rel_err_1, self.tol)
        self.assertLess(rel_err_2, self.tol)
        self.assertLess(rel_err_3, self.tol)
        self.assertLess(rel_err_4, self.tol)
Ejemplo n.º 3
0
    def test_orthonormalization(self):
        """test orthonormalization"""

        # construct non-operator tensor train
        cores = [self.cores[i][:, :, 0:1, :] for i in range(self.order)]
        t_col = TT(cores)

        # orthonormalize t
        t_right = t_col.ortho(threshold=1e-14)

        # test if cores are right-orthonormal
        err = 0
        for i in range(1, self.order):
            c = np.tensordot(t_right.cores[i], t_right.cores[i], axes=([1, 3], [1, 3])).squeeze()
            if np.linalg.norm(c - np.eye(t_right.ranks[i])) > self.tol:
                err += 1

        # convert t_col to full format and flatten
        t_full = t_col.full().flatten()

        # compute relative error
        rel_err = np.linalg.norm(t_right.full().flatten() - t_full) / np.linalg.norm(t_full)

        # check if t_right is right-orthonormal and equal to t_col
        self.assertEqual(err, 0)
        self.assertLess(rel_err, self.tol)

        # check if orthonormalization fails if maximum rank is not positive
        with self.assertRaises(ValueError):
            t_col.ortho(max_rank=0)

        # check if orthonormalization fails if threshold is negative
        with self.assertRaises(ValueError):
            t_col.ortho(threshold=-1)
Ejemplo n.º 4
0
    def test_right_orthonormalization(self):
        """test right-orthonormalization"""

        # construct non-operator tensor train
        cores = [self.cores[i][:, :, 0:1, :] for i in range(self.order)]
        t_col = TT(cores)

        # right-orthonormalize t
        t_right = t_col.ortho_right()

        # test if cores are right-orthonormal
        err = 0
        for i in range(1, self.order):
            c = np.tensordot(t_right.cores[i],
                             t_right.cores[i],
                             axes=([1, 3], [1, 3])).squeeze()
            if np.linalg.norm(c - np.eye(t_right.ranks[i])) > self.tol:
                err += 1

        # convert t_col to full format and flatten
        t_full = t_col.full().flatten()

        # compute relative error
        rel_err = np.linalg.norm(t_right.full().flatten() -
                                 t_full) / np.linalg.norm(t_full)

        # check if t_right is right-orthonormal and equal to t_col
        self.assertEqual(err, 0)
        self.assertLess(rel_err, self.tol)
Ejemplo n.º 5
0
def multisponge(dimension, level):
    """
    Construction of a multisponge.

    Generate a binary tensor representing a multisponge fractal (e.g., Sierpinski carpet,
    Menger sponge, etc.), see [1]_, by exploiting the tensor-train format and Kronecker
    products.

    Parameters
    ----------
    dimension : int
        dimension (>1) of the multisponge
    level : int
        level of the fractal construction to generate

    Returns
    -------
    np.ndarray
        tensor representing the multisponge fractal

    References
    ----------
    .. [1] P. Gelß, C. Schütte, "Tensor-generated fractals: Using tensor decompositions for
           creating self-similar patterns", arXiv:1812.00814, 2018
    """

    if dimension > 1:

        # construct generating tensor
        cores = [np.zeros([1, 3, 1, 2])]
        cores[0][0, :, 0, 0] = [1, 1, 1]
        cores[0][0, :, 0, 1] = [1, 0, 1]
        for _ in range(1, dimension - 1):
            cores.append(np.zeros([2, 3, 1, 2]))
            cores[-1][0, :, 0, 0] = [1, 0, 1]
            cores[-1][1, :, 0, 0] = [0, 1, 0]
            cores[-1][1, :, 0, 1] = [1, 0, 1]
        cores.append(np.zeros([2, 3, 1, 1]))
        cores[-1][0, :, 0, 0] = [1, 0, 1]
        cores[-1][1, :, 0, 0] = [0, 1, 0]
        generator = TT(cores)
        generator = generator.full().reshape(generator.row_dims)

        # construct fractal in the form of a binary tensor
        fractal = generator
        for i in range(2, level + 1):
            fractal = np.kron(fractal, generator)
        fractal = fractal.astype(int)

    else:

        raise ValueError('dimension must be larger than 1')

    return fractal
Ejemplo n.º 6
0
def approximate(x, psi_x, thresholds: list, max_ranks: list):
    """Approximate psi_x using HOSVD and HOCUR, respectively.

    Parameters
    ----------
    x: array
        snapshot matrix
    psi_x: array
        transformed data matrix
    thresholds: list of floats
        tresholds for HOSVD
    max_ranks: list of ints
        maximum ranks for HOCUR

    Returns
    -------
    ranks_hosvd: list of lists of ints
        ranks of the approximations computed with HOSVD
    errors_hosvd: list of floats
        relative errors of the approximations computed with HOSVD
    ranks_hocur: list of lists of ints
        ranks of the approximations computed with HOCUR
    errors_hocur: list of floats
        relative errors of the approximations computed with HOCUR
    """

    # define returns
    ranks_hosvd = []
    errors_hosvd = []
    ranks_hocur = []
    errors_hocur = []

    start_time = utl.progress('Approximation in TT format', 0)

    # reshape psi_x into tensor
    psi_x_full = psi_x.reshape([number_of_boxes, number_of_boxes, 1, 1, 1, psi_x.shape[1]])

    # approximate psi_x using HOSVD
    for i in range(len(thresholds)):
        psi_approx = TT(psi_x_full, threshold=thresholds[i])
        ranks_hosvd.append(psi_approx.ranks)
        errors_hosvd.append(np.linalg.norm(psi_x_full - psi_approx.full()) / np.linalg.norm(psi_x_full))
        utl.progress('Approximation in TT format', 100 * (i + 1) / 6, cpu_time=_time.time() - start_time)

    # approximate psi_x using HOCUR
    for i in range(len(max_ranks)):
        psi_approx = tdt.hocur(x, basis_list, max_ranks[i], repeats=3, multiplier=100, progress=False)
        ranks_hocur.append(psi_approx.ranks)
        errors_hocur.append(np.linalg.norm(psi_x - psi_approx.transpose(cores=2).matricize()) / np.linalg.norm(psi_x))
        utl.progress('Approximation in TT format', 100 * (i + 4) / 6, cpu_time=_time.time() - start_time)

    return ranks_hosvd, errors_hosvd, ranks_hocur, errors_hocur
Ejemplo n.º 7
0
    def approximated_dynamics(_, theta):
        """Construction of the right-hand side of the system from the coefficient tensor"""

        cores = [np.zeros([1, theta.shape[0] + 1, 1, 1])] + [np.zeros([1, theta.shape[0] + 1, 1, 1]) for _ in
                                                             range(1, p)]
        for q in range(p):
            cores[q][0, :, 0, 0] = [1] + [psi[q](theta[r]) for r in range(theta.shape[0])]
        psi_x = TT(cores)
        psi_x = psi_x.full().reshape(np.prod(psi_x.row_dims), 1)

        rhs = psi_x.transpose() @ xi
        rhs = rhs.reshape(rhs.size)
        return rhs
Ejemplo n.º 8
0
def vicsek_fractal(dimension, level):
    """Construction of a Vicsek fractal

    Generate a binary tensor representing a Vicsek fractal, see [1]_, by exploiting the 
    tensor-train format and Kronecker products.

    Parameters
    ----------
    dimension: int (>1)
        dimension of the Vicsek fractal
    level: int 
        level of the fractal construction to generate

    Returns
    -------
    fractal: ndarray
        tensor representing the Vicsek fractal

    References
    ----------
    .. [1] P. Gelß, C. Schütte, "Tensor-generated fractals: Using tensor decompositions for 
           creating self-similar patterns", arXiv:1812.00814, 2018
    """

    if dimension > 1:
        # construct generating tensor
        cores = [None] * dimension
        cores[0] = np.zeros([1, 3, 1, 2])
        cores[0][0, :, 0, 0] = [1, 1, 1]
        cores[0][0, :, 0, 1] = [0, 1, 0]
        cores[dimension - 1] = np.zeros([2, 3, 1, 1])
        cores[dimension - 1][0, :, 0, 0] = [0, 1, 0]
        cores[dimension - 1][1, :, 0, 0] = [1, 0, 1]
        for i in range(1, dimension - 1):
            cores[i] = np.zeros([2, 3, 1, 2])
            cores[i][0, :, 0, 0] = [0, 1, 0]
            cores[i][1, :, 0, 0] = [1, 0, 1]
            cores[i][1, :, 0, 1] = [0, 1, 0]
        generator = TT(cores)
        generator = generator.full().reshape(generator.row_dims)

        # construct fractal in the form of a binary tensor
        fractal = generator
        for i in range(2, level + 1):
            fractal = np.kron(fractal, generator)
        fractal = fractal.astype(int)
    else:
        fractal = None

    return fractal
Ejemplo n.º 9
0
    def test_left_orthonormalization(self):
        """test left-orthonormalization"""

        # construct non-operator tensor train
        cores = [self.cores[i][:, :, 0:1, :] for i in range(self.order)]
        t_col = TT(cores)

        # left-orthonormalize t
        t_left = t_col.ortho_left(threshold=1e-14)

        # test if cores are left-orthonormal
        err = 0
        for i in range(self.order - 1):
            c = np.tensordot(t_left.cores[i],
                             t_left.cores[i],
                             axes=([0, 1], [0, 1])).squeeze()
            if np.linalg.norm(c - np.eye(t_left.ranks[i + 1])) > self.tol:
                err += 1

        # convert t_col to full format and flatten
        t_full = t_col.full().flatten()

        # compute relative error
        rel_err = np.linalg.norm(t_left.full().flatten() -
                                 t_full) / np.linalg.norm(t_full)

        # check if t_left is left-orthonormal and equal to t_col
        self.assertEqual(err, 0)
        self.assertLess(rel_err, self.tol)

        # check if orthonormalization fails if maximum rank is not positive
        with self.assertRaises(ValueError):
            t_col.ortho_left(max_rank=0)

        # check if orthonormalization fails if threshold is negative
        with self.assertRaises(ValueError):
            t_col.ortho_left(threshold=-1)

        # check if orthonormalization fails if start and end indices are not integers
        with self.assertRaises(TypeError):
            t_col.ortho_left(start_index="a")
            t_col.ortho_left(end_index="b")
Ejemplo n.º 10
0
    def test_1_norm(self):
        """test 1-norm"""

        # construct non-operator tensor train
        cores = [
            np.abs(self.cores[i][:, :, 0:1, :]) for i in range(self.order)
        ]
        t_col = TT(cores)

        # convert to full array and flatten
        t_full = t_col.full().flatten()

        # compute norms
        norm_tt = t_col.norm(p=1)
        norm_full = np.linalg.norm(t_full, 1)

        # compute relative error
        rel_err = (norm_tt - norm_full) / norm_full

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)
Ejemplo n.º 11
0
def cantor_dust(dimension, level):
    """
    Construction of a (multidimensional) Cantor dust.

    Generate a binary tensor representing a Cantor dust, see [1]_, by exploiting the
    tensor-train format and Kronecker products.

    Parameters
    ----------
    dimension : int
        dimension of the Cantor dust
    level : int
        level of the fractal construction to generate

    Returns
    -------
    np.ndarray
        tensor representing the Cantor dust

    References
    ----------
    .. [1] P. Gelß, C. Schütte, "Tensor-generated fractals: Using tensor decompositions for
           creating self-similar patterns", arXiv:1812.00814, 2018
    """

    # construct generating tensor
    cores = []
    for _ in range(dimension):
        cores.append(np.zeros([1, 3, 1, 1]))
        cores[-1][0, :, 0, 0] = [1, 0, 1]
    generator = TT(cores)
    generator = generator.full().reshape(generator.row_dims)

    # construct fractal in the form of a binary tensor
    fractal = generator
    for i in range(2, level + 1):
        fractal = np.kron(fractal, generator)
    fractal = fractal.astype(int)

    return fractal
Ejemplo n.º 12
0
# --------------------------

utl.progress('Compute exact eigenfunctions in matrix format', 0)
eigenvalues_pf_exact, eigenfunctions_pf_exact = splin.eigs(
    operator.matricize(), k=number_ev)
utl.progress('Compute exact eigenfunctions in matrix format', 100)

# convert results to matrices
# ---------------------------

eigenfunctions_mat = []
eigenfunctions_mat_exact = []

for i in range(number_ev):
    eigenfunctions_mat.append(
        np.rot90(TT.full(eigenfunctions_pf[i])[:, :, 0, 0]))
    eigenfunctions_mat_exact.append(
        np.rot90(
            np.real(eigenfunctions_pf_exact[:, i]).reshape(n_states,
                                                           n_states)))
    if i > 0:
        eigenfunctions_mat[i] = eigenfunctions_mat[i] / np.amax(
            abs(eigenfunctions_mat[i]))
        eigenfunctions_mat_exact[i] = eigenfunctions_mat_exact[i] / np.amax(
            abs(eigenfunctions_mat_exact[i]))
for i in range(number_ev):
    eigenfunctions_mat.append(
        np.rot90(TT.full(eigenfunctions_km[i])[:, :, 0, 0]))
    eigenfunctions_mat[i + 3] = eigenfunctions_mat[i + 3] / np.amax(
        abs(eigenfunctions_mat[i + 3]))
Ejemplo n.º 13
0
# --------------------------

utl.progress('Compute exact eigenfunctions in matrix format', 0)
eigenvalues_exact, eigenfunctions_exact = splin.eigs(operator.matricize(), k=number_ev)
idx = eigenvalues_exact.argsort()[::-1]
eigenvalues_exact = eigenvalues_exact[idx]
eigenfunctions_exact = eigenfunctions_exact[:, idx]
utl.progress('Compute exact eigenfunctions in matrix format', 100)

# convert results to matrices
# ----------------------------------

eigentensors_tt = []
eigentensors_ex = []
for i in range(number_ev):
    eigentensors_tt.append(TT.full(eigenfunctions[i])[:, :, :, 0, 0, 0])
    eigentensors_ex.append(eigenfunctions_exact[:, i].reshape(n_states, n_states, n_states))
    if i == 0:
        eigentensors_tt[i] = eigentensors_tt[i] / np.sum(eigentensors_tt[i])
        eigentensors_ex[i] = eigentensors_ex[i] / np.sum(eigentensors_ex[i])
    else:
        eigentensors_tt[i] = eigentensors_tt[i] / np.amax(abs(eigentensors_tt[i]))
        eigentensors_ex[i] = eigentensors_ex[i] / np.amax(abs(eigentensors_ex[i]))

# print table
# -----------

print('\n---------------------------------------------')
print('k    lambda_k    err(lambda_k)    err(phi_k))')
print('---------------------------------------------')
for i in range(number_ev):
Ejemplo n.º 14
0
    def test_1_axis(self):
        # test contraction over 1 axis
        # last-first contraction
        T = np.random.random((2, 3, 4, 5))
        U = np.random.random((5, 6, 7))
        TU = np.tensordot(T, U, axes=([3], [0]))

        T_tt = TT(np.reshape(T, (2, 3, 4, 5, 1, 1, 1, 1)))
        U_tt = TT(np.reshape(U, (5, 6, 7, 1, 1, 1)))
        TU_back = T_tt.tensordot(U_tt, 1)

        TU_back = np.squeeze(TU_back.full())
        error = np.linalg.norm(TU_back - TU)
        self.assertLess(error, self.tol)

        # last-last contraction
        T = np.random.random((2, 3, 4, 5))
        U = np.random.random((6, 7, 5))
        TU = np.tensordot(T, U, axes=([3], [2]))
        TU = np.transpose(TU, [0, 1, 2, 4, 3])

        T_tt = TT(np.reshape(T, (2, 3, 4, 5, 1, 1, 1, 1)))
        U_tt = TT(np.reshape(U, (6, 7, 5, 1, 1, 1)))
        T_tt.tensordot(U_tt, 1, mode='last-last', overwrite=True)

        TU_back = np.squeeze(T_tt.full())
        error = np.linalg.norm(TU_back - TU)
        self.assertLess(error, self.tol)

        # first-last contraction
        T = np.random.random((2, 3, 4, 5))
        U = np.random.random((6, 7, 2))
        TU = np.tensordot(T, U, axes=([0], [2]))
        TU = np.transpose(TU, [3, 4, 0, 1, 2])

        T_tt = TT(np.reshape(T, (2, 3, 4, 5, 1, 1, 1, 1)))
        U_tt = TT(np.reshape(U, (6, 7, 2, 1, 1, 1)))
        T_tt.tensordot(U_tt, 1, mode='first-last', overwrite=True)

        TU_back = np.squeeze(T_tt.full())
        error = np.linalg.norm(TU_back - TU)
        self.assertLess(error, self.tol)

        # first-first contraction
        T = np.random.random((2, 3, 4, 5))
        U = np.random.random((2, 6, 7))
        TU = np.tensordot(T, U, axes=([0], [0]))
        TU = np.transpose(TU, [4, 3, 0, 1, 2])

        T_tt = TT(np.reshape(T, (2, 3, 4, 5, 1, 1, 1, 1)))
        U_tt = TT(np.reshape(U, (2, 6, 7, 1, 1, 1)))
        T_tt.tensordot(U_tt, 1, mode='first-first', overwrite=True)

        TU_back = np.squeeze(T_tt.full())
        error = np.linalg.norm(TU_back - TU)
        self.assertLess(error, self.tol)

        # last-first contraction for operator
        T_tt = rand([2, 3, 4], [5, 6, 7], ranks=3)
        U_tt = rand([4, 8, 9], [7, 8, 9], ranks=2)
        T = T_tt.full()
        U = U_tt.full()

        TU = np.tensordot(T, U, axes=([2, 5], [0, 3]))
        TU = np.transpose(TU, [0, 1, 4, 5, 2, 3, 6, 7])
        T_tt.tensordot(U_tt, 1, overwrite=True)
        TU_back = T_tt.full()
        error = np.linalg.norm(TU_back - TU)
        self.assertLess(error, self.tol)
Ejemplo n.º 15
0
class TestTT(TestCase):
    def setUp(self):
        """Generate random parameters for a tensor train"""

        # set tolerance for relative errors
        self.tol = 10e-10

        # generate random order in [3,5]
        self.order = np.random.randint(3, 6)

        # generate random ranks in [3,5]
        self.ranks = [1] + list(
            np.random.randint(3, high=6, size=self.order - 1)) + [1]

        # generate random row  and column dimensions in [3,5]
        self.row_dims = list(np.random.randint(3, high=6, size=self.order))
        self.col_dims = list(np.random.randint(3, high=6, size=self.order))

        # define cores
        self.cores = [
            2 * np.random.rand(self.ranks[i], self.row_dims[i],
                               self.col_dims[i], self.ranks[i + 1]) - 1
            for i in range(self.order)
        ]

        # construct tensor train
        self.t = TT(self.cores)

    def test_construction_from_cores(self):
        """test tensor train class for list of cores"""

        # check if all parameters are correct
        self.assertEqual(self.t.order, self.order)
        self.assertEqual(self.t.ranks, self.ranks)
        self.assertEqual(self.t.row_dims, self.row_dims)
        self.assertEqual(self.t.col_dims, self.col_dims)
        self.assertEqual(self.t.cores, self.cores)

    def test_conversion(self):
        """test conversion to full format and element extraction"""

        # convert to full format
        t_full = self.t.full()

        # number of wrong entries
        err = 0

        # loop through all elements of the tensor
        for i in range(np.int(np.prod(self.row_dims + self.col_dims))):

            # convert flat index
            j = np.unravel_index(i, self.row_dims + self.col_dims)

            # extract elements of both representations
            v = self.t.element(list(j))
            w = t_full[j]

            # count wrong entries
            if (v - w) / v > self.tol:
                err += 1

        # check if no wrong entry exists
        self.assertEqual(err, 0)

    def test_matricize(self):
        """test matricization of tensor trains"""

        # matricize t
        t_mat = self.t.matricize()

        # convert t to full array and reshape
        t_full = self.t.full().reshape(
            [np.prod(self.row_dims),
             np.prod(self.col_dims)])

        # compute relative error
        rel_err = np.linalg.norm(t_mat - t_full) / np.linalg.norm(t_full)

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)

    def test_addition(self):
        """test addition/subtraction of tensor trains"""

        # compute difference of t and itself
        t_diff = (self.t - self.t)

        # convert to full array and reshape to vector
        t_diff = t_diff.full().flatten()

        # convert t to full array and reshape to vector
        t_tmp = self.t.full().flatten()

        # compute relative error
        rel_err = np.linalg.norm(t_diff) / np.linalg.norm(t_tmp)

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)

    def test_scalar_multiplication(self):
        """test scalar multiplication"""

        # random constant in [0,10]
        c = 10 * np.random.rand(1)[0]

        # multiply tensor train with scalar value, convert to full array, and reshape to vector
        t_tmp = TT.__mul__(self.t, c)
        t_tmp = t_tmp.full().flatten()

        # convert t to full array and reshape to vector
        t_full = self.t.full().flatten()

        # compute error
        err = np.linalg.norm(t_tmp) / np.linalg.norm(t_full) - c

        # check if error is smaller than tolerance
        self.assertLess(err, self.tol)

    def test_transpose(self):
        """test transpose of tensor trains"""

        # transpose in TT format, convert to full format, and reshape to vector
        t_trans = self.t.transpose().full().flatten()

        # convert to full format, transpose, and rehape to vector
        t_full = self.t.full().transpose(
            list(np.arange(self.order) + self.order) +
            list(np.arange(self.order))).flatten()

        # compute relative error
        rel_err = np.linalg.norm(t_trans - t_full) / np.linalg.norm(t_full)

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)

    def test_multiplication(self):
        """test multiplication of tensor trains"""

        # multiply t with its tranpose
        t_tmp = self.t.transpose() @ self.t

        # convert to full format and reshape to vector
        t_tmp = t_tmp.full().flatten()

        # convert t to full format and matricize
        t_full = self.t.full().reshape(
            [np.prod(self.row_dims),
             np.prod(self.col_dims)])

        # multiply with its transpose and flatten
        t_full = (t_full.transpose() @ t_full).flatten()

        # compute relative error
        rel_err = np.linalg.norm(t_tmp - t_full) / np.linalg.norm(t_full)

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)

    def test_construction_from_array(self):
        """test tensor train class for arrays"""

        # convert t to full format and construct tensor train form array
        t_full = self.t.full()

        # construct tensor train
        t_tmp = TT(t_full)

        # compute difference, convert to full format, and flatten
        t_diff = (self.t - t_tmp).full().flatten()

        # compute relative error
        rel_err = np.linalg.norm(t_diff) / np.linalg.norm(t_full.flatten())

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)

    def test_operator(self):
        """test operator check"""

        # check t
        t_check = self.t.isoperator()

        # construct non-operator tensor trains
        cores = [self.cores[i][:, :, 0:1, :] for i in range(self.order)]
        u = TT(cores)
        cores = [self.cores[i][:, 0:1, :, :] for i in range(self.order)]
        v = TT(cores)

        # check u and v
        u_check = u.isoperator()
        v_check = v.isoperator()

        # check if operator checks are correct
        self.assertTrue(t_check)
        self.assertFalse(u_check)
        self.assertFalse(v_check)

    def test_left_orthonormalization(self):
        """test left-orthonormalization"""

        # construct non-operator tensor train
        cores = [self.cores[i][:, :, 0:1, :] for i in range(self.order)]
        t_col = TT(cores)

        # left-orthonormalize t
        t_left = t_col.ortho_left()

        # test if cores are left-orthonormal
        err = 0
        for i in range(self.order - 1):
            c = np.tensordot(t_left.cores[i],
                             t_left.cores[i],
                             axes=([0, 1], [0, 1])).squeeze()
            if np.linalg.norm(c - np.eye(t_left.ranks[i + 1])) > self.tol:
                err += 1

        # convert t_col to full format and flatten
        t_full = t_col.full().flatten()

        # compute relative error
        rel_err = np.linalg.norm(t_left.full().flatten() -
                                 t_full) / np.linalg.norm(t_full)

        # check if t_left is left-orthonormal and equal to t_col
        self.assertEqual(err, 0)
        self.assertLess(rel_err, self.tol)

    def test_right_orthonormalization(self):
        """test right-orthonormalization"""

        # construct non-operator tensor train
        cores = [self.cores[i][:, :, 0:1, :] for i in range(self.order)]
        t_col = TT(cores)

        # right-orthonormalize t
        t_right = t_col.ortho_right()

        # test if cores are right-orthonormal
        err = 0
        for i in range(1, self.order):
            c = np.tensordot(t_right.cores[i],
                             t_right.cores[i],
                             axes=([1, 3], [1, 3])).squeeze()
            if np.linalg.norm(c - np.eye(t_right.ranks[i])) > self.tol:
                err += 1

        # convert t_col to full format and flatten
        t_full = t_col.full().flatten()

        # compute relative error
        rel_err = np.linalg.norm(t_right.full().flatten() -
                                 t_full) / np.linalg.norm(t_full)

        # check if t_right is right-orthonormal and equal to t_col
        self.assertEqual(err, 0)
        self.assertLess(rel_err, self.tol)

    def test_1_norm(self):
        """test 1-norm"""

        # construct non-operator tensor train
        cores = [
            np.abs(self.cores[i][:, :, 0:1, :]) for i in range(self.order)
        ]
        t_col = TT(cores)

        # convert to full array and flatten
        t_full = t_col.full().flatten()

        # compute norms
        norm_tt = t_col.norm(p=1)
        norm_full = np.linalg.norm(t_full, 1)

        # compute relative error
        rel_err = (norm_tt - norm_full) / norm_full

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)

    def test_2_norm(self):
        """test 2-norm"""

        # convert to full array and flatten
        t_full = self.t.full().flatten()

        # compute norms
        norm_tt = self.t.norm()
        norm_full = np.linalg.norm(t_full)

        # compute relative error
        rel_err = (norm_tt - norm_full) / norm_full

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)

    def test_qtt2tt_tt2qtt(self):
        """test qtt2tt and tt2qtt"""

        # suppose t to be in QTT format and contract first two cores of t
        t_tt = self.t.qtt2tt([2, self.order - 2])

        # convert t_tt to full array, and flatten
        t_tt_full = t_tt.full().flatten()

        # split the cores of t_tt, convert to full array, and flatten
        t_qtt = t_tt.tt2qtt([self.row_dims[0:2], self.row_dims[2:]],
                            [self.col_dims[0:2], self.col_dims[2:]])

        # convert t_qtt to full array, and flatten
        t_qtt_full = t_qtt.full().flatten()

        # convert t and to full format and flatten
        t_full = self.t.full().flatten()

        # compute relative errors
        rel_err_tt = np.linalg.norm(t_tt_full -
                                    t_full) / np.linalg.norm(t_full)
        rel_err_qtt = np.linalg.norm(t_qtt_full -
                                     t_full) / np.linalg.norm(t_full)

        # check if relative errors are smaller than tolerance
        self.assertLess(rel_err_tt, self.tol)
        self.assertLess(rel_err_qtt, self.tol)

    def test_pinv(self):
        """test pinv"""

        # construct non-operator tensor train
        cores = [self.cores[i][:, :, 0:1, :] for i in range(self.order)]
        t = TT(cores)

        # compute pseudoinverse
        t_pinv = TT.pinv(t, self.order - 1)

        # matricize tensor trains
        t = t.full().reshape([np.prod(self.row_dims[:-1]), self.row_dims[-1]])
        t_pinv = t_pinv.full().reshape(
            [np.prod(self.row_dims[:-1]), self.row_dims[-1]]).transpose()

        # compute relative errors
        rel_err_1 = np.linalg.norm(t @ t_pinv @ t - t) / np.linalg.norm(t)
        rel_err_2 = np.linalg.norm(t_pinv @ t @ t_pinv -
                                   t_pinv) / np.linalg.norm(t_pinv)
        rel_err_3 = np.linalg.norm((t @ t_pinv).transpose() -
                                   t @ t_pinv) / np.linalg.norm(t @ t_pinv)
        rel_err_4 = np.linalg.norm((t_pinv @ t).transpose() -
                                   t_pinv @ t) / np.linalg.norm(t_pinv @ t)

        # check if relative errors are smaller than tolerance
        self.assertLess(rel_err_1, self.tol)
        self.assertLess(rel_err_2, self.tol)
        self.assertLess(rel_err_3, self.tol)
        self.assertLess(rel_err_4, self.tol)

    def test_zeros(self):
        """test tensor train of all zeros"""

        # construct tensor train of all zeros
        t_zeros = tt.zeros(self.t.row_dims, self.t.col_dims)

        # compute norm
        t_norm = np.linalg.norm(t_zeros.full().flatten())

        # check if norm is 0
        self.assertEqual(t_norm, 0)

    def test_ones(self):
        """test tensor train of all ones"""

        # construct tensor train of all ones, convert to full format, and flatten
        t_ones = tt.ones(self.row_dims, self.col_dims).full().flatten()

        # construct full array of all ones
        t_full = np.ones(
            np.int(np.prod(self.row_dims)) * np.int(np.prod(self.col_dims)))

        # compute relative error
        rel_err = np.linalg.norm(t_ones - t_full) / np.linalg.norm(t_full)

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)

    def test_identity(self):
        """test identity tensor train"""

        # construct identity tensor train, convert to full format, and flatten
        t_eye = tt.eye(self.row_dims).full().flatten()

        # construct identity matrix and flatten
        t_full = np.eye(np.int(np.prod(self.row_dims))).flatten()

        # compute relative error
        rel_err = np.linalg.norm(t_eye - t_full) / np.linalg.norm(t_full)

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)

    def test_uniform(self):
        """test uniform tensor train"""

        # construct uniform tensor train
        norm = 10 * np.random.rand()
        t_uni = tt.uniform(self.row_dims, ranks=self.ranks, norm=norm)

        # compute norms
        norm_tt = t_uni.norm()

        # compute relative error
        rel_err = (norm_tt - norm) / norm

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)
Ejemplo n.º 16
0
    def test_full_other(self):
        # contraction over full U
        # last-first contraction
        T = np.random.random((2, 3, 4, 5))
        U = np.random.random((3, 4, 5))
        TU = np.tensordot(T, U, axes=([1, 2, 3], [0, 1, 2]))

        T_tt = TT(np.reshape(T, (2, 3, 4, 5, 1, 1, 1, 1)))
        U_tt = TT(np.reshape(U, (3, 4, 5, 1, 1, 1)))
        T_tt.tensordot(U_tt, 3, overwrite=True)

        TU_back = np.squeeze(T_tt.full())
        error = np.linalg.norm(TU_back - TU)
        self.assertLess(error, self.tol)

        # last-last contraction
        T = np.random.random((2, 3, 4, 5, 6))
        U = np.random.random((4, 5, 6))
        TU = np.tensordot(T, U, axes=([2, 3, 4], [0, 1, 2]))

        T_tt = TT(np.reshape(T, (2, 3, 4, 5, 6, 1, 1, 1, 1, 1)))
        U_tt = TT(np.reshape(U, (4, 5, 6, 1, 1, 1)))
        T_tt.tensordot(U_tt, 3, mode='last-last', overwrite=True)

        TU_back = np.squeeze(T_tt.full())
        error = np.linalg.norm(TU_back - TU)
        self.assertLess(error, self.tol)

        # first-last contraction
        T = np.random.random((2, 3, 4, 5, 6))
        U = np.random.random((2, 3, 4))
        TU = np.tensordot(T, U, axes=([0, 1, 2], [0, 1, 2]))

        T_tt = TT(np.reshape(T, (2, 3, 4, 5, 6, 1, 1, 1, 1, 1)))
        U_tt = TT(np.reshape(U, (2, 3, 4, 1, 1, 1)))
        T_tt.tensordot(U_tt, 3, mode='first-last', overwrite=True)

        TU_back = np.squeeze(T_tt.full())
        error = np.linalg.norm(TU_back - TU)
        self.assertLess(error, self.tol)

        # first-first contraction
        T = np.random.random((2, 3, 4, 5, 6))
        U = np.random.random((2, 3, 4))
        TU = np.tensordot(T, U, axes=([0, 1, 2], [0, 1, 2]))

        T_tt = TT(np.reshape(T, (2, 3, 4, 5, 6, 1, 1, 1, 1, 1)))
        U_tt = TT(np.reshape(U, (2, 3, 4, 1, 1, 1)))
        T_tt.tensordot(U_tt, 3, mode='first-first', overwrite=True)

        TU_back = np.squeeze(T_tt.full())
        error = np.linalg.norm(TU_back - TU)
        self.assertLess(error, self.tol)

        # last-first contraction for operator
        T_tt = rand([2, 3, 4, 5], [5, 6, 7, 8], ranks=2)
        U_tt = rand([4, 5], [7, 8], ranks=3)
        T = T_tt.full()
        U = U_tt.full()

        TU = np.tensordot(T, U, axes=([2, 3, 6, 7], [0, 1, 2, 3]))
        T_tt.tensordot(U_tt, 2, overwrite=True)
        TU_back = T_tt.full()
        error = np.linalg.norm(TU_back - TU)
        self.assertLess(error, self.tol)
Ejemplo n.º 17
0
    def test_multiple_axes(self):
        # contraction over multiple axis
        # last-first contraction
        T = np.random.random((2, 3, 4, 5))
        U = np.random.random((3, 4, 5, 6, 7))
        TU = np.tensordot(T, U, axes=([1, 2, 3], [0, 1, 2]))

        T_tt = TT(np.reshape(T, (2, 3, 4, 5, 1, 1, 1, 1)))
        U_tt = TT(np.reshape(U, (3, 4, 5, 6, 7, 1, 1, 1, 1, 1)))
        T_tt.tensordot(U_tt, 3, overwrite=True)

        TU_back = np.squeeze(T_tt.full())
        error = np.linalg.norm(TU_back - TU)
        self.assertLess(error, self.tol)

        # last-last contraction
        T = np.random.random((2, 3, 4, 5))
        U = np.random.random((6, 7, 4, 5))
        TU = np.tensordot(T, U, axes=([2, 3], [2, 3]))
        TU = np.transpose(TU, [0, 1, 3, 2])

        T_tt = TT(np.reshape(T, (2, 3, 4, 5, 1, 1, 1, 1)))
        U_tt = TT(np.reshape(U, (6, 7, 4, 5, 1, 1, 1, 1)))
        TU_back = T_tt.tensordot(U_tt, 2, mode='last-last')

        TU_back = np.squeeze(TU_back.full())
        error = np.linalg.norm(TU_back - TU)
        self.assertLess(error, self.tol)

        # first-last contraction
        T = np.random.random((2, 3, 4, 5))
        U = np.random.random((6, 7, 2, 3))
        TU = np.tensordot(T, U, axes=([0, 1], [2, 3]))
        TU = np.transpose(TU, [2, 3, 0, 1])

        T_tt = TT(np.reshape(T, (2, 3, 4, 5, 1, 1, 1, 1)))
        U_tt = TT(np.reshape(U, (6, 7, 2, 3, 1, 1, 1, 1)))
        TU_back = T_tt.tensordot(U_tt, 2, mode='first-last')

        TU_back = np.squeeze(TU_back.full())
        error = np.linalg.norm(TU_back - TU)
        self.assertLess(error, self.tol)

        # first-first contraction
        T = np.random.random((2, 3, 4, 5))
        U = np.random.random((2, 3, 6, 7))
        TU = np.tensordot(T, U, axes=([0, 1], [0, 1]))
        TU = np.transpose(TU, [3, 2, 0, 1])

        T_tt = TT(np.reshape(T, (2, 3, 4, 5, 1, 1, 1, 1)))
        U_tt = TT(np.reshape(U, (2, 3, 6, 7, 1, 1, 1, 1)))
        TU_back = T_tt.tensordot(U_tt, 2, mode='first-first')

        TU_back = np.squeeze(TU_back.full())
        error = np.linalg.norm(TU_back - TU)
        self.assertLess(error, self.tol)

        # last-last contraction for operator
        T_tt = rand([3, 4, 5], [6, 7, 8], ranks=3)
        U_tt = rand([8, 9, 4, 5], [2, 3, 7, 8], ranks=2)
        T = T_tt.full()
        U = U_tt.full()

        TU = np.tensordot(T, U, axes=([1, 2, 4, 5], [2, 3, 6, 7]))
        TU = np.transpose(TU, [0, 3, 2, 1, 5, 4])
        T_tt.tensordot(U_tt, 2, mode='last-last', overwrite=True)
        TU_back = T_tt.full()
        error = np.linalg.norm(TU_back - TU)
        self.assertLess(error, self.tol)
Ejemplo n.º 18
0
class TestTT(TestCase):
    def setUp(self):
        """Generate random parameters for a tensor train"""

        # set tolerance for relative errors
        self.tol = 1e-7

        # set threshold and maximum rank for orthonormalization
        self.threshold = 1e-14
        self.max_rank = 50

        # generate random order in [3,5]
        self.order = np.random.randint(3, 6)

        # generate random ranks in [3,5]
        self.ranks = [1] + list(
            np.random.randint(3, high=6, size=self.order - 1)) + [1]

        # generate random row  and column dimensions in [3,5]
        self.row_dims = list(np.random.randint(3, high=6, size=self.order))
        self.col_dims = list(np.random.randint(3, high=6, size=self.order))

        # define cores
        self.cores = [
            2 * np.random.rand(self.ranks[i], self.row_dims[i],
                               self.col_dims[i], self.ranks[i + 1]) - 1
            for i in range(self.order)
        ]

        # construct tensor train
        self.t = TT(self.cores,
                    threshold=self.threshold,
                    max_rank=self.max_rank)

    def test_construction_from_cores(self):
        """test tensor train class for list of cores"""

        # check if all parameters are correct
        self.assertEqual(self.t.order, self.order)
        self.assertEqual(self.t.ranks, self.ranks)
        self.assertEqual(self.t.row_dims, self.row_dims)
        self.assertEqual(self.t.col_dims, self.col_dims)
        self.assertEqual(self.t.cores, self.cores)

        # check if construction fails if ranks are inconsistent
        with self.assertRaises(ValueError):
            TT([np.random.rand(1, 2, 3, 3), np.random.rand(4, 3, 2, 1)])

        # check if construction fails if cores are not 4-dimensional
        with self.assertRaises(ValueError):
            TT([np.random.rand(1, 2, 3), np.random.rand(3, 3, 2, 1)])

    def test_representation(self):
        """test string representation of tensor trains"""

        # get string representation
        string = self.t.__repr__()

        # check if string is not empty
        self.assertIsNotNone(string)

    def test_element(self):
        """test element extraction"""

        # indices of last entry
        indices = self.row_dims + self.col_dims

        # check if element extraction fails if indices are out of range
        with self.assertRaises(IndexError):
            indices[0] += 1
            self.t.element(indices)

        # check if element extraction fails if number of indices is not correct
        with self.assertRaises(ValueError):
            self.t.element(indices[1:])

        # check if element extraction fails if an index is not an integer
        with self.assertRaises(TypeError):
            # noinspection PyTypeChecker
            indices[0] = None
            self.t.element(indices)

        # check if element extraction fails if input is not a list of integers
        with self.assertRaises(TypeError):
            # noinspection PyTypeChecker
            self.t.element("a")

    def test_conversion(self):
        """test conversion to full format and element extraction"""

        # convert to full format
        t_full = self.t.full()

        # number of wrong entries
        err = 0

        # loop through all elements of the tensor
        for i in range(np.int(np.prod(self.row_dims + self.col_dims))):

            # convert flat index
            j = np.unravel_index(i, self.row_dims + self.col_dims)

            # extract elements of both representations
            v = self.t.element(list(j))
            w = t_full[j]

            # count wrong entries
            if (v - w) / v > self.tol:
                err += 1

        # check if no wrong entry exists
        self.assertEqual(err, 0)

    def test_matricize(self):
        """test matricization of tensor trains"""

        # matricize t
        t_mat = self.t.matricize()

        # convert t to full array and reshape
        t_full = self.t.full().reshape(
            [np.prod(self.row_dims),
             np.prod(self.col_dims)])

        # compute relative error
        rel_err = np.linalg.norm(t_mat - t_full) / np.linalg.norm(t_full)

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)

    def test_addition(self):
        """test addition/subtraction of tensor trains"""

        # compute difference of t and itself
        t_diff = (self.t - self.t)

        # convert to full array and reshape to vector
        t_diff = t_diff.full().flatten()

        # convert t to full array and reshape to vector
        t_tmp = self.t.full().flatten()

        # compute relative error
        rel_err = np.linalg.norm(t_diff) / np.linalg.norm(t_tmp)

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)

        # check if addition fails when inputs do not have the same dimensions
        with self.assertRaises(ValueError):
            cores = self.t.cores
            cores[0] = np.random.rand(self.ranks[0], self.row_dims[0] + 1,
                                      self.col_dims[0], self.ranks[1])
            self.t + TT(cores)

        # check if addition fails when input is not a tensor train
        with self.assertRaises(TypeError):
            self.t + 0

    def test_scalar_multiplication(self):
        """test scalar multiplication"""

        # random constant in [0,10]
        c = 10 * np.random.rand(1)[0]

        # multiply tensor train with scalar value, convert to full array, and reshape to vector
        t_tmp = c * self.t
        t_tmp = t_tmp.full().flatten()

        # convert t to full array and reshape to vector
        t_full = self.t.full().flatten()

        # compute error
        err = np.linalg.norm(t_tmp) / np.linalg.norm(t_full) - c

        # check if error is smaller than tolerance
        self.assertLess(err, self.tol)

        # check if multiplication fails when input is neither integer, float, nor complex
        with self.assertRaises(TypeError):
            self.t * "a"

    def test_transpose(self):
        """test transpose of tensor trains"""

        # transpose in TT format, convert to full format, and reshape to vector
        t_trans = self.t.transpose().full().flatten()

        # convert to full format, transpose, and rehape to vector
        t_full = self.t.full().transpose(
            list(np.arange(self.order) + self.order) +
            list(np.arange(self.order))).flatten()

        # compute relative error
        rel_err = np.linalg.norm(t_trans - t_full) / np.linalg.norm(t_full)

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)

    def test_multiplication(self):
        """test multiplication of tensor trains"""

        # multiply t with its tranpose
        t_tmp = self.t.transpose().dot(self.t)

        # convert to full format and reshape to vector
        t_tmp = t_tmp.full().flatten()

        # convert t to full format and matricize
        t_full = self.t.full().reshape(
            [np.prod(self.row_dims),
             np.prod(self.col_dims)])

        # multiply with its transpose and flatten
        t_full = (t_full.transpose().dot(t_full)).flatten()

        # compute relative error
        rel_err = np.linalg.norm(t_tmp - t_full) / np.linalg.norm(t_full)

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)

        # check if multiplication fails when dimensions do not match
        with self.assertRaises(ValueError):
            t_tmp = self.t.copy()
            t_tmp.cores[0] = np.random.rand(self.ranks[0],
                                            self.row_dims[0] + 1,
                                            self.col_dims[0], self.ranks[1])
            self.t.transpose().dot(t_tmp)

        # check if multiplication fails when input is not a tensor train
        with self.assertRaises(TypeError):
            self.t.dot(0)

    def test_construction_from_array(self):
        """test tensor train class for arrays"""

        # convert t to full format and construct tensor train form array
        t_full = self.t.full()

        # construct tensor train
        t_tmp = TT(t_full, threshold=self.threshold, max_rank=self.max_rank)

        # compute difference, convert to full format, and flatten
        t_diff = (self.t - t_tmp).full().flatten()

        # compute relative error
        rel_err = np.linalg.norm(t_diff) / np.linalg.norm(t_full.flatten())

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)

        # check if construction fails if number of dimensions is not a multiple of 2
        with self.assertRaises(ValueError):
            TT(np.random.rand(1, 2, 3))

        # check if construction fails if input is neither a list of cores nor an ndarray
        with self.assertRaises(TypeError):
            TT(None)

    def test_operator(self):
        """test operator check"""

        # check t
        t_check = self.t.isoperator()

        # construct non-operator tensor trains
        cores = [self.cores[i][:, :, 0:1, :] for i in range(self.order)]
        u = TT(cores)
        cores = [self.cores[i][:, 0:1, :, :] for i in range(self.order)]
        v = TT(cores)

        # check u and v
        u_check = u.isoperator()
        v_check = v.isoperator()

        # check if operator checks are correct
        self.assertTrue(t_check)
        self.assertFalse(u_check)
        self.assertFalse(v_check)

    def test_left_orthonormalization(self):
        """test left-orthonormalization"""

        # construct non-operator tensor train
        cores = [self.cores[i][:, :, 0:1, :] for i in range(self.order)]
        t_col = TT(cores)

        # left-orthonormalize t
        t_left = t_col.ortho_left(threshold=1e-14)

        # test if cores are left-orthonormal
        err = 0
        for i in range(self.order - 1):
            c = np.tensordot(t_left.cores[i],
                             t_left.cores[i],
                             axes=([0, 1], [0, 1])).squeeze()
            if np.linalg.norm(c - np.eye(t_left.ranks[i + 1])) > self.tol:
                err += 1

        # convert t_col to full format and flatten
        t_full = t_col.full().flatten()

        # compute relative error
        rel_err = np.linalg.norm(t_left.full().flatten() -
                                 t_full) / np.linalg.norm(t_full)

        # check if t_left is left-orthonormal and equal to t_col
        self.assertEqual(err, 0)
        self.assertLess(rel_err, self.tol)

        # check if orthonormalization fails if maximum rank is not positive
        with self.assertRaises(ValueError):
            t_col.ortho_left(max_rank=0)

        # check if orthonormalization fails if threshold is negative
        with self.assertRaises(ValueError):
            t_col.ortho_left(threshold=-1)

        # check if orthonormalization fails if start and end indices are not integers
        with self.assertRaises(TypeError):
            t_col.ortho_left(start_index="a")
            t_col.ortho_left(end_index="b")

    def test_right_orthonormalization(self):
        """test right-orthonormalization"""

        # construct non-operator tensor train
        cores = [self.cores[i][:, :, 0:1, :] for i in range(self.order)]
        t_col = TT(cores)

        # right-orthonormalize t
        t_right = t_col.ortho_right(threshold=1e-14)

        # test if cores are right-orthonormal
        err = 0
        for i in range(1, self.order):
            c = np.tensordot(t_right.cores[i],
                             t_right.cores[i],
                             axes=([1, 3], [1, 3])).squeeze()
            if np.linalg.norm(c - np.eye(t_right.ranks[i])) > self.tol:
                err += 1

        # convert t_col to full format and flatten
        t_full = t_col.full().flatten()

        # compute relative error
        rel_err = np.linalg.norm(t_right.full().flatten() -
                                 t_full) / np.linalg.norm(t_full)

        # check if t_right is right-orthonormal and equal to t_col
        self.assertEqual(err, 0)
        self.assertLess(rel_err, self.tol)

        # check if orthonormalization fails if maximum rank is not positive
        with self.assertRaises(ValueError):
            t_col.ortho_right(max_rank=0)

        # check if orthonormalization fails if threshold is negative
        with self.assertRaises(ValueError):
            t_col.ortho_right(threshold=-1)

        # check if orthonormalization fails if start and end indices are not integers
        with self.assertRaises(TypeError):
            t_col.ortho_right(start_index="a")
            t_col.ortho_right(end_index="b")

    def test_orthonormalization(self):
        """test orthonormalization"""

        # construct non-operator tensor train
        cores = [self.cores[i][:, :, 0:1, :] for i in range(self.order)]
        t_col = TT(cores)

        # orthonormalize t
        t_right = t_col.ortho(threshold=1e-14)

        # test if cores are right-orthonormal
        err = 0
        for i in range(1, self.order):
            c = np.tensordot(t_right.cores[i],
                             t_right.cores[i],
                             axes=([1, 3], [1, 3])).squeeze()
            if np.linalg.norm(c - np.eye(t_right.ranks[i])) > self.tol:
                err += 1

        # convert t_col to full format and flatten
        t_full = t_col.full().flatten()

        # compute relative error
        rel_err = np.linalg.norm(t_right.full().flatten() -
                                 t_full) / np.linalg.norm(t_full)

        # check if t_right is right-orthonormal and equal to t_col
        self.assertEqual(err, 0)
        self.assertLess(rel_err, self.tol)

        # check if orthonormalization fails if maximum rank is not positive
        with self.assertRaises(ValueError):
            t_col.ortho(max_rank=0)

        # check if orthonormalization fails if threshold is negative
        with self.assertRaises(ValueError):
            t_col.ortho(threshold=-1)

    def test_1_norm(self):
        """test 1-norm"""

        # construct tensor train without negative entries
        cores = [
            np.abs(self.cores[i][:, :, 0:1, :]) for i in range(self.order)
        ]
        tt_col = TT(cores)

        # transpose
        tt_row = tt_col.transpose()

        # convert to full matrix
        tt_mat = tt_col.matricize()

        # compute norms
        norm_tt_row = tt_row.norm(p=1)
        norm_tt_col = tt_col.norm(p=1)
        norm_full = np.linalg.norm(tt_mat, 1)

        # compute relative errors
        rel_err_row = (norm_tt_row - norm_full) / norm_full
        rel_err_col = (norm_tt_col - norm_full) / norm_full

        # construct tensor-train operator without negative entries
        cores = [np.abs(self.cores[i][:, :, :, :]) for i in range(self.order)]
        tt_op = TT(cores)

        # convert to full matrix
        tt_mat = tt_op.matricize()

        # compute norms
        norm_tt = tt_op.norm(p=1)
        norm_full = np.linalg.norm(tt_mat, 1)

        # compute relative error
        rel_err = (norm_tt - norm_full) / norm_full

        # check if relative errors are smaller than tolerance
        self.assertLess(rel_err_row, self.tol)
        self.assertLess(rel_err_col, self.tol)
        self.assertLess(rel_err, self.tol)

    def test_2_norm(self):
        """test 2-norm"""

        # construct tensor train
        cores = [self.cores[i][:, :, 0:1, :] for i in range(self.order)]
        tt_col = TT(cores)

        # transpose
        tt_row = tt_col.transpose()

        # convert to full matrix
        tt_mat = tt_col.matricize()

        # compute norms
        norm_tt_row = tt_row.norm(p=2)
        norm_tt_col = tt_col.norm(p=2)
        norm_full = np.linalg.norm(tt_mat, 2)

        # compute relative errors
        rel_err_row = (norm_tt_row - norm_full) / norm_full
        rel_err_col = (norm_tt_col - norm_full) / norm_full

        # define tensor-train operator
        tt_op = self.t

        # convert to full matrix
        tt_mat = tt_op.matricize()

        # compute norms
        norm_tt = tt_op.norm(p=2)
        norm_full = np.linalg.norm(tt_mat, 'fro')

        # compute relative error
        rel_err = (norm_tt - norm_full) / norm_full

        # check if relative errors are smaller than tolerance
        self.assertLess(rel_err_row, self.tol)
        self.assertLess(rel_err_col, self.tol)
        self.assertLess(rel_err, self.tol)

    def test_p_norm(self):
        """test for p-norm, p>2"""

        with self.assertRaises(ValueError):
            self.t.norm(p=3)

    def test_qtt2tt_tt2qtt(self):
        """test qtt2tt and tt2qtt"""

        # suppose t to be in QTT format and contract first two cores of t
        t_tt = self.t.qtt2tt([2, self.order - 2])

        # convert t_tt to full array, and flatten
        t_tt_full = t_tt.full().flatten()

        # split the cores of t_tt, convert to full array, and flatten
        t_qtt = t_tt.tt2qtt([self.row_dims[0:2], self.row_dims[2:]],
                            [self.col_dims[0:2], self.col_dims[2:]],
                            threshold=1e-14)

        # convert t_qtt to full array, and flatten
        t_qtt_full = t_qtt.full().flatten()

        # convert t and to full format and flatten
        t_full = self.t.full().flatten()

        # compute relative errors
        rel_err_tt = np.linalg.norm(t_tt_full -
                                    t_full) / np.linalg.norm(t_full)
        rel_err_qtt = np.linalg.norm(t_qtt_full -
                                     t_full) / np.linalg.norm(t_full)

        # check if relative errors are smaller than tolerance
        self.assertLess(rel_err_tt, self.tol)
        self.assertLess(rel_err_qtt, self.tol)

    def test_pinv(self):
        """test pinv"""

        # construct non-operator tensor train
        cores = [self.cores[i][:, :, 0:1, :] for i in range(self.order)]
        t = TT(cores)

        # compute pseudoinverse
        t_pinv = TT.pinv(t, self.order - 1)

        # matricize tensor trains
        t = t.full().reshape([np.prod(self.row_dims[:-1]), self.row_dims[-1]])
        t_pinv = t_pinv.full().reshape(
            [np.prod(self.row_dims[:-1]), self.row_dims[-1]]).transpose()

        # compute relative errors
        rel_err_1 = np.linalg.norm(t.dot(t_pinv).dot(t) -
                                   t) / np.linalg.norm(t)
        rel_err_2 = np.linalg.norm(t_pinv.dot(t).dot(t_pinv) -
                                   t_pinv) / np.linalg.norm(t_pinv)
        rel_err_3 = np.linalg.norm((t.dot(t_pinv)).transpose() -
                                   t.dot(t_pinv)) / np.linalg.norm(
                                       t.dot(t_pinv))
        rel_err_4 = np.linalg.norm((t_pinv.dot(t)).transpose() -
                                   t_pinv.dot(t)) / np.linalg.norm(
                                       t_pinv.dot(t))

        # check if relative errors are smaller than tolerance
        self.assertLess(rel_err_1, self.tol)
        self.assertLess(rel_err_2, self.tol)
        self.assertLess(rel_err_3, self.tol)
        self.assertLess(rel_err_4, self.tol)

    def test_zeros(self):
        """test tensor train of all zeros"""

        # construct tensor train of all zeros
        t_zeros = tt.zeros(self.t.row_dims, self.t.col_dims)

        # compute norm
        t_norm = np.linalg.norm(t_zeros.full().flatten())

        # check if norm is 0
        self.assertEqual(t_norm, 0)

    def test_ones(self):
        """test tensor train of all ones"""

        # construct tensor train of all ones, convert to full format, and flatten
        t_ones = tt.ones(self.row_dims, self.col_dims).full().flatten()

        # construct full array of all ones
        t_full = np.ones(
            np.int(np.prod(self.row_dims)) * np.int(np.prod(self.col_dims)))

        # compute relative error
        rel_err = np.linalg.norm(t_ones - t_full) / np.linalg.norm(t_full)

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)

    def test_eye(self):
        """test identity tensor train"""

        # construct identity tensor train, convert to full format, and flatten
        t_eye = tt.eye(self.row_dims).full().flatten()

        # construct identity matrix and flatten
        t_full = np.eye(np.int(np.prod(self.row_dims))).flatten()

        # compute relative error
        rel_err = np.linalg.norm(t_eye - t_full) / np.linalg.norm(t_full)

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)

    def test_unit(self):
        """test unit tensor train"""

        # construct unit tensor train, convert to full format, and flatten
        t_unit = tt.unit(self.row_dims, [0] * self.order).full().flatten()

        # construct unit vector
        t_full = np.eye(np.int(np.prod(self.row_dims)), 1).T

        # compute relative error
        rel_err = np.linalg.norm(t_unit - t_full) / np.linalg.norm(t_full)

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)

    def test_random(self):
        """test random tensor train"""

        # construct random tensor train
        t_rand = tt.rand(self.row_dims, self.col_dims)

        # check if attributes are correct
        self.assertEqual(t_rand.order, self.order)
        self.assertEqual(t_rand.row_dims, self.row_dims)
        self.assertEqual(t_rand.col_dims, self.col_dims)
        self.assertEqual(t_rand.ranks, [1] * (self.order + 1))

    def test_uniform(self):
        """test uniform tensor train"""

        # construct uniform tensor train
        norm = 10 * np.random.rand()
        t_uni = tt.uniform(self.row_dims, ranks=self.ranks[1], norm=norm)

        # compute norms
        norm_tt = t_uni.norm()

        # compute relative error
        rel_err = (norm_tt - norm) / norm

        # check if relative error is smaller than tolerance
        self.assertLess(rel_err, self.tol)