# v0.1 Implementation Plan This plan tracks the incremental v0.1 implementation. It intentionally contains no placeholder integrations or speculative extension points: every listed component is needed for the acceptance behavior. ## Deliverable 1: immutable graph core Build the six IR node families and scalar-to-constant coercion. Required work: - immutable expression/node representation; - child traversal and DAG sharing; - operator overloads for `+`, `-`, `*`, `/`, and `//`; - unary nodes for `exp`, `log`, `softplus`, and absolute value; - distribution nodes for `Normal`, `Uniform`, and `Bernoulli`; - alias-agnostic structural equality; - cycle validation. Acceptance checks: - constructing expressions performs no sampling; - attempts to mutate nodes fail; - equivalent separately built DAGs compare equal; - shared-node and duplicate-node graphs compare structurally equal when their computation trees are otherwise equivalent; - resolved checkpoints distinguish stochastic aliasing, node entropy, and bound sampling-seed differences. ## Deliverable 2: plate algebra and value alignment Implement named plate inference, contracts, reductions, and the minimal concrete alignment required by the backend boundary. Required work: - `plates` and canonical lexicographic `plate_layout` inference; - `add_plates(*plates, expect=None)`; - `check_plates(*plates)`; - `reduce_plates` plus the six convenience reduction methods; - strictly positive materialization-time plate sizes; - alignment of concrete values by named plate order; - complete output plate declarations on distribution nodes; - deterministic broadcasting through `AddPlates`; - the public plate error hierarchy. Acceptance checks: - duplicate plate introduction fails; - an incorrect `expect` fails before graph transformation; - `check_plates` returns the same object; - reducing a missing plate fails; - `Normal(row_value, col_value)` aligns to logical `(row, col)`; - distribution output plates create conditionally independent draws; - distribution output plates must contain every parameter plate; - adding a plate to any expression broadcasts its existing value. Concrete-value work: - expose `constant(value, *, plates=(), dtype=None)`; - infer supported Boolean, integer, and floating inputs; - canonicalize storage to `np.bool_`, `np.int64`, or `np.float64`; - transpose declared input axes into canonical plate order; - derive and enforce four-state support after coercion; - reject unsupported kinds and rank/layout mismatches at the boundary. ## Deliverable 3: phase annotation Implement distribution-only sampling phases. Required work: - a context-local current phase; - nested `sampling_phase` restoration; - phase capture by distribution constructors only; - recursive `pending_phases` introspection; - unphased distribution nodes with no phase barrier. Acceptance checks: - deterministic nodes created inside a phase do not acquire a phase; - nested contexts assign the innermost phase and restore the outer phase; - pending phases reflect only reachable, unmaterialized distributions. ## Deliverable 4: graph-aware RNG derivation and NumPy execution Implement deterministic graph-key resolution and concrete NumPy sampling. Required work: - stable seed normalization; - a stochastic-only graph projection; - dependency hashes, direct-consumer hashes, repeated-use multiplicities, and symmetric-node enumeration; - compressed projected edges for repeated stochastic consumption; - optional `rng_label` entropy mixed after the graph hash; - node-entropy resolution coupled to initial materialization; - one run seed selected per materialization invocation; - phase-enabled sampling seeds bound immediately, even while dependencies are blocked; - NumPy sampling for the implemented distributions; - concrete deterministic arithmetic, transforms, and reductions. `Uniform` must support arbitrary symbolic bounds with unit defaults and elementwise `low < high` validation. `Bernoulli` must accept symbolic probabilities, validate `0 <= p <= 1`, and return canonical Boolean values. Acceptance checks: - the same graph and seed reproduce exactly; - different seeds alter pending stochastic nodes; - unrelated graph construction does not perturb a result; - labels never override graph structure or couple distinct nodes; - separately allocated symmetric nodes receive distinct default keys; - aliases of one node share one resolved entropy identity and sampled value. ## Deliverable 5: immutable partial materialization Implement staged graph rewriting and value extraction. Required work: - `materialize(phases=..., seed=..., plate_sizes=...)`; - an opaque, non-composable `SamplingCheckpoint`; - monotonic phase-barrier removal; - dependency-blocked enabled nodes that resume on a later pass; - node-owned exact dependency rewriting through a private reconstruction hook; - distribution-to-constant replacement; - eager constant folding for deterministic downstream nodes; - unchanged subgraph sharing; - checkpoint `materialize` and `realize`; - checkpoint-only `stochastically_equal`; - materialization error reporting. Acceptance checks: - the source graph remains unchanged; - materializing one phase leaves other distributions symbolic; - enabling a downstream phase before its dependency retains the permission; - later enabling the dependency finishes all newly ready enabled work; - two branches share embedded early-phase constants; - later-phase seeds can vary independently; - value extraction fails while stochastic nodes remain. - deterministic raw expressions can realize without a checkpoint; - checkpoint `value()` extracts only an already constant root. ## Deliverable 6: product validation suite and examples Validate the authoring experience rather than only line coverage. Required work: - one small hierarchical model example; - one row/column plate-alignment example; - one fixed-latent/repeated-observation example; - expected-graph fixtures suitable for testing generated code; - error-message snapshots for common plate mistakes; - README examples converted to executable tests. - a public-import-only static typing fixture; - branch coverage of at least 95%; - a fixed v0.1 graph-digest fixture and cross-`PYTHONHASHSEED` subprocess check; - wheel validation proving that `py.typed` and inline annotations ship without `.pyi` files. The release candidate is acceptable only if each example is shorter or materially more explicit about stochastic structure than its equivalent imperative NumPy program. ## Test matrix | Concern | Required cases | | --- | --- | | Equality | equal structures, different args, allocation-order independence, shared vs duplicated stochastic node | | Plates | add, duplicate add, exact check, failed expectation, ordered union, each reduction | | Alignment | scalar/scalar, row/scalar, row/col, reduced row/col | | Phases | named, unphased, nested context, blocked enabled phase, enable all | | RNG | same seed, different seed, unrelated construction, label mixing, repeated projected edge, symmetric enumeration, blocked-node seed persistence | | Materialization | none eligible, some eligible, all eligible, immutable source, branch reuse | | Errors | invalid plate, missing size, numeric sampling failure, premature value extraction | ## Release deliverables The v0.1 release must contain: - the typed public runtime interfaces; - inline annotations and `py.typed`, with no shipped stub files; - NumPy concrete propagation and distribution sampling; - full implementations of the six node families; - plate-aware deterministic evaluation and the six reductions; - phase contexts and partial materialization; - structural and checkpoint stochastic equality; - tests for the matrix above; - the realistic end-to-end examples; - package metadata and generated API documentation. - Python 3.10/latest-supported CI for Ruff, ty, tests, coverage, wheel build, wheel inspection, and installed-package smoke tests. ## Deferred work Do not begin the following during v0.1 unless the core acceptance work proves impossible without it: - custom extension registries; - JAX or PyTorch support; - serialization or visualization; - optimizers, compilers, tracing, or autodiff; - inference or observations; - custom reduction callables; - rich tensor indexing or dynamic plates; - public graph visitor or rewrite APIs. ## Locked implementation decisions - optional `rng_label` supplements graph-derived entropy and never overrides it; - exact concrete values use NumPy arrays; - `PlateLayout` is canonicalized lexicographically; - unphased distributions remain expressible and have no phase barrier; - a public backend protocol is deferred; - distribution constructors retain their natural parameters (for example, `Normal(mu, sigma)`) rather than lowering to standardized distributions; - public generic reductions use the immutable singleton objects in `stoch_ir.reductions`; - concrete NumPy storage always matches authoritative `ValueMeta.dtype`; - custom-node execution support and public graph rewrite APIs are deferred.