dense_ttsvd#

t3toolbox.backend.t3_svd.dense_ttsvd(T, min_ranks=None, max_ranks=None, rtol=None, atol=None)#
def dense_ttsvd(
        T: common.NDArray,  # shape=(N0,...,N(d-1))
        min_ranks:  typ.Sequence[int] = None, # len=d+1
        max_ranks:  typ.Sequence[int] = None,  # len=d+1
        rtol: float = None,
        atol: float = None,
) -> typ.Tuple[
    typ.Tuple[common.NDArray,...], # tt_cores
    typ.Tuple[common.NDArray,...], # singular values of unfoldings
]:

Compute tensor train (TT) decomposition and unfolding singular values for dense tensor.

Parameters:
  • T (NDArray) – The dense tensor. shape=(N1, …, Nd)

  • min_ranks (typ.Sequence[int]) – Minimum TT-ranks for truncation. len=d+1. e.g., (1,3,3,3,1)

  • max_ranks (typ.Sequence[int]) – Maximum TT-ranks for truncation. len=d+1. e.g., (1,5,5,5,1)

  • rtol (float) – Relative tolerance for truncation.

  • atol (float) – Absolute tolerance for truncation.

  • xnp – Linear algebra backend. Default: np (numpy)

Returns:

  • typ.Tuple[NDArray,…] – TT cores. len=d. elm_shape=(ri, ni, r(i+1))

  • typ.Tuple[NDArray,…] – Singular values of unfoldings. len=d+1. elm_shape=(ri,)

Return type:

t3toolbox.backend.common.typ.Tuple[t3toolbox.backend.common.typ.Tuple[NDArray, Ellipsis], t3toolbox.backend.common.typ.Tuple[NDArray, Ellipsis]]

See also

truncated_svd, dense_tucker_svd, t3_svd_dense, t3_svd

Examples

No truncation – a lossless TT decomposition. The cores reconstruct T; there are d cores and d+1 singular-value vectors (the two boundary entries are both just ||T||):

>>> import numpy as np
>>> import t3toolbox.backend.t3_svd as t3_svd
>>> np.random.seed(0)
>>> T = np.random.randn(5, 6, 7)
>>> cores, ss = t3_svd.dense_ttsvd(T)
>>> print([G.shape for G in cores])               # cores[i].shape = (ri, ni, r(i+1))
[(1, 5, 5), (5, 6, 7), (7, 7, 1)]
>>> T2 = np.einsum('aib,bjc,ckd->ijk', cores[0], cores[1], cores[2])
>>> print(np.allclose(T, T2))                     # exact reconstruction
True
>>> print(len(ss), np.allclose(ss[0], np.linalg.norm(T)), np.allclose(ss[-1], np.linalg.norm(T)))
4 True True

The internal singular values ARE the singular values of the matrix unfoldings (shown for the first unfolding; the others are analogous):

>>> import numpy as np
>>> import t3toolbox.backend.t3_svd as t3_svd
>>> np.random.seed(0)
>>> T = np.random.randn(5, 6, 7)
>>> _, ss = t3_svd.dense_ttsvd(T)
>>> dense_svals = np.linalg.svd(T.reshape(5, 6 * 7), compute_uv=False)   # first unfolding
>>> print(np.allclose(ss[1], dense_svals[:len(ss[1])]))
True

Truncation – a smooth tensor has gradually decaying unfolding spectra, so rtol truncates meaningfully (a sharp random spectrum would not):

>>> import numpy as np
>>> import t3toolbox.backend.t3_svd as t3_svd
>>> i, j, k = np.ogrid[1:9, 1:9, 1:9]
>>> T = 1.0 / (i + j + k)                          # graded-spectrum tensor
>>> cores_f, ss_full = t3_svd.dense_ttsvd(T)           # full (untruncated) spectra
>>> cores, ss = t3_svd.dense_ttsvd(T, rtol=1e-3)       # truncate at rtol
>>> full_ranks = tuple(G.shape[0] for G in cores_f) + (1,)
>>> tt_ranks = tuple(G.shape[0] for G in cores) + (1,)
>>> print(full_ranks, '->', tt_ranks)
(1, 8, 8, 1) -> (1, 3, 3, 1)
>>> T2 = np.einsum('aib,bjc,ckd->ijk', cores[0], cores[1], cores[2])
>>> dropped_sq = sum(float(np.sum(s[r:]**2)) for s, r in zip(ss_full[1:-1], tt_ranks[1:-1]))
>>> print(bool(np.linalg.norm(T - T2) <= np.sqrt(dropped_sq)))  # accuracy bound [Oseledets]
True