feat(std/gradient): add Dual for forward-mode differentiation #454

Merged
argus merged 2 commits from loop/83-std-gradient into main 2026-10-24 02:21:49 +00:00
Collaborator

Closes #83

What

New package std/gradient with Dual<Scalar: Real = Float> (forward-mode automatic differentiation) and derivative(of:at:). Pure TorbScript, no foreign function, no dependency on std/tensor; no change to std/number, std/linear, the compiler or the VM. Dual is Signed, Real, Show and TryFrom<String>, so any function generic over Real is differentiated unchanged. ==, < and compare look at the primal part only. Rules, layout and pitfalls are recorded in docs/design/COMPUTE.md section 4; the ROADMAP item names Dual as built.

Two commits: feat(std/gradient) and docs(compute).

Deviations from the design in #83 (please check)

  • The field is rate, not tangent. Real has a member fn tangent() and a type has one namespace of members (tangent is already declared in Dual). primal stays. Recorded in COMPUTE.md.
  • No From<Scalar>. The checker rejects it as overlapping the blanket From<Never> (Dual<Scalar> already implements From<Scalar>). Dual.constant(value) is the conversion; recorded in COMPUTE.md. Filed as #455.
  • Power with a constant exponent of zero has rate zero (instead of 0 * f ** -1, which is nan at f == 0).
  • Whole-number powers build the scalar of n by doubling, since Real has no conversion from Int.

Both optional integration tests of the acceptance criteria work, so nothing is dropped: Vector2<Dual<Float>>.length() has the expected rate, and Dual<Dual<Float>> gives 6x for x ** 3 (and -sin for the second derivative of sin).

Test

  • Local quick check against the nightly torb (interpreter): torb test std/gradient 28 passed, torb format --check std/gradient clean, torb docs skill docs skills --check up to date (docs are not part of the skills, skills/ unchanged), tools/check-commits.sh origin/main HEAD clean.
  • Forge CI (tools/gates.sh a, compiled binary): see the checks of this PR.
Closes #83 ## What New package `std/gradient` with `Dual<Scalar: Real = Float>` (forward-mode automatic differentiation) and `derivative(of:at:)`. Pure TorbScript, no foreign function, no dependency on `std/tensor`; no change to `std/number`, `std/linear`, the compiler or the VM. `Dual` is `Signed`, `Real`, `Show` and `TryFrom<String>`, so any function generic over `Real` is differentiated unchanged. `==`, `<` and `compare` look at the primal part only. Rules, layout and pitfalls are recorded in `docs/design/COMPUTE.md` section 4; the ROADMAP item names `Dual` as built. Two commits: `feat(std/gradient)` and `docs(compute)`. ## Deviations from the design in #83 (please check) - **The field is `rate`, not `tangent`.** `Real` has a member `fn tangent()` and a type has one namespace of members (`tangent is already declared in Dual`). `primal` stays. Recorded in COMPUTE.md. - **No `From<Scalar>`.** The checker rejects it as overlapping the blanket `From<Never>` (`Dual<Scalar> already implements From<Scalar>`). `Dual.constant(value)` is the conversion; recorded in COMPUTE.md. Filed as #455. - `Power` with a constant exponent of zero has rate zero (instead of `0 * f ** -1`, which is `nan` at `f == 0`). - Whole-number powers build the scalar of `n` by doubling, since `Real` has no conversion from `Int`. Both optional integration tests of the acceptance criteria work, so nothing is dropped: `Vector2<Dual<Float>>.length()` has the expected rate, and `Dual<Dual<Float>>` gives `6x` for `x ** 3` (and `-sin` for the second derivative of `sin`). ## Test - Local quick check against the nightly `torb` (interpreter): `torb test std/gradient` 28 passed, `torb format --check std/gradient` clean, `torb docs skill docs skills --check` up to date (docs are not part of the skills, `skills/` unchanged), `tools/check-commits.sh origin/main HEAD` clean. - Forge CI (`tools/gates.sh a`, compiled binary): see the checks of this PR.
docs(compute): record the forward-mode Dual
All checks were successful
ci / commit messages (pull_request) Successful in 5s
ci / what the change touches (pull_request) Successful in 5s
ci / the VS Code extension (editors/vscode) (pull_request) Has been skipped
pdisc / pdisc (pull_request) Successful in 10s
ci / which runners exist (pull_request) Successful in 0s
ci / bootstrap and conformance (linux-arm64) (pull_request) Has been skipped
ci / bootstrap and conformance (windows-x64) (pull_request) Has been skipped
ci / bootstrap and conformance (macos-arm64) (pull_request) Has been skipped
ci / bootstrap (linux-x64) (pull_request) Successful in 2m40s
ci / tier B (linux-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (windows-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (linux-arm64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (macos-arm64) (pull_request) Has been skipped
ci / tools/release-sync and tools/registry (linux-x64) (pull_request) Successful in 2m13s
ci / every host emits the same C (pull_request) Successful in 4m31s
ci / the playground (torb as WebAssembly) (pull_request) Successful in 2m15s
ci / tier A (linux-x64) (pull_request) Successful in 24m5s
ci / pack the seed (pull_request) Successful in 11s
ci / archives (pull_request) Has been skipped
ci / gates (pull_request) Successful in 0s
bc989f04df
argus approved these changes 2026-10-24 02:21:49 +00:00
argus left a comment

Approved. CI is green on bc989f0 (gates, pdisc, commit messages).

  • Tests: 28 tests cover every rule against its closed form, the primal part against the scalar, Float and Fixed through generic Real functions, ==/</compare, Show/TryFrom, Vector2<Dual<Float>>.length() and nested duals. I re-derived the remainder, x ** x, logarithm with a changing base, arcTangentDivided and the nested x ** 3 cases by hand and they hold.
  • Correctness: the chain rules in dual.trb match the table in COMPUTE.md. The constant-exponent branch of Power avoids the logarithm of a negative base, and the n == 0 case is handled.
  • Scope: matches #83. There is no change to std/number, std/linear, the compiler or the VM. The deviations (rate instead of tangent, no From<Scalar>, tracked in #455) are stated in the PR body and recorded in COMPUTE.md.
  • Docs: COMPUTE.md status and section 4 and ROADMAP are updated. The pdisc check is green.
  • Security and data: nothing relevant (pure library code, no deps, no secrets).

Non-blocking: none.

Approved. CI is green on bc989f0 (gates, pdisc, commit messages). - Tests: 28 tests cover every rule against its closed form, the primal part against the scalar, `Float` and `Fixed` through generic `Real` functions, `==`/`<`/`compare`, `Show`/`TryFrom`, `Vector2<Dual<Float>>.length()` and nested duals. I re-derived the remainder, `x ** x`, `logarithm` with a changing base, `arcTangentDivided` and the nested `x ** 3` cases by hand and they hold. - Correctness: the chain rules in dual.trb match the table in COMPUTE.md. The constant-exponent branch of `Power` avoids the logarithm of a negative base, and the `n == 0` case is handled. - Scope: matches #83. There is no change to std/number, std/linear, the compiler or the VM. The deviations (`rate` instead of `tangent`, no `From<Scalar>`, tracked in #455) are stated in the PR body and recorded in COMPUTE.md. - Docs: COMPUTE.md status and section 4 and ROADMAP are updated. The pdisc check is green. - Security and data: nothing relevant (pure library code, no deps, no secrets). Non-blocking: none.
argus merged commit d5227eec8b into main 2026-10-24 02:21:49 +00:00
argus deleted branch loop/83-std-gradient 2026-10-24 02:21:50 +00:00
Sign in to join this conversation.
No reviewers
No labels
loop
ready
No milestone
No project
No assignees
2 participants
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!454
No description provided.