fovi.arch.knn_autograd
Training-capable compact backends for KNNConvLayer.
The baseline layer computes
y[b, o, n] = bias[o] + sum_{c,k} x[b, c, knn[k, n]] * weight[o, c*V + rf_index[n, k]]
through a dense one-hot einsum followed by F.linear. The backends here use the
compact contraction width P = Cin*K instead of Q = Cin*V and provide a custom
autograd path so training no longer falls back to the baseline.
Structure:
TrainingMetabundles the derived index tables every backend consumes, including a reverse-CSR structure mapping each input node to the (k, n) pairs that reference it (needed for deterministic grad-input kernels).KNNConvFunctionis the singletorch.autograd.Function; per-backend compute is looked up inOPS_REGISTRYso kernel backends (Warp, CUDA) only need to register an ops object withforward/grad_input/grad_weight.torch_scatterreplaces the dense one-hot einsum withscatter_add_and keeps native autograd;torch_compactis the compact bmm formulation with the custom backward.
Gradient accumulation is always fp32; scattered writes go through index_add_ on
fp32 buffers (never fp16 atomics). Under autocast, inputs are cast to the autocast
dtype inside the Function, grad_input is returned in x.dtype and grad_weight /
grad_bias in the parameter dtype (fp32 master weights under AMP).
- fovi.arch.knn_autograd.dataclass(cls=None, /, *, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False)[source]
Add dunder methods based on the fields defined in the class.
Examines PEP 526 __annotations__ to determine fields.
If init is true, an __init__() method is added to the class. If repr is true, a __repr__() method is added. If order is true, rich comparison dunder methods are added. If unsafe_hash is true, a __hash__() method is added. If frozen is true, fields may not be assigned to after instance creation. If match_args is true, the __match_args__ tuple is added. If kw_only is true, then by default all fields are keyword-only. If slots is true, a new class with a __slots__ attribute is returned.
- class fovi.arch.knn_autograd.TrainingMeta(cin: int, cout: int, nin: int, nout: int, k: int, v: int, p: int, p64: int, q: int, input_linear: Tensor, weight_linear: Tensor, input_linear_flat: Tensor, weight_linear_flat: Tensor, rev_rowptr: Tensor, rev_col: Tensor, device: device)[source]
Bases:
objectDerived, device-resident index tables shared by all training backends.
- fovi.arch.knn_autograd.ensure_training_metadata(layer, device: device) TrainingMeta[source]
Build (or fetch cached)
TrainingMetaforlayerondevice.Cached on
layer._knn_training_metadata; released byfovi.arch.knn_optimization.clear_cache(). Never entersstate_dict.
- fovi.arch.knn_autograd._nout_chunk(meta: TrainingMeta, batch: int, itemsize: int) int[source]
Largest Nout slice keeping per-chunk staging (W_eff + A + dA) under budget.
- fovi.arch.knn_autograd._pad_flat_input(meta: TrainingMeta, x: Tensor) Tensor[source]
[B, Cin, Nin] -> [B, Cin*Nin + 1] with a trailing zero pad column.
- fovi.arch.knn_autograd._effective_weight_t(meta: TrainingMeta, weight: Tensor) Tensor[source]
weight [Cout, Q] -> transposed compact operand source [Q, Cout], contiguous.
- class fovi.arch.knn_autograd.CompactTorchOps[source]
Bases:
objectPure-torch compact ops: gather -> bmm forward, bmm -> fp32 index_add_ backward.
Serves as the correctness oracle and the structural skeleton for kernel backends. All three entry points take the compute-dtype tensors prepared by
KNNConvFunction(casting/AMP policy handled there, not here).- name = 'torch_compact'
- fovi.arch.knn_autograd.register_ops(ops) None[source]
Register a kernel backend’s ops object (must expose name/forward/grad_input/grad_weight).
- class fovi.arch.knn_autograd.KNNConvFunction(*args, **kwargs)[source]
Bases:
Function- static forward(ctx, x, weight, bias, meta, ops_name)[source]
Define the forward of the custom autograd Function.
This function is to be overridden by all subclasses. There are two ways to define forward:
Usage 1 (Combined forward and ctx):
@staticmethod def forward(ctx: Any, *args: Any, **kwargs: Any) -> Any: pass
It must accept a context ctx as the first argument, followed by any number of arguments (tensors or other types).
See Combined or separate forward() and setup_context() for more details
Usage 2 (Separate forward and ctx):
@staticmethod def forward(*args: Any, **kwargs: Any) -> Any: pass @staticmethod def setup_context(ctx: Any, inputs: Tuple[Any, ...], output: Any) -> None: pass
The forward no longer accepts a ctx argument.
Instead, you must also override the
torch.autograd.Function.setup_context()staticmethod to handle setting up thectxobject.outputis the output of the forward,inputsare a Tuple of inputs to the forward.See Extending torch.autograd for more details
The context can be used to store arbitrary data that can be then retrieved during the backward pass. Tensors should not be stored directly on ctx (though this is not currently enforced for backward compatibility). Instead, tensors should be saved either with
ctx.save_for_backward()if they are intended to be used inbackward(equivalently,vjp) orctx.save_for_forward()if they are intended to be used for injvp.
- static backward(ctx, grad_y)[source]
Define a formula for differentiating the operation with backward mode automatic differentiation.
This function is to be overridden by all subclasses. (Defining this function is equivalent to defining the
vjpfunction.)It must accept a context
ctxas the first argument, followed by as many outputs as theforward()returned (None will be passed in for non tensor outputs of the forward function), and it should return as many tensors, as there were inputs toforward(). Each argument is the gradient w.r.t the given output, and each returned value should be the gradient w.r.t. the corresponding input. If an input is not a Tensor or is a Tensor not requiring grads, you can just pass None as a gradient for that input.The context can be used to retrieve tensors saved during the forward pass. It also has an attribute
ctx.needs_input_gradas a tuple of booleans representing whether each input needs gradient. E.g.,backward()will havectx.needs_input_grad[0] = Trueif the first input toforward()needs gradient computed w.r.t. the output.
- fovi.arch.knn_autograd.compact_forward(layer, x: Tensor, ops_name: str = 'torch_compact') Tensor[source]
Layer-level entry point for registry-backed backends (training and inference).
- fovi.arch.knn_autograd.scatter_forward(layer, x: Tensor) Tensor[source]
Baseline-equivalent forward with
scatter_add_instead of the one-hot einsum.Exact-arithmetic-identical to the baseline (padding neighbors gather the zero pad node and scatter zeros; rf-bin collisions sum, exactly as the einsum does), fully differentiable through native autograd, and valid for every dtype/device.