# Probabilistic-on-SX: weighted nondeterminism + traces + inference Programs declare distributions; the runtime infers. The most orthogonal addition to the set — every existing guest treats execution as deterministic-or-resumable. Probabilistic programming requires *weighted, traceable* executions with explicit posterior-inference machinery on top. **Anglican** (Wood et al.) or **Church** (Goodman et al.) is the closest reference; we'll target a Church-flavoured core. **The chisel:** *trace*. What does it mean to record an execution? What's a probability weight? How do branches in `conde`-like nondeterminism differ from `sample`/`observe` choices? The substrate has multi-shot continuations (a prerequisite for any decent inference algorithm) but doesn't articulate weights or traces — implementing a probabilistic language forces it to. **What this exposes about the substrate:** - Whether `cek-resume` can be invoked many times per `perform` with different values (multi-shot we know works; *parameterised* multi-shot is the question). - Whether traces — sequences of (random-variable-id, sampled-value, log-weight) — fit naturally in the value space. - Whether the substrate can support efficient *trace replay* (start a fresh execution but force certain random choices to specific values). - Whether handler/effect machinery (lib/guest/effects/ when it exists) can host inference-as-handler. **End-state goal:** **Anglican-style probabilistic Scheme** — `sample`, `observe`, basic distribution library, importance sampling, MCMC (Metropolis-Hastings), and a path to variational inference. Programs are distributions; `query expr` returns a distribution over outcomes. ## Ground rules - Scope: `lib/probabilistic/**` and `plans/probabilistic-on-sx.md` only. Substrate gaps → `sx-improvements.md`. - Consumes from `lib/guest/`: `core/lex`, `core/pratt`, `core/ast`, `core/match`. Possibly `effects/` once that sub-layer exists (inference algorithms are naturally handlers over `sample`/`observe`). - **May propose** `lib/guest/probabilistic/` sub-layer — trace-recording infrastructure, weight-algebra primitives (log-domain arithmetic), inference combinators, distribution constructors. Second consumer would be a future Pyro-style language or a Bayesian DSL. - Branch: `loops/probabilistic`. Standard worktree pattern. ## Architecture sketch ``` Probabilistic source text (Church-flavoured: scheme + sample/observe) │ ▼ lib/probabilistic/parser.sx — s-expression reader │ ▼ lib/probabilistic/eval.sx — pure evaluator (deterministic except at sample/observe) │ sample/observe are perform-shaped: suspend execution, │ let inference algorithm decide what to do ▼ lib/probabilistic/inference/ — handlers that interpret sample/observe: │ importance.sx importance sampling, likelihood-weighting │ mh.sx Metropolis-Hastings (proposal kernels) │ variational.sx mean-field VI │ ▼ lib/probabilistic/distributions.sx — uniform, normal, gamma, beta, dirichlet, mixture, conditional, etc. ``` ## Semantic mappings | Probabilistic construct | SX mapping | |------------------------|-----------| | `(sample (uniform 0 1))` | `(perform (:sample (uniform 0 1)))` — inference handler decides actual value | | `(observe (normal 0 1) 0.5)` | `(perform (:observe (normal 0 1) 0.5))` — adds log-prob to weight | | `(query body)` | run `body` under inference handler; return weighted samples | | `(uniform a b)` | distribution value: `{:type :dist :family :uniform :params (a b)}` | | `(score lpdf x)` | accumulate log-prob; equivalent to observe | | Trace | `(list (:choice id sampled-value log-weight) ...)` — first-class value | The key trick: `sample` and `observe` aren't primitives — they're effect requests. The inference algorithm is a handler that interprets them. Importance sampling samples each `sample` from the prior and accumulates weights from each `observe`. MH proposes changes to the trace and accepts/rejects. ## Roadmap ### Phase 1 — Parser + deterministic core - [ ] Scheme-flavoured parser (s-expressions, `let`, `lambda`, `if`, arithmetic, lists). - [ ] Deterministic evaluator running on SX CEK. - [ ] Tests: standard Scheme programs run. ### Phase 2 — `sample` / `observe` as effects - [ ] `sample dist` → `perform :sample`. - [ ] `observe dist value` → `perform :observe`. - [ ] Default handler: forward sampling, no inference (just produce a draw). - [ ] Tests: simple stochastic programs (coin flip, sum-of-dice) produce different results across runs. ### Phase 3 — Distribution library - [ ] `uniform`, `normal`, `gamma`, `beta`, `bernoulli`, `categorical`, `dirichlet`, `poisson`. - [ ] Each carries `(sample-fn, log-prob-fn)`. - [ ] Tests: log-prob of known density values matches reference. ### Phase 4 — Trace recording + replay - [ ] Tracing handler: every `sample` records `{:id :value :log-weight}` in a trace value. - [ ] Replay handler: given a trace, force `sample` to return the recorded value when called with the same `id`. - [ ] Tests: record a trace, replay it, get identical outputs. ### Phase 5 — Importance sampling - [ ] `importance-sample n query` runs `query` `n` times under sampling handler. - [ ] Each run accumulates log-weights from `observe` calls. - [ ] Returns weighted samples. - [ ] Tests: posterior over a coin's bias given Bernoulli observations. ### Phase 6 — Metropolis-Hastings - [ ] `mh n query` runs MH for `n` steps. - [ ] Each step: pick a random choice in the current trace, propose a new value, accept/reject by Hastings ratio. - [ ] Multi-shot continuation usage: re-execute from the proposed-changed point onward. - [ ] Tests: gaussian regression, change-point detection, mixture clustering. ### Phase 7 — Mean-field variational inference - [ ] Approximate posterior as product of independent simple distributions. - [ ] Optimise ELBO via gradient ascent. - [ ] Requires automatic differentiation — `lib/probabilistic/autodiff.sx` (forward-mode minimum). - [ ] Tests: normal-normal model, ELBO converges to known truth. ### Phase 8 — Standard library + idioms - [ ] Mixture models, Gaussian processes, hidden Markov models, change-point models. - [ ] Tests: each as an end-to-end test that should give roughly known posteriors. ### Phase 9 — Propose `lib/guest/probabilistic/` - [ ] Identify reusable trace + weight infrastructure (log-domain arithmetic, ESS, sample weighting). - [ ] Wait for a second consumer before extracting. ## lib/guest feedback loop **Consumes:** `core/lex`, `core/pratt`, `core/ast`, `core/match`. Future: `effects/` for handler-based inference. **Stresses substrate:** parameterised multi-shot continuations (each MH step replays from a chosen point with a new value); efficient trace storage; whether `perform`/`cek-resume` survives nesting (handler within handler — inference inside another inference). **May propose:** `lib/guest/probabilistic/` — trace primitives, weight algebra (log-sum-exp etc.), distribution interfaces. **What it teaches:** whether SX's effect/continuation machinery is up to *real* multi-shot work, not just textbook examples. Inference algorithms call `cek-resume` thousands of times per query; if the substrate has hidden quadratic costs in continuation manipulation, this surfaces them. ## References - Goodman, Mansinghka, Roy, Bonawitz, Tenenbaum, "Church: a language for generative models" (UAI 2008). - Wood, van de Meent, Mansinghka, "A new approach to probabilistic programming inference" (AISTATS 2014) — Anglican. - van de Meent, Paige, Yang, Wood, "An Introduction to Probabilistic Programming" (arXiv 2018). - Bingham et al., "Pyro: Deep Universal Probabilistic Programming" (JMLR 2019). ## Progress log _(awaiting Phase 1 — depends on multi-shot continuation stability)_ ## Blockers _(none yet — main concern is hidden substrate costs in continuation manipulation)_