# Curriculum and ctx

A curriculum moves the training loader between its stages based on what an
evaluation pass just observed. `ctx` is the plist of run progress that
schedules, optimizers and the curriculum read.

```lisp
(defun curriculum (stage ctx observations)
  (where (< stage 1)
         (where (< (getf observations "eval/loss") 3.3) (+ stage 1) stage)
         stage))
```

## curriculum

- Receives the current stage as an `i64` scalar tensor, `ctx`, and the
  observations of the pass that just finished, a plist keyed by metric name
  as a string.
- Returns the new stage: any stage of the training loader, forwards or
  backwards, or the current one to stay. Stages are declared with
  `:stages` on the loader; see [loaders](https://sexpgpu.041.io/docs/data.md).
- It is a graph, so branching is `where`, not `if`. See
  [truth and staging](https://sexpgpu.041.io/docs/control.md#truth-and-staging).
- Runs after every pass, including those at step 0, never after the last
  step. Two passes due at one step run it twice, each time on the
  observations of the pass that just ran.
- Diagnostics are never offered to it; they do not exist on unsampled
  steps. A `metric` in it is not read.
- A curriculum with no pass is `E-CONTRACT-014`.

A stage change is the annotation `stage <old> -> <new>`, every metric
carries the `stage` it was measured in, and `curriculum/stage` is emitted
with every evaluation. See [events](https://sexpgpu.041.io/docs/events.md#annotations).

## ctx

A plist of scalar tensors, read with `getf`:

| key | dtype | meaning |
|---|---|---|
| `:step` | `i64` | optimizer steps completed |
| `:steps` | `i64` | the run's total, `defrun :steps` |
| `:microbatch` | `i64` | the microbatch index inside the step |
| `:records` | `i64` | loader records consumed |
| `:progress` | `f32` | `:step` divided by `:steps` |
| `:stage` | `i64` | the curriculum stage |
| `:stage-steps` | `i64` | steps since the stage began |
| `:stage-records` | `i64` | records since the stage began |
| `:<counter>` | `i64` | one per declared [counter](https://sexpgpu.041.io/docs/data.md#counters), for the whole run |
| `:stage-<counter>` | `i64` | the same, since the stage began |

The `stage-` entries reset to zero whenever the stage changes. Because the
values are tensors, arithmetic on them builds graph nodes: cast before
mixing, `(cast (+ (getf ctx :step) 1) :f32)`.

Related: [evaluation passes](https://sexpgpu.041.io/docs/evaluation.md), [schedules](https://sexpgpu.041.io/docs/optimizers.md#schedules).

---

SexpGPU documentation. Every page: https://sexpgpu.041.io/llms.txt
