Skip to content

Understand and control planned execution

Use plan_* when the same mathematical operation recurs with new coefficients. The returned module fixes the signature, operation, input and output layouts, storage contracts, and any pairwise item-axis convention while leaving coefficient values and ordinary leading batch dimensions dynamic.

import torch
from clifra import ResourceLimits, TensorContract, make_algebra

limits = ResourceLimits()
algebra = make_algebra(3, dtype=torch.float64, resource_limits=limits)
vector = algebra.layout((1,))
bivector = algebra.layout((2,))
wedge = algebra.plan_product(
    op="wedge", left=vector, right=vector, output=bivector
)
assert wedge.inputs == (TensorContract.compact(vector),) * 2
assert wedge.output == TensorContract.compact(bivector)

for batch in (2, 5):
    x = torch.randn(batch, 3, dtype=torch.float64, requires_grad=True)
    y = torch.randn(3, dtype=torch.float64)
    result = wedge(x, y)
    assert result.shape == (batch, 3)
    result.square().sum().backward()
    assert torch.isfinite(x.grad).all()

Each call executes the resolved operation directly, without consulting the algebra or planner. Gradients belong to the input tensor graph; reusing a plan does not retain a previous call's graph. buffers and compose them with other operations.

Construction checks the algebra's resource_limits against the static requirements of eligible execution routes. These limits bound coefficient widths and fixed interaction footprints, excluding caller-controlled batch sizes. A successful plan therefore does not guarantee that every batch fits device memory. The limits determine feasibility, not a performance policy; see planning resource limits to configure them.

Direct calls such as algebra.wedge(...) remain useful for exploratory work. They resolve declarations and use cached execution machinery. An explicit plan makes that boundary visible and gives a stable callable for a loop or compiled function.

Changing an input layout, requested output grades, or pairwise requires a new plan. .to() changes execution placement, not the mathematical contracts. Build plans in the intended precision: movement converts execution buffers directly rather than rerunning algorithm selection. Request only the output grades needed by the next operation; the requested result is a grade projection, not a requirement to construct every intermediate full multivector.