T3Tangent.probe_transpose#

static t3toolbox.manifold.T3Tangent.probe_transpose(ztildes, ww, frame, sum_over_probes=False)#
def probe_transpose(
        ztildes:            typ.Sequence[NDArray],  # probe residuals, len=d, elm_shape=W+K+C+(Ni,)
        ww:                 typ.Sequence[NDArray],  # probing vectors, len=d, elm_shape=W+(Ni,)
        frame:              bvf.T3Frame,
        sum_over_probes:    bool = False,           # True: sum the probe stack W (Gauss-Newton J^T r)
) -> 'T3Tangent':

Apply the transpose (J^(s))^T of the probe map to residuals; returns a T3Tangent at frame.

The adjoint of probe(). The residuals ztildes live in the forward probe space, elm_shape = W + K + C + (Ni,) (probe stack W outer, optional tangent batch K, frame stack C inner – the output space of a K-stacked probe(); K is empty in the common case). The tangent batch K is always carried to the result’s tangent stack; the probe stack W is summed or kept per sum_over_probes:

  • sum_over_probes=False (default): each probe residual becomes one tangent – the result’s tangent stack is W + K (frame stack C).

  • sum_over_probes=True: the probe stack is summed – the result’s tangent stack is K (frame stack C) – the usual Gauss-Newton J^T r (a single tangent when K = ()).

False is the primary transpose (W a passthrough stack); True is the derived contraction sum_over_probes=True == Σ_W sum_over_probes=False. See Batching & stacking §11 (docs/batching_and_stacking.md) for which mode to use and why.

Bare (J^(s))^T (no gauge projector). See Section 6.2.3 (Algorithm 8) of Alger et al. (2026).

See also

probe

Examples

Adjoint identity <z, J v> = <J^T z, v> (sum over probes):

>>> import numpy as np
>>> import t3toolbox.tucker_tensor_train as t3
>>> import t3toolbox.frame_variations_format as bvf
>>> import t3toolbox.manifold as t3m
>>> x = t3.TuckerTensorTrain.randn((10, 11, 12), (5, 6, 4), (1, 2, 3, 1))
>>> frame, _ = bvf.t3_orthogonal_representations(x)
>>> v = t3m.MANIFOLD.randn(frame)
>>> ww = (np.random.randn(2, 10), np.random.randn(2, 11), np.random.randn(2, 12))
>>> z = (np.random.randn(2, 10), np.random.randn(2, 11), np.random.randn(2, 12))
>>> Jv = v.probe(ww)
>>> JTz = t3m.T3Tangent.probe_transpose(z, ww, frame, sum_over_probes=True)
>>> lhs = float(np.sum([np.sum(a * b) for a, b in zip(z, Jv)]))
>>> print(bool(abs(lhs - float(JTz.corewise_inner(v))) < 1e-9))
True

Without summing, the result is a tangent-stacked T3Tangent (V = the probe stack):

>>> JTz_batch = t3m.T3Tangent.probe_transpose(z, ww, frame)  # sum_over_probes=False
>>> print(JTz_batch.tangent_stack_shape, JTz_batch.frame_stack_shape)
(2,) ()

With K-stacked residuals (W + K + C), the tangent batch K is carried through:

>>> zb = tuple(np.random.randn(2, 3, N) for N in (10, 11, 12))  # W=(2,), K=(3,), C=()
>>> print(t3m.T3Tangent.probe_transpose(zb, ww, frame, sum_over_probes=True).tangent_stack_shape)
(3,)
>>> print(t3m.T3Tangent.probe_transpose(zb, ww, frame).tangent_stack_shape)  # sum=False -> W + K
(2, 3)

sum_over_probes=True is exactly the probe-stack (W) sum of the False result:

>>> kept   = t3m.T3Tangent.probe_transpose(z, ww, frame)                        # W stays a stack
>>> summed = t3m.T3Tangent.probe_transpose(z, ww, frame, sum_over_probes=True)   # W summed
>>> dU_sum = tuple(np.sum(c, axis=0) for c in kept.variations.tucker_variations)  # sum the W axis
>>> err = max(float(np.linalg.norm(a - b))
...           for a, b in zip(dU_sum, summed.variations.tucker_variations))
>>> print(bool(err < 1e-9))
True
Parameters:
  • ztildes (t3toolbox.backend.common.typ.Sequence[NDArray])

  • ww (t3toolbox.backend.common.typ.Sequence[NDArray])

  • frame (T3Frame)

  • sum_over_probes (bool)

Return type:

T3Tangent