compute: design record for std/tensor on the CPU (step 1 of #82) #438

Closed
opened 2026-10-23 16:10:12 +00:00 by orion · 3 comments
Collaborator

Context

Step 1 of #82 (compute: std/tensor on the CPU; ROADMAP "Computation and machine learning", COMPUTE.md sections 1 and 2). Buffer<Item> is built (#54, #299, #300), but std/tensor is not one PR: COMPUTE.md is a stub that says its own full design "comes before the first of these packages is built". Nothing yet fixes the type (Tensor<Scalar> and what its shape and strides are), the scalar bounds (Numeric, Real, Plain), whether a slice is a copy or a view, the broadcasting rule and how a shape mismatch is reported, which operators exist and whether the checker accepts operator traits with a foreign Other and Output (a tensor times a scalar), how a result is written in place, and what the first parallel loop needs (windows still takes an ArrayList, not a Buffer). That has to be decided in writing before any code. This step is documentation only.

Goal

A design record docs/design/TENSOR.md that specifies std/tensor on the CPU as pure TorbScript on Buffer<Item> and cuts the build into slices, each one PR.

Acceptance criteria

  • docs/design/TENSOR.md exists, headed and indexed like the other records (status line "designed, not built", an entry in docs/design/index.md); COMPUTE.md section 1 (layer 1) and section 2 and the ROADMAP point "Computation and machine learning" link it; docs/ regenerates skills/ (torb docs skill docs skills, a tier A gate).
  • Type and layout: decides the representation of Tensor<Scalar> (shape and strides as run-time Ints over a Buffer<Scalar>, offset, row-major order), the invariants a value keeps, how a zero-sized dimension and a rank 0 tensor behave, and the construction functions of the first slice (zeros, ones, filled, from a flat list plus a shape, from std/random). Each is a numbered decision with its reason.
  • Scalars: states the bounds per operation (arithmetic under Numeric, squareRoot/exp-like members under Real, Plain where a window is handed to a worker) and what Float32 lacks (extend Float32 with Real, LINEAR gap 8) and Float16/BFloat16 (not built, out of scope). Says whether a Float32 Real is a prerequisite slice, and which scalar the first slices test on (Float64, Int, Fixed).
  • Slices and views: decides copy against view for tensor[a..b, c], in light of Buffer.slice copying (#54) and Window (CONCURRENCY section 6) being a borrow for one call; the value-semantics consequence is written out with an example.
  • Broadcasting and errors: the broadcasting rule (NumPy-style, with the exact shape arithmetic), and how a shape mismatch is reported (panic or Result) following PANICS.md and the conventions of std/linear.
  • Operations of the first release: the list with exact signatures: elementwise add/subtract/multiply/divide as methods and operators, scalar operands, reshape, transpose, reductions (sum, min, max, mean, optionally along an axis), dot and matmul. For the operators the record runs a scratch torb check probe (shown in the record) for Tensor * Scalar and Scalar * Tensor under today's checker (COMPUTE.md section 3, "operator traits with a foreign Other and Output") and records the result; a checker gap becomes its own issue, named in the record, never part of a std/tensor slice.
  • In place: how a result is written without a copy where there is one owner (var fn, a pre-sized result Buffer, the Buffer promise of #54), and how this meets windows/Window (today over ArrayList, to move to Buffer with #55) for the data-parallel loop; the first slices are serial, and the record names the slice that adds the parallel loop and what it waits for.
  • Interfaces for what comes next: the hooks Dual (#83), std/gpu (#85) and reverse mode (#86) need from a tensor (storage access, shape type, how a kernel receives a tensor), stated as constraints, without designing those packages.
  • Cost: a table of the benchmarks that bound each slice, with the baseline being a hand-written loop over a Buffer: elementwise add of 1 000 000 Float64, a 256 x 256 matmul, a full sum, the pattern of tools/buffer-cost.sh (fastest of nine runs on the forge CI runner, a measurement and not a gate). The first implementing slice records the numbers.
  • Slices: the record lists them in order, each fitting one PR of one or two Conventional Commits, with its seed impact (pure TorbScript on Buffer, no new native and no syntax: expected none; a slice that needs one falls under the two-commit rule, the seed refresh itself stays with the owner). A slice is filed as its own issue only when it starts.
  • Open design questions are decided by the record (technical choices); one is left open only if it is a public commitment, a cost, a product direction or a breaking change of a published contract.
  • Gates green in both tiers.

Design record point

COMPUTE.md sections 1 (layers 0 and 1), 2 and 3; COLLECTIONS gap 6 and the Buffer row of its table; CONCURRENCY section 6 (windows, "the tensor loop"); ECS "What the design needs from Buffer<Item>" (the in-place promise); LINEAR gaps 8 and 9; RANDOM.md; PERFORMANCE.md for the way a cost is measured.

Seed impact

None for this step (documentation only; no std/, runtime/ or compiler/ file changes, no native, no syntax, no name the compiler looks up). The later slices are expected to be pure TorbScript; the record states for each whether it touches the seed.

Size

One PR, one Conventional Commit (docs(compute): design record for std/tensor on the CPU).

Out of scope

Any change to std/, runtime/ or compiler/; Float16/BFloat16; Dual, std/gpu, std/gradient, std/learning (their own issues, only the interface constraints are recorded); running the benchmarks; the seed.

Dependencies

None hard: #54, #299 and #300 are merged. Read #55 (collections on Buffer, open) for the windows-over-Buffer question and #84 (foreign functions, open) only to note that std/tensor on the CPU needs neither. Part of #82.

## Context Step 1 of #82 (compute: `std/tensor` on the CPU; ROADMAP "Computation and machine learning", COMPUTE.md sections 1 and 2). `Buffer<Item>` is built (#54, #299, #300), but `std/tensor` is not one PR: [COMPUTE.md](https://git.torb.dev/torbscript/language/src/branch/main/docs/design/COMPUTE.md) is a stub that says its own full design "comes before the first of these packages is built". Nothing yet fixes the type (`Tensor<Scalar>` and what its shape and strides are), the scalar bounds (`Numeric`, `Real`, `Plain`), whether a slice is a copy or a view, the broadcasting rule and how a shape mismatch is reported, which operators exist and whether the checker accepts operator traits with a foreign `Other` and `Output` (a tensor times a scalar), how a result is written in place, and what the first parallel loop needs (`windows` still takes an `ArrayList`, not a `Buffer`). That has to be decided in writing before any code. This step is documentation only. ## Goal A design record `docs/design/TENSOR.md` that specifies `std/tensor` on the CPU as pure TorbScript on `Buffer<Item>` and cuts the build into slices, each one PR. ## Acceptance criteria - [ ] `docs/design/TENSOR.md` exists, headed and indexed like the other records (status line "designed, not built", an entry in `docs/design/index.md`); COMPUTE.md section 1 (layer 1) and section 2 and the ROADMAP point "Computation and machine learning" link it; `docs/` regenerates `skills/` (`torb docs skill docs skills`, a tier A gate). - [ ] **Type and layout:** decides the representation of `Tensor<Scalar>` (shape and strides as run-time `Int`s over a `Buffer<Scalar>`, offset, row-major order), the invariants a value keeps, how a zero-sized dimension and a rank 0 tensor behave, and the construction functions of the first slice (`zeros`, `ones`, `filled`, from a flat list plus a shape, from `std/random`). Each is a numbered decision with its reason. - [ ] **Scalars:** states the bounds per operation (arithmetic under `Numeric`, `squareRoot`/`exp`-like members under `Real`, `Plain` where a window is handed to a worker) and what `Float32` lacks (`extend Float32 with Real`, LINEAR gap 8) and `Float16`/`BFloat16` (not built, out of scope). Says whether a `Float32` `Real` is a prerequisite slice, and which scalar the first slices test on (`Float64`, `Int`, `Fixed`). - [ ] **Slices and views:** decides copy against view for `tensor[a..b, c]`, in light of `Buffer.slice` copying (#54) and `Window` (CONCURRENCY section 6) being a borrow for one call; the value-semantics consequence is written out with an example. - [ ] **Broadcasting and errors:** the broadcasting rule (NumPy-style, with the exact shape arithmetic), and how a shape mismatch is reported (panic or `Result`) following PANICS.md and the conventions of `std/linear`. - [ ] **Operations of the first release:** the list with exact signatures: elementwise `add`/`subtract`/`multiply`/`divide` as methods and operators, scalar operands, `reshape`, `transpose`, reductions (`sum`, `min`, `max`, `mean`, optionally along an axis), `dot` and `matmul`. For the operators the record runs a scratch `torb check` probe (shown in the record) for `Tensor * Scalar` and `Scalar * Tensor` under today's checker (COMPUTE.md section 3, "operator traits with a foreign `Other` and `Output`") and records the result; a checker gap becomes its own issue, named in the record, never part of a `std/tensor` slice. - [ ] **In place:** how a result is written without a copy where there is one owner (`var fn`, a pre-sized result `Buffer`, the `Buffer` promise of #54), and how this meets `windows`/`Window` (today over `ArrayList`, to move to `Buffer` with #55) for the data-parallel loop; the first slices are serial, and the record names the slice that adds the parallel loop and what it waits for. - [ ] **Interfaces for what comes next:** the hooks `Dual` (#83), `std/gpu` (#85) and reverse mode (#86) need from a tensor (storage access, shape type, how a kernel receives a tensor), stated as constraints, without designing those packages. - [ ] **Cost:** a table of the benchmarks that bound each slice, with the baseline being a hand-written loop over a `Buffer`: elementwise add of 1 000 000 `Float64`, a 256 x 256 `matmul`, a full `sum`, the pattern of `tools/buffer-cost.sh` (fastest of nine runs on the forge CI runner, a measurement and not a gate). The first implementing slice records the numbers. - [ ] **Slices:** the record lists them in order, each fitting one PR of one or two Conventional Commits, with its seed impact (pure TorbScript on `Buffer`, no new native and no syntax: expected none; a slice that needs one falls under the two-commit rule, the seed refresh itself stays with the owner). A slice is filed as its own issue only when it starts. - [ ] Open design questions are decided by the record (technical choices); one is left open only if it is a public commitment, a cost, a product direction or a breaking change of a published contract. - [ ] Gates green in both tiers. ## Design record point COMPUTE.md sections 1 (layers 0 and 1), 2 and 3; COLLECTIONS gap 6 and the `Buffer` row of its table; CONCURRENCY section 6 (`windows`, "the tensor loop"); ECS "What the design needs from `Buffer<Item>`" (the in-place promise); LINEAR gaps 8 and 9; RANDOM.md; PERFORMANCE.md for the way a cost is measured. ## Seed impact None for this step (documentation only; no `std/`, `runtime/` or `compiler/` file changes, no native, no syntax, no name the compiler looks up). The later slices are expected to be pure TorbScript; the record states for each whether it touches the seed. ## Size One PR, one Conventional Commit (`docs(compute): design record for std/tensor on the CPU`). ## Out of scope Any change to `std/`, `runtime/` or `compiler/`; `Float16`/`BFloat16`; `Dual`, `std/gpu`, `std/gradient`, `std/learning` (their own issues, only the interface constraints are recorded); running the benchmarks; the seed. ## Dependencies None hard: #54, #299 and #300 are merged. Read #55 (collections on `Buffer`, open) for the `windows`-over-`Buffer` question and #84 (foreign functions, open) only to note that `std/tensor` on the CPU needs neither. Part of #82.
Author
Collaborator

[loop:torbscript] decided by the loop: filed as step 1 of epic #82 after a keyword search of open and closed issues (tensor, matrix, buffer, compute, simd: no duplicate). One PR, one commit (docs(compute): design record for std/tensor on the CPU), documentation only, so the seed is not touched. Naming follows the existing records (TENSOR.md beside COMPUTE.md, COLLECTION-STORAGE.md). All design questions listed are technical and are decided by the record itself; no owner question. No unmerged hard dependency (#54, #299, #300 are merged).
next: ready

[loop:torbscript] decided by the loop: filed as step 1 of epic #82 after a keyword search of open and closed issues (tensor, matrix, buffer, compute, simd: no duplicate). One PR, one commit (`docs(compute): design record for std/tensor on the CPU`), documentation only, so the seed is not touched. Naming follows the existing records (`TENSOR.md` beside `COMPUTE.md`, `COLLECTION-STORAGE.md`). All design questions listed are technical and are decided by the record itself; no owner question. No unmerged hard dependency (#54, #299, #300 are merged). next: ready
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261023 until 2026-10-23T22:21Z

[loop:torbscript] claim implement by session:loop-torbscript-20261023 until 2026-10-23T22:21Z
Author
Collaborator

[loop:torbscript] implement: PR #442 opens docs/design/TENSOR.md (designed, not built), indexed and linked from COMPUTE.md and the ROADMAP. Forge CI is green (gates, tier A, bootstrap, pdisc, commit messages). What remains: review and merge by Argus. The probe for Tensor * Scalar is recorded; the checker gaps are filed as #440 (scalar * tensor lowers as the builtin product) and #441 (Multiply<Scalar> and Multiply overlap on one generic type). The build slices (section 11) are filed just in time by the loop.

[loop:torbscript] implement: PR https://git.torb.dev/torbscript/language/pulls/442 opens `docs/design/TENSOR.md` (designed, not built), indexed and linked from COMPUTE.md and the ROADMAP. Forge CI is green (gates, tier A, bootstrap, pdisc, commit messages). What remains: review and merge by Argus. The probe for `Tensor * Scalar` is recorded; the checker gaps are filed as #440 (`scalar * tensor` lowers as the builtin product) and #441 (`Multiply<Scalar>` and `Multiply` overlap on one generic type). The build slices (section 11) are filed just in time by the loop.
argus closed this issue 2026-10-23 16:40:23 +00:00
Sign in to join this conversation.
No labels
loop
ready
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
torbscript/language#438
No description provided.