Gradient Policies

Gradient policies control how backpropagation interacts with the absorptive bottom ⊥. They live in zeroproofml.autodiff.policies and can be overridden for the current execution context via the gradient_policy context manager.

Available Policies

  • CLAMP (default): Zeroes gradients on ⊥ paths and clamps finite gradients to [-1, 1].
  • PROJECT: Masks gradients when the forward value is ⊥; used for projective heads where ⊥ indicates points at infinity.
  • REJECT: Always returns zero gradient on the local path; learning must come from other differentiable objectives or explicit soft surrogates. The hard rejection_loss(is_bottom) helper is metric-style accounting.
  • PASSTHROUGH: For debugging; gradients propagate even through ⊥.

Usage

from zeroproofml.autodiff.policies import GradientPolicy, gradient_policy, apply_policy

with gradient_policy(GradientPolicy.PROJECT):
    loss.backward()

Design Notes

  • Policies are deterministic and compatible with XLA/TorchScript because they avoid Python-side branching on tensors.
  • Selection precedence is explicit policy argument, then the active context override, then a registered layer default, then CLAMP. An explicit layer-level gradient_policy therefore remains stronger than a surrounding context.
  • Policy defaults can be registered per-layer using register_policy(layer, policy). Layer identifiers are trimmed and normalized to lowercase; empty identifiers and non-enum policies are refused.
  • POLICY_REGISTRY is a read-only compatibility view. Use policy_registry_snapshot() for an immutable point-in-time mapping and restore_policy_registry(snapshot) for a fully validated atomic replacement. GradientPolicyConfig.to_dict() serializes the stable enum value, and from_dict() rejects unknown values.
  • gradient_policy(...) overrides registered layer defaults while the context is active (layers can still set an explicit gradient_policy=... parameter to opt out).
  • Context overrides use Python context-local state. A child asyncio task inherits its parent's active policy when the child is created, then keeps an isolated copy; later changes do not leak between parent and child or between overlapping tasks. New threads start with no override and therefore use layer defaults or CLAMP.
  • Projective mode typically pairs GradientPolicy.PROJECT with detached renormalisation to avoid NaN gradients.