TuckerTensorTrain.continuation_ranks#

t3toolbox.tucker_tensor_train.TuckerTensorTrain.continuation_ranks(tau=10.0, n_chunk=1, kappa_guard=1000000000000.0, max_grow=None, rtol=None, atol=None, sharing=None)#
def continuation_ranks(
        self: 'TuckerTensorTrain',
        tau:         float = 10.0,   # grow an edge only if kappa_i < kappa_max / tau  (tau > 1)
        n_chunk:     int   = 1,      # rank increment added to each grown edge
        kappa_guard: float = 1e12,   # absolute safety cap: never grow an edge with kappa_i >= this
        max_grow:    typ.Optional[int] = None,  # cap on #edges grown per call (None = all eligible)
        rtol:        float = None,
        atol:        float = None,
        sharing:     typ.Sequence = None,       # len=d; group labels (None = unshared)
) -> Tuple[
    Tuple[int, ...],  # (n0', ..., n(d-1)')  new Tucker ranks
    Tuple[int, ...],  # (r0', ..., rd')      new TT ranks
]:

Rank-continuation update (Section 5.4.1): the ranks to grow to next, from this iterate’s spectra.

Computes the implicit T3-SVD of self and feeds the unfolding singular values to t3toolbox.backend.ranks.compute_continuation_ranks(), which grows the well-conditioned edges (each edge’s condition number a factor tau below the worst) so the ranks trend toward comparable conditioning across edges. Pair with resize() for the zero-padded warm start of the next fit – this is the outer loop of Riemannian fitting with rank continuation:

new_tucker, new_tt = X.continuation_ranks()
X0 = X.resize(X.shape, new_tucker, new_tt)   # warm start at the grown ranks (same tensor)

The current ranks are read from the SVD: with the default (no rtol/atol) these are the structural ranks of self – which, for a converged minimal-rank iterate, are its core ranks; pass rtol/atol to continue from the numerical rank at that tolerance instead. kappa_guard is an absolute conditioning safety net (see the backend function): when no edge is below it, the returned ranks equal self’s current ranks – the caller’s signal to stop continuation. (The current ranks are also returned when the structure is already maximal; both mean “stop”.) Defined for a single (unstacked) T3 only.

max_grow caps how many edges grow per call: None (default) grows every eligible edge at once; max_grow=1 grows one edge at a time (the single best-conditioned edge that has structural room) – pair with tau=1.0 for the most conservative, finest-grained continuation.

With sharing (Tucker factors tied within groups; the T3 must already be tied – safe mode checks), a group’s modes are ONE edge: the grouped t3svd() reports the group spectrum s_g at every group mode (the singular values of the concatenated matricization [T_(i1)|...|T_(ik)] = the Jacobian spectrum of the shared factor, so its condition number is exactly the tied subproblem’s conditioning), the group contributes one condition number to the pool, one growth decision applies group-wide (a group counts as ONE max_grow candidate), and useless-rank removal is the shared one (the group ceiling). Pair with resize(..., sharing=...) so the zero-padded warm start stays exactly tied (one array per group).

Examples

Mid-continuation: at a low-rank iterate, ask which ranks to grow to next, then warm-start there by zero-padding (resize) – the represented tensor is unchanged, ready for the next fit:

>>> import numpy as np
>>> import t3toolbox.tucker_tensor_train as t3
>>> i, j, k = np.ogrid[1:7, 1:7, 1:7]
>>> A = 1.0 / (i + 2 * j + 4 * k)                                   # smooth, anisotropic per mode
>>> X = t3.TuckerTensorTrain.t3svd_dense(A, max_tucker_ranks=2, max_tt_ranks=2)[0]
>>> print(X.tucker_ranks, X.tt_ranks)                              # a rank-2 iterate
(2, 2, 2) (1, 2, 2, 1)
>>> new_tucker, new_tt = X.continuation_ranks()
>>> print(new_tucker, new_tt)                                      # the grown ranks for the next fit
(3, 3, 3) (1, 3, 3, 1)
>>> X0 = X.resize(X.shape, new_tucker, new_tt)                     # zero-padded warm start
>>> print(X0.tucker_ranks, bool(np.allclose(X0.to_dense(), X.to_dense())))
(3, 3, 3) True

The edge condition numbers that drive the choice (length-1 boundary TT bonds are 1.0). Here all edges are comparably conditioned (~15–24), so none is a factor tau=10 below the worst and the fallback grows them uniformly:

>>> import t3toolbox.backend.ranks as ranks
>>> _, ss_tucker, ss_tt = X.t3svd()
>>> kappa_tucker, kappa_tt = ranks.edge_condition_numbers(ss_tucker, ss_tt)
>>> print([round(k, 2) for k in kappa_tucker])
[23.94, 15.91, 14.91]

A stricter tau grows only edges well below the worst – here it holds back the stiffest Tucker mode and TT bond (condition number ~24) while growing the better-conditioned ones:

>>> print(X.continuation_ranks(tau=1.5))
((2, 3, 3), (1, 2, 3, 1))

max_grow=1 grows only the single best-conditioned edge that has room (one edge at a time), instead of every eligible edge at once:

>>> print(X.continuation_ranks(tau=1.5, max_grow=1))
((2, 3, 2), (1, 2, 2, 1))

Shared factors: at a tied iterate, the group’s modes grow (or hold) TOGETHER, from the one group spectrum – and with max_grow=1 the whole group counts as the single grown edge:

>>> np.random.seed(0)
>>> Xs = t3.TuckerTensorTrain.t3svd_dense(A)[0].share((0, 0, 1), max_tucker_ranks=2,
...                                                   max_tt_ranks=2)
>>> print(Xs.tucker_ranks, Xs.tt_ranks)
(2, 2, 2) (1, 2, 2, 1)
>>> print(Xs.continuation_ranks(sharing=(0, 0, 1)))
((3, 3, 3), (1, 3, 3, 1))
>>> print(Xs.continuation_ranks(tau=1.0, max_grow=1, sharing=(0, 0, 1)))
((3, 3, 2), (1, 2, 2, 1))
Parameters:
  • tau (float)

  • n_chunk (int)

  • kappa_guard (float)

  • max_grow (Optional[int])

  • rtol (float)

  • atol (float)

  • sharing (Sequence)

Return type:

Tuple[Tuple[int, …], Tuple[int, …]]