Skip to content

feat(tensor): TensorId — structured identity from the module tree, assignTensorIds, describe() renderer (SKEEP-003 P1, S0.5) - #1054

Merged
michalharakal merged 1 commit into
developfrom
feature/1010-tensor-id
Aug 23, 2026
Merged

michalharakal merged 1 commit into
developfrom
feature/1010-tensor-id

Conversation

@michalharakal

Copy link
Copy Markdown
Contributor

Summary

SKEEP-003 slice S0.5 (milestone M0 #1001, PRD M0-F5; decisions #9/#10): TensorId — one stable, human-readable identity per tensor derived from the model structure — plus the describe() debugger renderer.

  • sk.ainet.lang.tensor.TensorId(modulePath, parameter, discriminator?): structured, with canonical (model.layers.blk.3.attn.q_proj.weight, …scores#step=17), legacyPath() (the slash form bindPaths / the WeightNameResolvers use: MLP/blk.0/attn), parse(canonical), fromLegacyPath(path, parameter), withDiscriminator, view(range).
  • Tensor.id: TensorId? default member (null = anonymous — a matMul b in a notebook stays free); TensorIdBearer { var id } implemented by VoidOpsTensor, OpsBoundTensor (delegates to its origin so ids survive re-binding to another context) and the context-factory origin tensor.
  • ModuleNode.assignTensorIds(root = name): TensorIdAssignment — explicit, idempotent walk over the module tree: module-path segments are exactly bindPaths' segments (id.legacyPath() == node.path), parameter = short name ("<module>.weight" → weight); tensors that cannot carry an id are reported in notCarried, all are in the tensors map (ids["model.layers.blk.0.attn.weight"]). No runtime code path changes (M0-A5).
  • Tensor.describe() / TensorStorage.describe(id) (sk.ainet.lang.memory, opt-in): TensorId · Format · Shape · storage kind · origin · scope · StorageId with — for the M1-only fields (scope, storage id); file-backed storage prints Mapped · <path> @0x<offset>.
  • Tests: TensorIdTest, AssignTensorIdsTest (tree walk, bindPaths parity, idempotence, not-carried, re-binding), DescribeTest.
  • BCV: lang-core jvm dump regenerated (additions only).

Activation ids (debug-only in eager, always-on when tracing) are the hook withDiscriminator is for; wiring them into tracing is P7. toString() is not overridden (existing tests and logs rely on current renderings); describe() is the renderer the IDE/debugger surface (decision #12) builds on.

Stacked on #1051 (S0.4 — Format); retarget to develop after it merges.

Test plan

Full local gate (scripts/pr-gate.sh, JDK 25) on the stacked tree; results in the first comment.

Closes #1010

🤖 Generated with Claude Code

@michalharakal

Copy link
Copy Markdown
Contributor Author

Local gate scripts/pr-gate.sh (JDK 25) on e821ffb (stacked on #1051):

=== pr-gate: JVM tests ===                                      BUILD SUCCESSFUL in 4m 20s (run 1; run 2 up-to-date)
=== pr-gate: apiCheck ===                                       BUILD SUCCESSFUL (backend-cpu dump regenerated: inherited getId())
=== pr-gate: verifyNpmPins jsTest wasmJsTest wasmWasiTest ===   BUILD SUCCESSFUL in 2m 39s
=== pr-gate: linuxX64Test ===                                   BUILD SUCCESSFUL in 1m 29s
=== pr-gate: assemble ===                                       BUILD SUCCESSFUL in 2m 9s
=== pr-gate: :skainet-test:skainet-test-java:test ===           BUILD SUCCESSFUL in 21s
pr-gate: all legs passed.

Targeted: lang-core sk.ainet.lang.memory.*, TensorIdTest, sk.ainet.lang.nn.topology.* 23/23.

…signTensorIds, describe() renderer (SKEEP-003 P1)

Milestone M0 (#1001), PRD M0-F5, decisions #9/#10: one stable, human-
readable identity per tensor, the same whether materialized or symbolic.

- sk.ainet.lang.tensor.TensorId(modulePath, parameter, discriminator?):
  canonical ("model.layers.blk.3.attn.q_proj.weight", "…scores#step=17"),
  legacyPath() (the slash form bindPaths / WeightNameResolvers use),
  parse(canonical), fromLegacyPath, withDiscriminator, view(range).
  Equality and hashing are by canonical string: parse splits dotted
  module names ("blk.0") into more segments than they were built from,
  so ids that print the same must be the same id.
- Tensor.id: TensorId? default member (null = anonymous); TensorIdBearer
  { var id } implemented by VoidOpsTensor, OpsBoundTensor (delegates to
  its origin so ids survive re-binding) and the context-factory origin.
- ModuleNode.assignTensorIds(root = name): TensorIdAssignment — explicit,
  idempotent walk; module-path segments equal bindPaths' segments
  (id.legacyPath() == node.path), parameter = short name
  ("<module>.weight" -> weight); tensors that cannot carry an id are
  reported in notCarried. No runtime code path changes (M0-A5).
- Tensor.describe() / TensorStorage.describe(id) (sk.ainet.lang.memory):
  "TensorId · Format · Shape · storage kind · origin · scope · StorageId",
  with — for the M1-only fields.
- Tests: TensorIdTest, AssignTensorIdsTest, DescribeTest. BCV lang-core
  jvm dump regenerated (additions; VoidOpsTensor/OpsBoundTensor gain the
  TensorIdBearer interface).

Closes #1010

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@michalharakal
michalharakal force-pushed the feature/1010-tensor-id branch from e821ffb to 6ca4ed9 Compare August 22, 2026 21:08
@michalharakal

Copy link
Copy Markdown
Contributor Author

Rebased onto the updated base after #1053 merged into feature/1008-format-and-encoding (lang-core API dump regenerated; no source change). The chain is now: #1051 (Format + AllocationSpec) ← #1054 (TensorId) ← #1055 (NameMap) ← #1056 (MemoryPlan) ← #1057 (skainet-plan CLI); merging each into its base keeps the next one a single-commit diff.

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.

[S0.5] P1: TensorId — structured identity from the module tree, canonical string, toString() renderer

1 participant