Skip to content

M23: Pendulum + SAC — continuous control (first Box action space)#3

Draft
PieterjanDeClippel wants to merge 1 commit into
masterfrom
m23-pendulum-sac-continuous
Draft

M23: Pendulum + SAC — continuous control (first Box action space)#3
PieterjanDeClippel wants to merge 1 commit into
masterfrom
m23-pendulum-sac-continuous

Conversation

@PieterjanDeClippel

Copy link
Copy Markdown
Member

What

Adds the SDK's continuous-action pillar — every prior agent picks one of N discrete actions; SAC outputs a real-valued torque. Showcased by Pendulum-v1.

Investigated by a 4-agent team (autograd seam, Box/env seam, SAC design, web/gates/docs), then built + measured the same day.

Core (no new backend kernels needed)

  • Tensor.ConcatCols / SliceCols — two small column-wise autograd ops (pure memory layout). Concat feeds the critic its concat(state, action) input (routing the actor's gradient through the action half); Slice splits the policy net's [B,2A] output into the mean / log-σ halves.
  • Nn/Normal — diagonal squashed Gaussian (the continuous mirror of Categorical): reparameterized tanh(mean+σ·ε) sample, log-prob with the tanh change-of-variables correction, Mode=tanh(mean). ε is a detached RandomNormal leaf (stop-gradient for free); log-σ clamped to [-20,2].
  • ContinuousReplayBuffer (+ checkpoint) — float-vector actions, no mask; the discrete ReplayBuffer stays byte-compatible.
  • SacTrainer — squashed-Gaussian actor, twin Q-critics (clipped double-Q), Polyak soft target update (a SoftUpdate helper, not a CopyFrom overload), auto-tuned entropy temperature. ContinuousPolicyAgent serves greedy = tanh(mean) rescaled to the box bounds. Bitwise-resumable SacTrainingState. The off-policy loop mirrors DqnTrainer.

Env

PendulumEnv (IEnvironment<float[],float[]> + IStatefulEnvironment) — faithful Gymnasium Pendulum-v1: obs [cos θ, sin θ, θ̇], action torque ∈ [−2,2] (Box(1)), semi-implicit Euler, pre-update cost, torque clamp, no terminal (truncate@200).

Web (PRD §7.1 principle B)

  • EpisodeStreamer generalized over TAct (MountainCar/Snake pass resetAction: -1).
  • PendulumModelService (SAC), PendulumController (WS /api/pendulum/live, h2 Extended CONNECT).
  • Angular page: <canvas> rod + Watch AI (WebSocket) / Swing it yourself (client TS physics on a JS timer, ←/→ = continuous torque). Nav + home card + route.
  • Seed models/pendulum.sac.ckpt (Git LFS), served actor-only via the existing MlpCheckpoint.

Measured

  • SAC (Hidden [128,128], 30k steps, ~6 min CPU) → greedy eval −149.1 over 100 episodes (random ≈ −1200; past the −200 solved bar and the −150 solve-threshold).
  • Bitwise-resume test green; 270 fast tests pass; both web modes verified live in-browser (Playwright, no console errors).
  • Slow median-of-3-seeds gate (Sac_SolvesPendulum, gate ≥ −250) included, opt-in.

Docs

PRD (algorithm ladder, non-goals clarification, env table, §7.1 interaction table), PLAN (M23 milestone, breadth list, gate-results table, console help), ADDING_A_GAME (continuous-control seam note).

🤖 Generated with Claude Code

…space)

Adds the continuous-action pillar: every prior agent picked one of N discrete
actions; SAC outputs a real-valued torque.

Core (no new backend kernels — the autograd layer was already enough):
- Tensor.ConcatCols / SliceCols: two small column-wise autograd ops (pure layout)
  — Concat feeds the critic concat(s,a) routing the actor grad through the action;
  Slice splits the policy net's [B,2A] output into mean/log-sigma.
- Nn/Normal: diagonal squashed Gaussian (continuous mirror of Categorical):
  reparameterized tanh(mean+sigma*eps) sample, log-prob with tanh Jacobian
  correction, Mode=tanh(mean); eps is a detached RandomNormal leaf; log-sigma
  clamped to [-20,2].
- ContinuousReplayBuffer (+ checkpoint): float-vector actions, no mask; the
  discrete ReplayBuffer stays byte-compatible.
- SacTrainer: squashed-Gaussian actor, twin Q-critics (clipped double-Q), Polyak
  soft target update (SoftUpdate helper, not a CopyFrom overload), auto-tuned
  entropy temperature; ContinuousPolicyAgent serves greedy=tanh(mean) rescaled to
  the box bounds; bitwise-resumable SacTrainingState. Off-policy loop mirrors DqnTrainer.

Env: PendulumEnv (IEnvironment<float[],float[]> + IStatefulEnvironment) — faithful
Gym Pendulum-v1 (semi-implicit Euler, pre-update cost, torque clamp, truncate@200).

Web: EpisodeStreamer generalized over TAct (MountainCar/Snake pass resetAction:-1);
PendulumModelService (SAC), PendulumController (WS /api/pendulum/live, h2 CONNECT),
Angular pendulum page (canvas rod; watch-AI WS + swing-it-yourself client physics
with continuous torque). Seed models/pendulum.sac.ckpt (Git LFS), served actor-only.

Measured: SAC reaches greedy eval -149.1 over 100 episodes (random ~ -1200; past
the -200 solved bar); bitwise-resume green; 270 fast tests pass; both web modes
verified live in-browser.

Docs: PRD (algorithm ladder, non-goals, env table, §7.1 interaction table), PLAN
(M23 milestone, breadth list, gate table, console help), ADDING_A_GAME (continuous note).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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