Skip to content

feat(userApi): layer factory API — PR 1 (infra + Linear/ReLU/Flatten)#174

Open
LeoBuron wants to merge 2 commits into
developfrom
layer-factory-api-pr1
Open

feat(userApi): layer factory API — PR 1 (infra + Linear/ReLU/Flatten)#174
LeoBuron wants to merge 2 commits into
developfrom
layer-factory-api-pr1

Conversation

@LeoBuron
Copy link
Copy Markdown
Member

Summary

Introduces the new layer factory API per docs/superpowers/specs/2026-05-15-layer-factory-api-design.md:

  • New types: layerQuant_t (4-slot quantization profile, strict-required-per-relevant-field), bias_t tri-state enum (BIAS_DEFAULT/TRUE/FALSE), linearInit_t (designated-init-friendly config struct)
  • New userApi modules: LayerQuant, LayerWeightsApi, StateDictApi
  • New QuantizationApi builders: quantizationInitSymInt32WithBits(rm, bits), quantizationInitSym(bits, rm)
  • Rewritten linearLayerInit + new linearLayerInitOwning (Borrowing/Owning variants — ownsQuantizations flag drives free-time teardown)
  • Rewritten reluLayerInit + new reluLayerInitOwning
  • Renamed pre-existing factories to *Legacy for two-binary coexistence (strategy Z) — linearLayerInitLegacy, linearLayerInitNonTrainableLegacy, freeLinearLayerLegacy, reluLayerInitLegacy, freeReluLayerLegacy

Three layers chosen to exercise the three distinct factory shapes:

  • Linear — params + geometry + bias + quant (full case)
  • ReLU — no params, no geometry, has quant
  • Flatten — unchanged from existing signature (already matches new spec; no rewrite needed)

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 *Legacy variants.

Test plan

  • All unit tests pass on unit_test_debug preset (47/47)
  • All unit tests pass on unit_test_error preset (47/47, strict log level)
  • All unit tests pass on production unit_test preset (42/42)
  • Python tests pass (22/22)
  • Local ci script green (C + Python combined)
  • TDD red→green discipline followed throughout (failing test committed separately, then GREEN commit)
  • Reviewer check: layerQuant_t field-relevance docs match what each layer actually reads
  • Reviewer check: freeLinearLayer/freeReluLayer Owning-variant teardown matches ownsQuantizations=true factory side

Spec

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

LeoBuron added 2 commits May 23, 2026 11:58
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
@LeoBuron LeoBuron force-pushed the layer-factory-api-pr1 branch from 2588296 to b6a8a51 Compare May 23, 2026 09:58
@LeoBuron LeoBuron closed this May 23, 2026
@LeoBuron LeoBuron reopened this May 23, 2026
@LeoBuron LeoBuron closed this May 23, 2026
@LeoBuron LeoBuron reopened this May 23, 2026
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.

1 participant