tv_probe_transpose#

t3toolbox.backend.probing.tv_probe_transpose(ztildes, ww, frame, sum_over_probes=False)#
def tv_probe_transpose(
        ztildes:        typ.Union[typ.Sequence[NDArray],    NDArray], # len=d, elm_shape=(...,Ni)
        ww:             typ.Union[typ.Sequence[NDArray],    NDArray], # input vectors, len=d, elm_shape=(...,Ni)
        frame:           typ.Union[
            typ.Tuple[
                typ.Sequence[NDArray],  # up_tucker_cores. len=d. U_xo U_yo   = I_xy, U.shape = (nU, N)
                typ.Sequence[NDArray],  # down_tt_cores.   len=d. O_ixj O_iyj = I_xy  O.shape = (rL, nO, rR)
                typ.Sequence[NDArray],  # left_tt_cores.   len=d. P_iax P_iay = I_xy, P.shape = (rL, nU, rR)
                typ.Sequence[NDArray],  # right_tt_cores.  len=d. Q_xaj Q_yaj = I_xy  Q.shape = (rL, nU, rR)
            ],
            typ.Tuple[
                NDArray,  # up_tucker_supercore. shape=(d, nU, N),      up orthogonal elements
                NDArray,  # down_tt_supercore.   shape=(d, rL, nO, rR), down orthogonal elements
                NDArray,  # left_tt_supercore.   shape=(d, rL, nU, rR), left orthogonal elements
                NDArray,  # right_tt_supercore.  shape=(d, rL, nU, rR), right orthogonal elements
            ],
        ], # frame order = T3Frame.data = (up, down, left, right) = (U, O, P, Q)
        sum_over_probes: bool = False,
) -> typ.Union[
    typ.Tuple[
        typ.Tuple[NDArray,...], # dU_tildes. len=d, elm_shape=(...,nOi,Ni)
        typ.Tuple[NDArray,...], # dG_tildes. len=d, elm_shape=(...,rLi,nUi,rRi)
    ],
    typ.Tuple[
        NDArray,  # dU_tildes. shape=(d, ..., nOi, Ni)
        NDArray,  # dG_tildes. shape=(d, ..., rLi, nUi, rRi)
    ],
]:

Apply the transpose of the map from a tangent vector to its probes (apply (J^(s))^T to ztildes).

Stacking (handled by the W/C custom contractions in contractions.py): the residuals ztildes live in the forward probe space, elm_shape = W + C + (Ni,) (probe stack W outermost, T3 stack C innermost – base-inner), while ww carries only the probe stack W. With sum_over_probes=False the resulting variations keep both stacks (W + C + ..., the probe stack W becoming the tangent stack K); with sum_over_probes=True the probe stack W is summed and the T3 stack C is kept (C + ...).

See Section 6.2.3, particularly Algorithm 8, in:

Alger, N., Christierson, B., Chen, P., & Ghattas, O. (2026). “Tucker Tensor Train Taylor Series.” arXiv preprint arXiv:2603.21141. https://arxiv.org/abs/2603.21141

Parameters:
  • ztildes (typ.Sequence[NDArray]) – Probe residuals to apply the transpose map to. len=d, elm_shape=(…,Ni)

  • ww (typ.Sequence[NDArray]) – input vectors that defined the (forward) probe map. len=d, elm_shape=(…,Ni)

  • frame ((up_tucker_cores, down_tt_cores, left_tt_cores, right_tt_cores)) – Orthogonal frame for the point where the tangent space attaches to the manifold. This is exactly T3Frame.data order (U, O, P, Q) – pass frame.data directly, no reorder.

  • sum_over_probes (bool) – Sum results over all probe residuals, rather than returning results for each probe residual.

Returns:

Tangent variations (a bvf.T3Variations.data tuple) resulting from applying the transpose map.

Return type:

(dU_tildes, dG_tildes)

See also

t3_probe, tv_probe

Examples

Adjoint identity <z, J v> = <J^T z, v> with one set of probing vectors:

>>> import numpy as np
>>> import t3toolbox.corewise as cw
>>> import t3toolbox.tucker_tensor_train as t3
>>> import t3toolbox.frame_variations_format as bvf
>>> import t3toolbox.manifold as t3m
>>> import t3toolbox.backend.probing as t3p
>>> np.random.seed(0)
>>> x = t3.TuckerTensorTrain.randn((10, 11, 12), (5, 6, 4), (1, 2, 3, 1))
>>> frame, _ = bvf.t3_orthogonal_representations(x)
>>> probe_frame = frame.data  # probing's frame order == T3Frame.data, no reorder
>>> ww = (np.random.randn(10), np.random.randn(11), np.random.randn(12))
>>> v = t3m.MANIFOLD.randn(frame)
>>> z = (np.random.randn(10), np.random.randn(11), np.random.randn(12))
>>> Jv  = t3p.tv_probe(ww, v.variations.data, probe_frame)
>>> JTz = t3p.tv_probe_transpose(z, ww, probe_frame)   # (dU_tildes, dG_tildes)
>>> lhs = cw.corewise_dot(z, Jv)                  # <z, J v>
>>> rhs = cw.corewise_dot(JTz, v.variations.data)  # <J^T z, v>
>>> print(bool(np.allclose(lhs, rhs)))
True

With sum_over_probes=True (the Gauss-Newton J^T r), the adjoint identity still holds when a probe stack W is summed on both sides:

>>> import numpy as np
>>> import t3toolbox.corewise as cw
>>> import t3toolbox.tucker_tensor_train as t3
>>> import t3toolbox.frame_variations_format as bvf
>>> import t3toolbox.manifold as t3m
>>> import t3toolbox.backend.probing as t3p
>>> np.random.seed(0)
>>> x = t3.TuckerTensorTrain.randn((10, 11, 12), (5, 6, 4), (1, 2, 3, 1))
>>> frame, _ = bvf.t3_orthogonal_representations(x)
>>> ww = (np.random.randn(2, 10), np.random.randn(2, 11), np.random.randn(2, 12))  # W=(2,)
>>> v = t3m.MANIFOLD.randn(frame)
>>> z = (np.random.randn(2, 10), np.random.randn(2, 11), np.random.randn(2, 12))
>>> Jv  = t3p.tv_probe(ww, v.variations.data, frame.data)        # W-stacked probes
>>> JTz = t3p.tv_probe_transpose(z, ww, frame.data, sum_over_probes=True)
>>> lhs = cw.corewise_dot(z, Jv)                  # sum_W <z_W, (J v)_W>
>>> rhs = cw.corewise_dot(JTz, v.variations.data)  # <sum_W J^T z_W, v>
>>> print(bool(np.allclose(lhs, rhs)))
True