feat(userApi): layer factory API — PR 1 (infra + Linear/ReLU/Flatten)#174
Open
LeoBuron wants to merge 2 commits into
Open
feat(userApi): layer factory API — PR 1 (infra + Linear/ReLU/Flatten)#174LeoBuron wants to merge 2 commits into
LeoBuron wants to merge 2 commits into
Conversation
Factory takes layerQuant_t* and stores .forwardMath / .backwardMath as the layer's forward/backward quantizations. Owning variant deep-copies both. freeReluLayer reads ownsQuantizations to decide whether to also tear down the two quantization_t*. Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md sections 4, 5 feat(layer): add ownsQuantizations flag to linearConfig_t Sets up the Borrowing/Owning factory variants — free* reads this flag to decide whether to tear down the four quantization_t* slots. Legacy factories explicitly set false; new factories (next commits) set true in the Owning variant. Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md section 5 test(layer): failing tests for new linearLayerInit Borrowing variant Tests for shape correctness, BIAS_DEFAULT resolution, BIAS_FALSE leaving bias NULL, and the documented PRINT_ERROR path (TEST_IGNORE'd because Unity cannot catch exit()). Tests fail at link with 'undefined reference to linearLayerInit' — implementation follows next. Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md section 4 feat(layer): implement linearLayerInit Borrowing variant Factory allocates weight + bias parameters internally with KAIMING_UNIFORM weights / ZEROS bias. Stores the four lq pointers verbatim (no copy); sets ownsQuantizations=false. freeLinearLayer tears down the parameters unconditionally and the quantizations only when ownsQuantizations=true. Uses the new initTensor + initDistribution API rather than the deprecated tensorInitWithDistribution (the plan's reference call passed NULL for the data pointer, which would have segfaulted; the new API allocates data internally). The factory clones each lq->*Storage via getQLike so the tensor owns its quantization independently of the borrowed lq slots. ZEROS bias init is a no-op because reserveMemory (calloc) already gives a zero-initialized buffer; initDistribution(ZEROS) would have been a memset over the same zeros. Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md sections 4, 5.1, 5.3 test(layer): failing tests for linearLayerInitOwning Owning variant Verify deep copy of all four quantization_t* into fresh allocations, ownsQuantizations=true, and clean teardown without leaks. Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md section 5.2 feat(layer): implement linearLayerInitOwning (deep-copy variant) Factory deep-copies each of the four quantization_t* in lq into fresh reserveMemory allocations. Always four separate copies (no aliasing even if lq slots shared a pointer), keeping freeLinearLayer simple. Caller can drop lq + all four quantizations immediately after the call. Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md section 5.2 test(layer): failing tests for new reluLayerInit Borrowing + Owning Adds ownsQuantizations to reluConfig_t and declares the new factory signatures. Tests fail at link until impl lands in next commit. Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md sections 4, 5
Verifies the iterator skips param-less layers and consumes entries[] in model order. Count mismatch path is TEST_IGNORE'd (fires exit()). Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md section 8.2 refactor(userApi): rename Linear factories to *Legacy for new API coexistence Prepares for the layerQuant_t-based factory API by freeing the canonical linearLayerInit/freeLinearLayer names. Legacy signatures are functionally unchanged. Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md refactor(userApi): rename Relu factories to *Legacy for new API coexistence Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md feat(userApi): add bias_t tri-state enum in LayerCommon.h Foundation for the layer factory API: lets *Init_t structs distinguish BIAS_DEFAULT (zero-init, resolved per layer to PyTorch default) from explicit BIAS_TRUE / BIAS_FALSE. Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md feat(userApi): add layerQuant_t profile struct (declaration + stub) Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md section 3.1 feat(userApi): implement layerQuantInitUniform Sets all four slots of a layerQuant_t to the same quantization_t*. Caller retains ownership; helper does not copy. Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md section 3.1 feat(userApi): add quantizationInitSymInt32WithBits Lets callers specify the active fixed-point bit width directly; the existing quantizationInitSymInt32(rm) hardcodes qMaxBits=16. Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md section 7 feat(userApi): add quantizationInitSym Sub-byte symmetric quantization builder. Previously missing from QuantizationApi.h — only the low-level initSymQConfig + initSymQuantization pair was available. Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md section 7 feat(userApi): declare linearInit_t and new Linear factory signatures No implementation yet — those follow in subsequent commits with TDD-paced tests. Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md sections 3.3, 4 test(userApi): layerLoadWeights LINEAR case — weight + bias overwrite Verifies the LINEAR dispatch case: memcpy from buffer into the factory-allocated tensor data, including the no-bias variant. Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md section 8.1 feat(userApi): add modelLoadStateDict aggregate loader Iterates model[] and dispatches layerLoadWeights for each param layer, consuming entries[] in order. Param-less layers are skipped. Errors loudly on count mismatch or NULL weightData. Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md section 8.2 feat(userApi): add layerLoadWeights dispatcher (LINEAR case) Per-layer buffer-load primitive; dispatches by layer->type. PR 1 implements the LINEAR case (Conv* cases added in PR 2). Param-less layers (ReLU/Softmax/Flatten/Pool) error loudly. Refs spec: docs/superpowers/specs/2026-05-15-layer-factory-api-design.md section 8.1
2588296 to
b6a8a51
Compare
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.
Summary
Introduces the new layer factory API per
docs/superpowers/specs/2026-05-15-layer-factory-api-design.md:layerQuant_t(4-slot quantization profile, strict-required-per-relevant-field),bias_ttri-state enum (BIAS_DEFAULT/TRUE/FALSE),linearInit_t(designated-init-friendly config struct)LayerQuant,LayerWeightsApi,StateDictApiQuantizationApibuilders:quantizationInitSymInt32WithBits(rm, bits),quantizationInitSym(bits, rm)linearLayerInit+ newlinearLayerInitOwning(Borrowing/Owning variants —ownsQuantizationsflag drives free-time teardown)reluLayerInit+ newreluLayerInitOwning*Legacyfor two-binary coexistence (strategy Z) —linearLayerInitLegacy,linearLayerInitNonTrainableLegacy,freeLinearLayerLegacy,reluLayerInitLegacy,freeReluLayerLegacyThree layers chosen to exercise the three distinct factory shapes:
PR 2 will add Conv1d, Conv1dTransposed, MaxPool1d, AvgPool1d, Softmax factories + example migration (
har_classifier_v2,ecg_anomaly_ae_v2) + bit-parity CI step. A separate cleanup PR after PR 2 drops the*Legacyvariants.Test plan
unit_test_debugpreset (47/47)unit_test_errorpreset (47/47, strict log level)unit_testpreset (42/42)ciscript green (C + Python combined)layerQuant_tfield-relevance docs match what each layer actually readsfreeLinearLayer/freeReluLayerOwning-variant teardown matchesownsQuantizations=truefactory sideSpec
docs/superpowers/specs/2026-05-15-layer-factory-api-design.md(gitignored, local working spec)docs/superpowers/plans/2026-05-15-layer-factory-api-pr1.md(gitignored, local implementation plan)🤖 Generated with Claude Code