Skip to content

Add Axon.deferred/2 - #651

Merged
seanmor5 merged 2 commits into
mainfrom
sm-deferred
Aug 18, 2026
Merged

Add Axon.deferred/2#651
seanmor5 merged 2 commits into
mainfrom
sm-deferred

Conversation

@seanmor5

Copy link
Copy Markdown
Contributor

No description provided.

seanmor5 and others added 2 commits August 15, 2026 15:34
Adds `Axon.deferred/2`, which defers construction of a subgraph until
the parent template shapes are known at initialization time. This
covers cases where the structure of one part of a graph depends on the
shape of another part that isn't statically derivable at the call site
— decoders mirroring an encoder's input dim, projection heads matching
a backbone's hidden size, adapters wrapping an opaque inner model.

The factory receives a `{parent, template}` tuple per parent and must
return an `%Axon{}` rooted at those parents. It runs once, the first
time `init_fn` is called, and the resulting subgraph is compiled and
reused for the lifetime of the `build/2` result. Like blocks, deferred
subgraphs namespace their parameters under the layer name.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The deferred node memoized its materialized subgraph in an ETS table
created per node per Axon.build/2 call. Nothing ever deleted it, so
every build leaked a table: 200 builds left 200 live :axon_deferred
tables, which survive garbage collection. A process that rebuilds a
deferred model in a loop would eventually exhaust ERL_MAX_ETS_TABLES
and take down the VM.

Drop the cache and rebuild the subgraph from the parent templates in
both init and predict. The factory is a pure function of its templates,
so both paths agree on the subgraph and the parameters line up.

This trades the cache for extra tracing work. Under a caching Nx.Defn
compiler, or Axon.compile/4, the subgraph is built once per input
signature and not at all on subsequent calls. Under the default
evaluator, which re-traces on every call, it is rebuilt per call - the
same order of work the evaluator already does for the parent graph.

Since the invocation count is now compiler-dependent, the factory is
documented as needing to be pure, and the test asserts that repeated
traces agree rather than pinning an exact count.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@seanmor5
seanmor5 merged commit 47edfe3 into main Aug 18, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants