Commit Graph
10 Commits
Author SHA1 Message Date
osobhandClaude Sonnet 5 5155c081ca feat(mamba): GPU-accelerated backward pass (backward_cuda)
GPU Tests / Check GPU Availability (push) Successful in 0s
GPU Tests / CUDA Tests (11.8) (push) Skipped
GPU Tests / CUDA Tests (12.1) (push) Skipped
GPU Tests / Metal Tests (push) Skipped
CI / Clippy Check (push) Failing after 7s
CI / Build (ubuntu-latest) (push) Failing after 8s
Documentation / Build User Guide (push) Successful in 9s
CI / Format Check (push) Failing after 15s
CI / Build (macos-latest) (push) Failing after 20s
CI / Test (macos-latest) (push) Skipped
CI / Test (ubuntu-latest) (push) Skipped
CI / Python Bindings (maturin) (macos-latest) (push) Skipped
CI / Python Bindings (maturin) (ubuntu-latest) (push) Skipped
CI / WASM Build + Size Check (push) Skipped
CI / Distributed Training Tests (push) Skipped
CI / Build CPU-Only (Explicit) (push) Failing after 1m48s
CI / CI Success (push) Failing after 1s
Documentation / Build API Documentation (push) Failing after 2m29s
Performance Benchmarks / Run Benchmarks (push) Successful in 5m56s
MambaBlock::forward already had a working, tested CUDA dispatch
(forward_cuda: cuBLAS matmuls for projections, CPU for the scan).
backward() had none — it silently ran entirely CPU-serial on GPU
tensors via to_vec()/from_vec() D2H/H2D round-trips. This adds the
missing acceleration, mirroring forward_cuda's hybrid split: the
four large projection-parameter gradients (in_proj, x_proj, dt_proj,
out_proj) now go through batched GPU matmuls; the inherently
sequential scan reverse-pass and small per-channel grads stay CPU.

Extracted CpuWeights::pull and recompute_forward_cpu out of the old
inline per-batch forward-recompute block inside backward() (pure
refactor, gradient-checked unchanged by real_selective_scan.rs's
existing 6 tests) so CPU backward and the new CUDA backward share
identical forward math and can never numerically diverge on it.

New CUDA-vs-CPU gradient-check test (mamba_cuda_backward_matches_cpu,
#[ignore]-gated, GPU-only) caught a real bug during development:
Tensor::contiguous() is a no-op stub in this rtx-tensor version, and
cuda_matmul reads raw GPU storage by shape.dims() ignoring
strides/offset, so .transpose(..).matmul(..) on a GPU tensor silently
computed garbage (80-200x relative error on 3 of 4 accelerated
gradients). Fixed by building already-transposed [dim, b*l] buffers
on CPU before upload instead of transposing GPU-side. All 9 gradients
now match CPU backward within ~2.2e-5 max relative error (tolerance
1e-4).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-10 07:11:20 -07:00
Omar SobhandClaude Sonnet 4.6 a08adfbf57 fix(gaps): G4 — re-enable all rtx-transformers Phase 2/3 modules (222 compile errors fixed)
Uncommented all deferred modules in lib.rs and fixed API drift across ~60 files in
9 module groups: continual, curriculum, meta, modular, neural_ode, graph, kan,
perceiver, distributed/pipeline_parallelism.

Common patterns fixed across modules:
- Tensor::randn/zeros/ones([a,b]) → (&[a,b], device)? (slice + Result)
- Result<T, TensorError> → .map_err(Into::into)? in TransformerError contexts
- Device by value → &device references
- &Tensor where Tensor expected → .clone()
- tensor.relu()/tanh()/sigmoid() as methods not ops functions
- Tensor arithmetic returning Result: (a + b)? → (a.clone() + b)?
- shape literals → shape.dims() for Shape type
- sum(n) → sum(Some(n)), mean(None) → mean(&[], false)
- i64 indices → usize where required
- backward(x) → backward(x, None)
- Borrow conflicts on self.field resolved by extracting to locals before mut borrow
- BatchingStats private fields → pub(crate)
- TransformerError::Serialization → ::SerializationError
- Add scalar to tensor: (t + 0.1)? → t.add_scalar(0.1)?

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-06-26 16:08:02 +00:00
Omar SobhandClaude Sonnet 4.6 470fe07144 D350: GPU backbone training benchmark (RTX 5060 Ti)
Adds d350_gpu_backbone_training — 200-step Adam loop on a 4-regime
corpus that prefers Device::cuda(0) and falls back gracefully to CPU.

Measured numbers:
- CPU (DIM=16):  886 steps/s,  MSE 0.2163 → 0.0024
- GPU (DIM=16):  803 steps/s,  MSE 0.2163 → 0.0024

GPU is marginally slower at DIM=16 because the SSM scan and conv1d
remain on CPU in both paths; cuBLAS only helps the four linear
projections, which are tiny at dim=16.  The GPU advantage emerges at
larger dims (≥256) where the projections dominate. Correctness is
identical on both devices.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-06-23 21:48:04 +00:00
osobhandClaude Opus 4.8 51a9185056 SMT E3a: expose Mamba's recurrent state as a single-step updater (equivalence-pinned)
MambaRecurrence reconstructs MambaBlock's selective-scan as an explicit
step(state, x_t) -> (state', y_t) recurrence over host f32 weights, with the
recurrent state (h, conv-ring) carried between steps. This is the substrate the
SMT memory updater is behaviorally cloned on (E3b) — no BPTT, one step at a time.

Dimensions are derived from the persisted tensor shapes, so mamba.rs is left
untouched (it is near the 1250-LOC cap). silu/softplus are byte-identical copies
of the forward's.

Equivalence pin (the plan's highest-risk item): stepping a window one token at a
time from a zero state reproduces the full-sequence forward EXACTLY —
max_abs_diff = 0.0 (bit-identical), on an active_block with wide Delta so the
scan genuinely drives the output. Plus a fresh-state determinism test.

clippy(-D warnings) + fmt clean on the new module and test.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-16 11:00:12 -07:00
omar sobhandClaude Opus 4.8 bc82745d8f Phase 4a: trained real SSM beats memoryless baseline on a temporal task
Performance Benchmarks / Run Benchmarks (pull_request) Successful in 15m17s
CI / Format Check (pull_request) Failing after 58s
CI / Clippy Check (pull_request) Failing after 5m58s
CI / Build (ubuntu-latest) (pull_request) Failing after 7m29s
CI / Build CPU-Only (Explicit) (pull_request) Failing after 18m58s
Documentation / Build API Documentation (pull_request) Failing after 5m46s
Documentation / Build User Guide (pull_request) Successful in 15s
CI / Build (macos-latest) (pull_request) Has been cancelled
CI / Test (macos-latest) (pull_request) Has been cancelled
CI / Test (ubuntu-latest) (pull_request) Has been cancelled
CI / CI Success (pull_request) Has been cancelled
End-to-end demonstration that training the real selective-scan Mamba buys
a genuine temporal-modeling win — the payoff of M1–M3.

Task: next-token prediction on a multi-regime sequence (x[t] = μ_regime +
noise). Predicting x[t+1] inside a regime requires integrating recent
history to average out the noise — a memoryless model can't.

Result (same sequence, all three):
  persistence (memoryless)  MSE = 0.0895
  untrained Mamba           MSE = 0.2167
  trained Mamba (400 Adam)  MSE = 0.0002

The trained backbone integrates history to de-noise the regime mean,
beating the memoryless persistence baseline by ~450× and improving
~1000× over its untrained self. (Single-sequence fit: demonstrates the
SSM's temporal-modeling capacity, not held-out generalization.)

This replaces the old non-result ("random Mamba 6.5% vs linear 48%") with
a real "trained SSM exploits temporal structure" demonstration.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-03 12:10:19 +00:00
omar sobhandClaude Opus 4.8 639937e9f5 Mamba M3.5: canonical numerically-stable initialization
Plain randn(0,1) init made A=-exp(A_log) and Δ wildly large, overflowing
the real exp(Δ·A) scan to NaN (the stub never cared — it discarded these
weights). new()/new_seeded() now share a build() with canonical S6 init:
  - A_log = ln(1..=d_state) ⇒ A = -(1..=d_state), bounded
  - dt_bias so softplus(dt_bias) ≈ 0.01 (small, stable Δ; near-identity
    scan at init — intentional for gradient flow)
  - D = 1, zero conv bias, projections scaled by 1/√fan_in (capped 0.5)

This fixes the NaN that broke omni-cortex's d231 action-conditioned
predictor training (now green). Seeded determinism preserved.

Tests: active_block helper (Δ overridden to ≈0.69) exercises the
scan-active regime so the liveness check can observe each weight; the
training test asserts a seed-varying backbone weight (conv1d_weight)
moves. All 6 selective-scan tests green incl. the finite-diff grad check.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-03 11:30:33 +00:00
omar sobhandClaude Opus 4.8 33cf9bf731 Mamba M3: trainable — Adam loop reduces loss and moves weights
Demonstrates end-to-end trainability of the real selective scan. A
self-contained Adam loop (the rtx-transformers AdamOptimizer has no
public gradient setter — the spec permits a bespoke loop) fits a teacher
block's output on a fixed input: forward → MSE → analytic backward →
Adam step → rebuild. Over 200 steps the loss drops >50% and the backbone
weight A_log moves, confirming gradients actually train the model (not
just the head). All 6 selective-scan tests green.

The production AdamOptimizer can be wired once it exposes a gradient
setter; the M2 backward already returns grads in its HashMap shape.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-03 11:15:45 +00:00
omar sobhandClaude Opus 4.8 199130fa7d Mamba M2: analytic backward (gradient-checked)
Hand-written VJP of the selective-scan forward (Phase-3 spec §5/§7):
MambaBlock::backward(x, d_out) -> per-parameter gradients keyed by
persistence name, summed over the batch. Differentiates the scan
analytically (reverse-time recurrence over the cached h trajectory)
rather than through the immature rtx-tensor autograd tape.

Covers every parameter: in_proj, conv1d_weight, conv1d_bias, A_log
(via A=-exp(A_log) ⇒ dA_log = dA·A), x_proj, dt_proj, dt_bias, D,
out_proj. Adds stable sigmoid_f32 / silu_grad_f32 helpers.

New test analytic_gradients_match_finite_differences: on a small
well-conditioned instance, ≥30 sampled grad elements across all 9
params match central finite differences within (5e-3 + 5e-2·|fd|).
All 5 selective-scan tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-03 11:13:46 +00:00
omar sobhandClaude Opus 4.8 00cd527ed4 Mamba M1: real selective-scan forward (replaces the passthrough stub)
The MambaBlock forward was a stub — SelectiveScan::forward passed input
through, discretize returned zeros, conv1d was a no-op, and B/C were
randn per call, leaving conv1d_weight/dt_proj/A_log as dead weights with
zero temporal mixing. This implements a genuine S6 selective-scan:

- New params: x_proj [d_inner, dt_rank+2*d_state] (data-dependent dt,B,C),
  dt_bias [d_inner], D [d_inner] (skip). Added to new()/new_seeded() and
  the persistence contract (persistence_tensors/from_persistence_tensors).
- Real forward (CPU f32, looped — backbone is small): in_proj -> causal
  depthwise conv1d -> SiLU -> x_proj->(dt,B,C) -> delta=softplus(dt.dt_proj
  +dt_bias) -> A=-exp(A_log) -> sequential scan h=dA.h+dBu, y=sum C.h + D.u
  -> gate by SiLU(z) -> out_proj. Residual moved OUT (canonical).
  Numerically-stable silu_f32/softplus_f32 helpers.
- The scan runs inline (not via the immature rtx-tensor autograd tape);
  the analytic backward lands in M2 per docs/phase3_real_ssm_spec.md.

New tests/real_selective_scan.rs (4 cases, all green): liveness (each
formerly-dead weight now moves the output), causality (no future
leakage), seeded determinism, and finite/non-constant output.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-03 11:02:46 +00:00
redclawsystems 4d88dc0584 Initial commit 2026-03-04 00:08:42 +00:00