feat(autodiff): add the generated tensor constant operator and reader - #130
Open
AlekseiChirkovVention wants to merge 3 commits into
Open
feat(autodiff): add the generated tensor constant operator and reader#130AlekseiChirkovVention wants to merge 3 commits into
AlekseiChirkovVention wants to merge 3 commits into
Conversation
Pin the concrete zero-operand constant operator, its descriptor schema, the shared reader and every categorized rejection it must produce, and the package-level export. The reader is the single parser every backend uses to materialize a generated constant, so a malformed descriptor must fail with a categorized error naming the offending node rather than leaking a builtin exception into consumer code.
The parametrized cases for a null and a non-sequence shape crashed while building the node under test, before the reader was ever called, because the helper derived a default typespec from the malformed parameters. Pass an explicit typespec, matching the two sibling malformed-descriptor tests. No assertion changes.
A backend without a native reduction needs generated constant tensors to exist as graph nodes before an expansion can multiply against them. Add a concrete zero-operand operator carrying a fill value, dtype, and shape, plus one validated reader so every backend materializes a constant from the same schema instead of hand-parsing operator parameters. A concrete operator type, rather than a parameter on an existing operator, is what lets handler dispatch reject a backend that does not understand the construct before any handler runs. Existing operators keep their operand contracts unchanged. Every rejection is a categorized error naming the offending node: a parameter key outside the schema, an operand on a constant node, or a wrong operator type are structural defects, while an absent shape or dtype keeps its own more specific category. Booleans are refused in both numeric positions.
code-tc
approved these changes
Aug 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Introduce the single public representation of a generated tensor-valued constant operand, so
that a later expansion can multiply against a tensor of ones that actually exists in the graph.
Goal
Add
FillOperator— a concrete zero-operandTensorOperatorwhoseop_paramsis exactly{fill, dtype, shape}— together with theFillDescriptorrecord and the sharedfill_descriptorreader, so every backend materializes a constant from one validated schemainstead of hand-parsing operator parameters.
A concrete operator type, rather than a parameter on an existing operator, is what lets
handler dispatch reject a backend that does not understand the construct before any handler
runs. Existing operators keep their operand contracts unchanged: no handler anywhere sees a
new operand count, a new parameter key, or a new shape convention.
Acceptance criteria
FillOperatoris concrete, declaresroute_name"fill", and adds no field beyond thebase operator.
fill_descriptorreturns the declared fill, dtype, and shape from a node and from itsOperationContext, as the same record.output_typespecequals its descriptor's dtype and shape.dimension, an absent or non-string dtype, a node carrying an operand, and a node whose
operator is not a fill each raise a categorized error naming the node id.
KeyError,IndexError,TypeError,ValueError, orAssertionErrorescapes thereader for those inputs.
tinychain.autodiff, present in__all__, and absentfrom the top-level
tinychainpackage."fill"is absent from the tracing capture allowlist and the error-category tuple isunchanged.
Test evidence
Level: unit. Tests first: the failing set landed in
ce1a5e2, before anyimplementation existed;
7a869c7made it pass.py/tests/test_autodiff_fill_contract.py— 17 test functions, 59 parametrized cases, allpassing. Coverage per criterion: the operator shape by three tests asserting
route_nameandthe declared field set; the reader by two tests comparing the node and context results; the
typespec agreement by one; the categorized-rejection table by two 15-case parametrizations
plus three targeted tests; the builtin-leak guarantee by a third 15-case parametrization; the
export surface by one test asserting
__all__membership, identity against the module, andabsence from the top-level package; and the two nothing-changed criteria by one test each.
Neighbouring autodiff suites: 182 passed. Broad non-integration suite: 973 passed, 9 failed,
1 skipped — all 9 failures pre-existing and unrelated to this branch (7 require the unbuilt
local backend, 2 are symbolic-style guards naming a file this branch does not touch).
Stack position
Stack position 1 of 8 — based on
main.This is the base of a stacked series; the seven branches that follow build on it in order, so
they cannot reach
mainbefore this one does.Review notes
Known follow-ups, already recorded and deliberately not fixed here:
TypeErrorfor a hand-builtOperationContextwhoseop_paramsis not iterable or whose operand list is not sized. Unreachable through the framework's own
construction path, which always supplies a mapping proxy and a tuple. The guard is deferred
because the same one must serve the expansion emitters that will share this module.
real guarantee is the truthful-shape audit that lands with the expansion passes, where
production code emits the typespec.