ut3_tie_tucker_factors ====================== .. py:function:: t3toolbox.backend.sharing.ut3_tie_tucker_factors(data, sharing) .. code-block:: python def ut3_tie_tucker_factors( data: typ.Tuple[ NDArray, # tucker_supercore, shape=(d,)+stack+(n,N) NDArray, # tt_supercore, shape=(d,)+stack+(r,n,r) typ.Sequence[int], # shape, static int tuple typ.Tuple[NDArray, NDArray], # (tucker_edge_mask, tt_edge_mask), HOST bool, static ], sharing: typ.Sequence, # len=d, static; one hashable group label per mode ) -> typ.Tuple[ NDArray, # tucker_supercore with each group's slices set to the group mean NDArray, # tt_supercore, untouched typ.Sequence[int], # shape, untouched typ.Tuple[NDArray, NDArray], # masks, untouched (the tie changes values, never ranks) ]: The uniform twin of :py:func:`t3_tie_tucker_factors`: tie the Tucker factors exactly, by per-group arithmetic averaging of the supercore slices. Use this to repair **numerical drift** away from equal factors without round-tripping through the ragged layer -- e.g. after many low-precision first-order steps, where an exactly-tied start can creep apart. TT cores, shape and masks are untouched: averaging changes factor *values*, never ranks, and a group's Tucker rank masks are required equal anyway (structural, raises otherwise). **Garbage-transparent, so no masking is needed.** A group's rank masks are equal, so every real slot is real at every mode of the group and the mean of the real content uses only real values; the padding averages to other padding, which is don't-care either way (``docs/uniform_equivalence_contract.md``). As in the ragged twin the mean is computed as ``B_ref + mean(B_i - B_ref)``, so an exactly-tied group is a **bitwise fixed point** for any group size. Unlike the ragged twin there is no array identity to preserve -- a supercore holds one slice per mode -- so ties are exact by *value*, which is what the uniform checkers compare. .. rubric:: Examples >>> import numpy as np >>> import t3toolbox.tucker_tensor_train as t3 >>> import t3toolbox.uniform_tucker_tensor_train as ut3 >>> import t3toolbox.backend.sharing as sharing >>> np.random.seed(0) >>> x = t3.TuckerTensorTrain.randn((6, 6, 5), (3, 3, 2), (1, 2, 2, 1)) >>> u = ut3.UniformTuckerTensorTrain.from_t3(x) # untied >>> print(bool(sharing.ut3_sharing_residual(u.data, (0, 0, 1)) > 0.1)) True >>> tied = sharing.ut3_tie_tucker_factors(u.data, (0, 0, 1)) >>> print(float(sharing.ut3_sharing_residual(tied, (0, 0, 1)))) 0.0 Masks and TT cores come back untouched, and re-tying is a bitwise fixed point: >>> print(bool(np.array_equal(tied[1], u.data[1])), tied[2] == u.data[2]) True True >>> again = sharing.ut3_tie_tucker_factors(tied, (0, 0, 1)) >>> print(bool(np.array_equal(np.asarray(again[0]), np.asarray(tied[0])))) True