t3m_form_then_round =================== .. py:function:: t3toolbox.backend.t3_linalg.t3m_form_then_round(x, y, max_tucker_ranks = None, max_tt_ranks = None, rtol = None, atol = None) .. code-block:: python def t3m_form_then_round( x: typ.Tuple[typ.Sequence[NDArray], typ.Sequence[NDArray]], # (tucker_cores_x, tt_cores_x) y: typ.Tuple[typ.Sequence[NDArray], typ.Sequence[NDArray]], # (tucker_cores_y, tt_cores_y) max_tucker_ranks: typ.Union[int, typ.Sequence[int], None] = None, # scalar caps all, or len=d max_tt_ranks: typ.Union[int, typ.Sequence[int], None] = None, # scalar caps all, or len=d+1 rtol: typ.Optional[float] = None, # requires unstacked (enforced by the frontend) atol: typ.Optional[float] = None, ) -> typ.Tuple[ typ.Tuple[NDArray, ...], # x_times_y tucker_cores typ.Tuple[NDArray, ...], # x_times_y tt_cores ]: Elementwise product ``x ⊙ y`` -- method (a): form the full product, then round. Forms the full Khatri-Rao/Kronecker product (:py:func:`t3_mult`, multiplied ranks) and, if any truncation is requested, rounds it with :py:func:`t3svd`; with no truncation it returns the exact full product directly. The forming step is embarrassingly parallel (no sweep) but materializes the whole product -- see ``docs/ttm_t3m_ht_note.tex`` for the cost trade-off vs the fused / swap methods. Stack-aware with max-rank truncation; ``rtol``/``atol`` require unstacked.