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))^Tof the probe map to residuals; returns a T3Tangent atframe.The adjoint of
probe(). The residualsztildeslive in the forward probe space,elm_shape = W + K + C + (Ni,)(probe stackWouter, optional tangent batchK, frame stackCinner – the output space of aK-stackedprobe();Kis empty in the common case). The tangent batchKis always carried to the result’s tangent stack; the probe stackWis summed or kept persum_over_probes:sum_over_probes=False(default): each probe residual becomes one tangent – the result’s tangent stack isW + K(frame stackC).sum_over_probes=True: the probe stack is summed – the result’s tangent stack isK(frame stackC) – the usual Gauss-NewtonJ^T r(a single tangent whenK = ()).
Falseis the primary transpose (Wa passthrough stack);Trueis the derived contractionsum_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
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 batchKis 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=Trueis exactly the probe-stack (W) sum of theFalseresult:>>> 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