dense_tucker_svd#

t3toolbox.backend.t3_svd.dense_tucker_svd(T, min_ranks=None, max_ranks=None, rtol=None, atol=None)#
def dense_tucker_svd(
        T: common.NDArray, # shape=(N1, N2, .., Nd)
        min_ranks:  typ.Sequence[int] = None, # len=d
        max_ranks:  typ.Sequence[int] = None,  # len=d
        rtol: float = None,
        atol: float = None,
) -> typ.Tuple[
    typ.Tuple[
        typ.Tuple[common.NDArray,...], # Tucker bases, ith_elm_shape=(ni, Ni)
        common.NDArray, # Tucker core, shape=(n1,n2,...,nd)
    ],
    typ.Tuple[common.NDArray,...], # singular values of matricizations
]:

Compute Tucker decomposition and matricization singular values for dense tensor.

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

  • min_ranks (typ.Sequence[int]) – Minimum Tucker ranks for truncation. len=d

  • max_ranks (typ.Sequence[int]) – Maximum Tucker ranks for truncation. len=d

  • rtol (float) – Relative tolerance for truncation.

  • atol (float) – Absolute tolerance for truncation.

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

Returns:

  • typ.Tuple[typ.Tuple[NDArray,…],NDArray] – Tucker decomposition (tucker_bases, tucker_core). tucker_bases[ii].shape=(ni,Ni). tucker_core.shape=(n1,…,nd)

  • typ.Tuple[NDArray,…] – Singular values of matricizations

Return type:

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

See also

truncated_svd, tt_svd_dense, t3_svd_dense, t3_svd

Examples

No truncation – a lossless Tucker decomposition. The factors reconstruct T, and bases[ii].shape = (ni, Ni) (the small rank ni first), one singular-value vector per mode:

>>> import numpy as np
>>> import t3toolbox.backend.t3_svd as t3_svd
>>> np.random.seed(0)
>>> T = np.random.randn(5, 6, 7)
>>> (bases, core), ss = t3_svd.dense_tucker_svd(T)
>>> print(core.shape, [B.shape for B in bases])
(5, 6, 7) [(5, 5), (6, 6), (7, 7)]
>>> T2 = np.einsum('abc,ai,bj,ck->ijk', core, bases[0], bases[1], bases[2])
>>> print(np.allclose(T, T2))                          # exact reconstruction
True

The mode-i singular values ARE the singular values of the mode-i matricization (shown for mode 0; the other modes 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_tucker_svd(T)
>>> dense_svals = np.linalg.svd(T.reshape(5, 6 * 7), compute_uv=False)   # mode-0 matricization
>>> print(np.allclose(ss[0], dense_svals[:len(ss[0])]))
True

Truncation – a smooth tensor has gradually decaying matricization 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
>>> (bases_f, _), ss_full = t3_svd.dense_tucker_svd(T)         # full (untruncated) spectra
>>> (bases, core), _ = t3_svd.dense_tucker_svd(T, rtol=1e-3)   # truncate at rtol
>>> print(tuple(B.shape[0] for B in bases_f), '->', tuple(B.shape[0] for B in bases))
(8, 8, 8) -> (3, 3, 3)
>>> T2 = np.einsum('abc,ai,bj,ck->ijk', core, bases[0], bases[1], bases[2])
>>> ranks = tuple(B.shape[0] for B in bases)
>>> dropped_sq = sum(float(np.sum(s[r:]**2)) for s, r in zip(ss_full, ranks))
>>> print(bool(np.linalg.norm(T - T2) <= np.sqrt(dropped_sq)))  # accuracy bound [Oseledets]
True