Conversation
A single natively-constrained square matrix (`cholesky_factor_corr` / `cholesky_factor_cov`) is sized by one dim (`<ct>[K]`, since `r_ndim(square_matrix) == 1`) but is logically K-by-K. Scalar element access `L[i, j]` had no getindex tracetype entry, so the l_ndim-peeling rule (functions.jl, `l_ndim > 0` branch) read the first index as an array-prefix selector and the result degraded to `anything` — using the element then threw "tracetype not defined" before any Stan was emitted. Add `(cholesky_factor_corr[m], int, int) => real` and the `_cov` twin to the `@defsig` getindex table, so a scalar element resolves to `real` exactly as the plain `matrix[K, K]` control does. The `[m, n]` plate entries below (array-of-cells, outer axis indexed first) are unchanged. closes 2026-07-28T09-13-00-729-1py3n0h Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PdbAKGKfkjzBJVDBVi8HSP
A plate cell constraint (`lower`/`upper`/`offset`/`multiplier`) that reaches
the per-cell position through DATA — e.g. `c ~ normal(m0, 1.; multiplier =
exp(l))` where `l` slices a data vector — used to be a loud refusal: the
promoted declaration is emitted outside the loop, so `exp(l)` would render
with the position unbound. D2a refused it rather than silently drop it (a
different, undiagnosed posterior).
D2b hoists it instead. `_plate_promoted_constraints` now classifies each
constraint into kept (index-independent), hoistable (index-dependent,
data-qualified), or refused (index-dependent, parameter-qualified — Stan
requires a promoted constraint to be data-computable; this is D3's rule on
the plate path, which returns early before the ordinary scope check).
`_plate_hoist_cell_constraints!` materialises each hoistable constraint over
the outer axis into a `transformed data` vector (mirroring the ragged
flat-memory emission — a bare carrier decl plus a data `for` loop, re-tracing
the constraint's raw expression so the fill loop rebinds the plate index),
and the promoted `vector[outer]` declaration references that carrier by name:
transformed data { vector[G] s_c__pl_multiplier_1;
for (i in 1:G) s_c__pl_multiplier_1[i] = exp(lamv[i]); }
parameters { vector<multiplier=s_c__pl_multiplier_1>[G] s_c; }
Scope (MVP): one outer axis, a scalar cell (so a `vector[outer]` carrier
aligns elementwise), a top-level plate, and a real-valued constraint. The
submodel, multi-axis, non-scalar, and integer-carrier cases stay a loud
error. Unblocks BRM's per-cell lambda through plate.
Full plate regression green (427/427, 19 items); the anchor item now asserts
the hoist emission + stanc-compiles and keeps a parameter-qualified refusal.
closes 2026-07-28T09-13-00-724-1bhesur
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PdbAKGKfkjzBJVDBVi8HSP
`Base.merge(base, args...)` previously accepted only statement-AST overlays
(`:(x ~ …)`, `quote … end`) and `NamedTuple` fixed-value bindings. A whole
`SlicModel` overlay was not accepted — you could merge a model with an expr,
but not with another model.
Accept a `SlicModel` argument: its body statements splice like any other
override/append overlay (same-LHS replaces, the rest append), and its bound
data merges into the result. Its data does NOT go through the `fixed` path —
a `fixed` name has its defining statement removed (pinning a parameter to a
value), but a merged model's data names are OBSERVATIONS whose likelihood
statements must survive. `docstring` (model metadata carried in `data`) is
dropped so the base keeps its own. Model, statement, and NamedTuple overlays
compose in one call; later overlays win on a same-LHS collision.
merge(m1, m2) # combine two models
merge(m1, m2, :(mu ~ prior())) # model overlay + statement overlay
Both inputs' parameters, data, and likelihoods survive; the merged model
compiles under stanc. Inputs are unmutated (merge is functional).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PdbAKGKfkjzBJVDBVi8HSP
KB-Committer-Id: StanBlocks
Under CV a held-out size symbol (`J = maximum(subject)` with `subject`
marked `maybecv`) must relocate the parameter it sizes to generated
quantities and re-draw it from its hyperprior. The bare-LHS / `n=` path
already did this because it derives the declared type from `autotype(rhs)`,
whose `stan_size` carries the size taint. The typed-LHS sampling path
(`forward!(::SamplingExpr{<:DeclExpr})`) computed cv from the DISTRIBUTION
ARGS only (`mu, tau`), never the declared size, so
`alpha :: vector[J] ~ normal(mu, tau)` stayed a fitted `parameter` while
the documented-equivalent `alpha ~ normal(mu, tau; n=J)` correctly moved
to generated quantities — a prior draw dressed up as a fit.
Fold the declared type's own taint into the qual decision:
`cv_args = any(stan.cv, args_resolved) || stan.cv(base_lhs_type)`, the same
`any(cv, stan_size)` the autotype path uses. Under no CV this is always
false, so un-tainted models are byte-identical. The two spellings now emit
identical Stan under CV.
Regression testitem: `slic: cv-tainted typed-LHS size re-draws the
parameter in gq` (tags :slic, :stanc, :regression).
Snag: typed-lhs-size-c-8a7fbfb2 (reported by StanBlocks:stancon2026)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HnS3xhk4W8ymVSpaj4mcQN
New conceptual page explaining how the two static passes (likelihood reachability + cross-validation taint) make the SAME `@slic` source become a prior-predictive simulator, a posterior fit, or a cross-validation / population-prediction model purely from what data is bound and how. Shows the actual emitted Stan for a hierarchical example under all three bindings (verified via a roles probe), ties the roles to the descriptor `held_out`/`operations` set, distinguishes kwarg-binding from `Base.merge`, and carries the typed-LHS-size cv-taint caveat (snag typed-lhs-size-c-8a7fbfb2). Registered in the docs nav after "Authoring support". Docs build green (warnonly=false). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PdbAKGKfkjzBJVDBVi8HSP
KB-Committer-Id: StanBlocks
KB-Committer-Id: StanBlocks:snag.typed-lhs-size-c-8a7fbfb2
…valent snag typed-lhs-size-c-8a7fbfb2 fixed the divergence (fix 4da0903, landed on devibe e62db21): `alpha :: vector[J] ~ normal(mu, tau)` now carries cv taint through its declared size exactly like `alpha ~ normal(mu, tau; n=J)`. Replace the "does not yet carry" caveat in activity-analysis.md with an affirmative equivalence note. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PdbAKGKfkjzBJVDBVi8HSP
KB-Committer-Id: StanBlocks
…rcles, fix public title QRs Session's deck edits, batched for the first publish since 515ba86: - Title-slide QR codes: inline the 3 QR SVGs as data: URIs (qr-title.html). The public docs/make.jl build renders WITHOUT embed-resources and its resource-copy step does not scan include-after-body HTML, so the SVGs were never copied to gh-pages -> broken QRs on GitHub Pages (the summary-slide QR is a markdown image and was always fine). Inlining removes the copy dependency entirely. - Commit three-modal.html (side-by-side generated-Stan modal); it is in include-after-body but had never been tracked, so the public build lacked it. - three-analyses slide: alpha :: vector[J] ~ normal(mu, tau) (typed-LHS). - Add .fragment click-reveals at block granularity across 9 content slides; "One model declaration" infer-cards reveal one-by-one. - BRM line: "largest current public (but unregistered) consumer ... currently lowers into Stan via StanBlocks.jl, but will target Julia via Turing.jl too." - Remove the "Backup - exact core emission" slide. - centered_sum @deffun: drop the rhs type annotations (byte-identical Stan). - Add "Backup - higher-order functions specialize at compile time" slide. - Drop the "Stabilise the small public authoring contract" priorities bullet. - Activity-analysis line: "activity analysis and metadata decide, per statement, where each sampling statement ends up." - Contributor circles use one Julia colour each (stancon.scss); Niko keeps Stan's maroon. - Remove both mobile @media fallbacks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ljhto3nbBhEiTewS1MZbnM
KB-Committer-Id: StanBlocks:stancon2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.