API Reference
Matryoshka.AbstractComponent Type
AbstractComponentAbstract supertype for predictor-term components (InterceptComponent, FixedEffectsComponent, RandomInterceptComponent). A component holds the data-contact artifacts for one formula term (design matrix, group indices, factor levels) and implements four functions: compprefix, submodel, priorslots, rebuild. New term types (e.g. Gaussian-process or smooth terms) are added by subtyping AbstractComponent, implementing these four, and adding a lower dispatch (see src/lowering.jl) that turns a matching formula term into the component. Vector-valued parameters sample as plain (unlabeled) arrays; new term types that introduce one should keep its label vector in the struct (see FixedEffectsComponent.names/RandomInterceptComponent.levels) so positional entries can be mapped onto labels by rekey at chain-build time.
Example
using Matryoshka
using Matryoshka: InterceptComponent
InterceptComponent(4) isa AbstractComponentMatryoshka.BernoulliFamily Type
BernoulliFamily <: FamilyBernoulli response family for binary outcomes: mu (success probability) is supplied by the predictor formula on the logit scale. obsmodel applies BernoulliLogit directly to the link-scale linear predictor rather than inverse-linking to a probability first, so extreme eta never saturates p == 1.0 under autodiff.
Example
using Matryoshka
lik = @likelihood Bernoulli y ~ x
lik.family isa BernoulliFamilyMatryoshka.Family Type
FamilyAbstract supertype for response families (NormalFamily, BernoulliFamily, PoissonFamily). A family declares its distribution parameters, per-parameter links, default priors, and observation submodel via parameters, links, default_priors, and obsmodel. New families are added by subtyping Family and implementing these four functions, plus a to_family dispatch (from the Distributions.jl type name used as the first argument to @likelihood).
Example
using Matryoshka
NormalFamily() isa FamilyMatryoshka.FixedEffect Type
FixedEffect([coef::Symbol])Selector for a fixed-effect coefficient. FixedEffect(:x) names the :x coefficient exactly; FixedEffect() (no argument) is the class selector matching every fixed-effect coefficient.
Example
using Matryoshka
using Matryoshka: isexact, matches
isexact(FixedEffect(:x)) # true
matches(FixedEffect(:x), FixedEffect()) # true — :x is in the classMatryoshka.FixedEffectsComponent Type
FixedEffectsComponent(X::Matrix{Float64}, names::Vector{Symbol}, term) <: AbstractComponentPopulation-level slope component for one or more predictor columns. Samples a single vector-valued coefficient (b ~ arraydist(priors) — DynamicPPL does not support per-name VarNames inside a Dict, so X's columns map onto b positionally, in names order; see test/spike_notes.md Q3) and returns X * b. term is the concrete StatsModels term used to recompute X from new data in rebuild; it may be nothing when FixedEffectsComponent is constructed directly (e.g. in unit tests) rather than via lower.
Example
using Matryoshka, Distributions
using Matryoshka: FixedEffectsComponent, submodel
X = [1.0 2.0; 3.0 4.0]
c = FixedEffectsComponent(X, [:x1, :x2], nothing)
m = submodel(c, [Normal(0, 1), Normal(0, 5)])Matryoshka.Intercept Type
Intercept()Selector for the model's intercept parameter. Always exact — there is no class form.
Example
using Matryoshka
Intercept()Matryoshka.InterceptComponent Type
InterceptComponent(n::Int) <: AbstractComponentPopulation-level intercept component. Contributes a single sampled intercept parameter, broadcast across all n observations.
Example
using Matryoshka
using Matryoshka: InterceptComponent
c = InterceptComponent(4)
c.n == 4Matryoshka.Likelihood Type
Likelihood{F,T}Parsed output of @likelihood: family::F (a Family instance) paired with formulas::T, a tuple of StatsModels.FormulaTerms — the response formula first, any distributional-regression formulas after. Construct via the @likelihood macro, not directly.
Example
using Matryoshka
lik = @likelihood Normal y ~ x
lik.family isa NormalFamilyMatryoshka.MatryoshkaModel Type
MatryoshkaModelWrapper around the DynamicPPL.Model built by model. Owning this type lets Matryoshka provide sample, predict, and returned methods that speak the selector vocabulary without pirating DynamicPPL methods. The raw model is the documented escape hatch m.inner — use it with any DynamicPPL/Turing tooling Matryoshka does not forward.
Matryoshka.MatryoshkaParam Type
MatryoshkaParamAbstract supertype for the parameter selector vocabulary (Intercept, Residuals, FixedEffect, SD, RandomEffect). A selector either names one parameter exactly (an exact selector, isexact(s) == true) or leaves one or more fields unset to target a family of parameters (a class selector). Exact selectors are valid chain keys; class selectors are valid prior targets and sub-chain indices. ==/hash are defined explicitly on each concrete type since selectors are used as dict keys in FlexiChain storage.
Example
using Matryoshka
using Matryoshka: isexact
isexact(FixedEffect(:x)) # true — exact selector
isexact(FixedEffect()) # false — class selectorMatryoshka.NormalFamily Type
NormalFamily <: FamilyNormal (Gaussian) response family: mu is supplied by the predictor formula on the identity scale; sigma is sampled with its own prior (default Exponential(1)) unless targeted by a formula (distributional regression; not yet supported in v0).
Example
using Matryoshka
lik = @likelihood Normal y ~ x
lik.family isa NormalFamilyMatryoshka.PoissonFamily Type
PoissonFamily <: FamilyPoisson response family for count outcomes: mu (rate) is supplied by the predictor formula on the log scale.
Example
using Matryoshka
lik = @likelihood Poisson y ~ x
lik.family isa PoissonFamilyMatryoshka.Priors Type
PriorsParsed output of @priors: an ordered list of PriorSpec(target, dist) targets. Construct via the @priors macro, not directly. Passed to model (or default_priors(lik, tbl)) for resolution against a fitted formula's actual parameters — exact targets beat class targets, and unmatched targets error with the list of valid ones.
Example
using Matryoshka, Distributions
pri = @priors begin
Intercept() ~ TDist(3)
FixedEffect() ~ Normal(0, 1)
end
pri isa PriorsMatryoshka.RandomEffect Type
RandomEffect([group::Symbol[, level::Symbol]])Selector for a group-level (random) effect. RandomEffect(:g, :a) names the :a level of group :g exactly; RandomEffect(:g) is the class selector for all levels of group :g; RandomEffect() is the class selector for every group-level effect. A level cannot be given without a group.
Example
using Matryoshka
using Matryoshka: isexact, matches
isexact(RandomEffect(:g, :a)) # true
matches(RandomEffect(:g, :a), RandomEffect(:g)) # true — :a is a level of :gMatryoshka.RandomInterceptComponent Type
RandomInterceptComponent(group::Symbol, idx::Vector{Int}, levels::Vector) <: AbstractComponentGroup-level (random) intercept component for one grouping variable — (1 | group) in formula syntax. Uses a non-centered parameterisation: samples sd (from its prior, default Exponential(1)) and z ~ filldist(Normal(), nlevels), returning (sd .* z)[idx]. idx maps each observation to its group's position in levels; levels are the training-time factor levels, enforced (not extended) on rebuild — a new level in newdata raises an ArgumentError.
Example
using Matryoshka
using Matryoshka: RandomInterceptComponent, compprefix
c = RandomInterceptComponent(:g, [1, 1, 2], ["a", "b"])
compprefix(c) === :gMatryoshka.Residuals Type
Residuals()Selector for the model's residual (observation-level) parameter. Always exact — there is no class form.
Example
using Matryoshka
Residuals()Matryoshka.SD Type
SD([group::Symbol])Selector for a group-level standard deviation parameter. SD(:g) names the :g group's SD exactly; SD() (no argument) is the class selector matching every group's SD.
Example
using Matryoshka
using Matryoshka: isexact
isexact(SD(:g)) # true
isexact(SD()) # false — class selectorFlexiChains.parameters Function
parameters(f::Family) -> Tuple{Vararg{Symbol}}Names of f's distribution parameters, response (predictor-supplied) parameter first.
Example
using Matryoshka
parameters(NormalFamily()) == (:mu, :sigma)Matryoshka.compprefix Function
compprefix(c::AbstractComponent) -> Union{Symbol,Nothing}Namespace prefix for c's submodel, or nothing for an unprefixed submodel. RandomInterceptComponent returns its grouping variable (e.g. :g, so its parameters appear as g.sd, g.z); InterceptComponent and FixedEffectsComponent return nothing.
Example
using Matryoshka
using Matryoshka: RandomInterceptComponent
compprefix(RandomInterceptComponent(:g, [1, 2, 1], ["a", "b"])) === :gMatryoshka.default_priors Function
default_priors(f::Family) -> NamedTupleDefault prior distribution for each of f's parameters that is not supplied by a formula — e.g. sigma for NormalFamily, sampled with its own prior unless a formula such as sigma ~ z targets it (distributional regression; not yet supported in v0).
See also the default_priors(lik::Likelihood, tbl) method, which returns the full resolved-prior table for a model and dataset rather than one family's defaults alone.
Example
using Matryoshka
default_priors(NormalFamily()) == (sigma = Exponential(1),)Matryoshka.default_priors Method
default_priors(lik::Likelihood, tbl) -> Vector{<:NamedTuple}Resolve the full prior table for lik against data tbl, without fitting. Returns a vector of (target, class, prior) rows — one per parameter the model introduces (each component's slots, then the family's own parameters) — with target and class as MatryoshkaParam selectors and prior the resolved Distributions.jl default. Mirrors brms' default_prior(): inspect before writing a @priors block, or to confirm what a partial @priors block leaves untouched.
See also the default_priors(f::Family) method, which returns only a family's own parameter defaults (as a NamedTuple, not a table).
Example
using Matryoshka, Distributions
df = (y = [1.0, 2.0, 3.0], x = [0.1, 0.2, 0.3], g = ["a", "b", "a"])
lik = @likelihood Normal y ~ x + (1 | g)
tab = default_priors(lik, df)
[r.target for r in tab] # [Intercept(), SD(:g), FixedEffect(:x), Residuals()]Matryoshka.links Function
links(f::Family) -> NamedTupleDefault link function for each of f's parameters, keyed by parameter name and given as a Symbol (:identity, :log, or :logit).
Example
using Matryoshka
links(PoissonFamily()) == (mu = :log,)Matryoshka.model Method
model(lik::Likelihood, pri::Priors, tbl) -> MatryoshkaModelBuild a MatryoshkaModel wrapping a DynamicPPL.Model from a @likelihood spec, a @priors spec, and a Tables.jl-compatible data source tbl. Applies the StatsModels schema, lowers each formula term into a component, resolves priors (exact target > class target > component default > family default), and wires everything into one model via to_submodel. The recipe (components, schema, family, resolved priors) travels inside m.inner.args.recipe (see Matryoshka.recipe), so model(m, newdata) can rebuild it later.
Errors at model() time — never at sampling time — on: unknown prior targets, formula variables missing from tbl, formulas on a parameter the family lacks, or a predictor-less formula.
Example
using Matryoshka, Distributions, Turing
df = (y = [1.1, 2.3, 0.9, 1.8], x = [0.5, 1.0, 0.2, 0.9])
lik = @likelihood Normal y ~ x
pri = @priors begin
Intercept() ~ Normal(0, 10)
FixedEffect() ~ Normal(0, 1)
Residuals() ~ Exponential(1)
end
m = model(lik, pri, df)
chain = sample(m, NUTS(), 100; progress = false)
chain[FixedEffect(:x)] # one coefficient's draws
chain[FixedEffect(), stack = true] # (iter, chain, coef) labeled DimArrayMatryoshka.model Method
model(m::MatryoshkaModel, newdata) -> MatryoshkaModelRebuild m — a model previously returned by model(lik, pri, tbl) — against newdata, reusing the recipe stored in m.inner.args.recipe (same components, schema, and resolved priors; grouping variables enforce training-time factor levels and reject unseen ones). If newdata has no response column, the rebuilt model's y is missing, ready for Turing.predict. If it does have the response column, the rebuilt model is fit-ready (a refit-on-new-data workflow).
Example
using Matryoshka, Distributions, Turing
df = (y = [1.1, 2.3, 0.9, 1.8], x = [0.5, 1.0, 0.2, 0.9])
lik = @likelihood Normal y ~ x
pri = @priors begin
Intercept() ~ Normal(0, 10)
FixedEffect() ~ Normal(0, 1)
Residuals() ~ Exponential(1)
end
m = model(lik, pri, df)
chain = sample(m, NUTS(), 100; progress = false)
m_new = model(m, (x = [0.4, 1.2],)) # no y column → predict-mode
preds = predict(m_new, chain)Matryoshka.obsmodel Function
obsmodel(f::Family) -> (eta, priors, y) -> DynamicPPL.ModelObservation-submodel constructor for f. Returns a callable built by model()/model(m, newdata) as obsmodel(f)(eta, fam_priors, y):
eta: the link-scale linear predictor forf's response parameter — the sum of all component contributions, before any inverse link is applied. Not the mean.fam_priors: aNamedTupleof resolved priors forf's other parameters (e.g.(sigma = Exponential(1),)forNormalFamily).y: the observed response, ormissingwhen predicting.
Each family applies its own inverse link to eta inside the returned model (not by first computing a mean-scale parameter), which is numerically safer under autodiff:
| Family | uses eta as |
|---|---|
NormalFamily | mu directly (identity link) |
BernoulliFamily | BernoulliLogit.(eta) (logit link) |
PoissonFamily | Poisson.(exp.(eta)) (log link) |
Example
using Matryoshka, Distributions, DynamicPPL
eta = [0.1, -0.2, 0.3]
y = [0, 1, 0]
m = obsmodel(BernoulliFamily())(eta, NamedTuple(), y)
m isa DynamicPPL.ModelMatryoshka.priorslots Function
priorslots(c::AbstractComponent) -> Vector{Tuple{MatryoshkaParam, MatryoshkaParam, Distribution}}Prior-targeting slots for c, one per parameter c introduces. Each slot is an (exact, class, default) triple: exact and class are MatryoshkaParam selectors (an exact selector and its class selector, e.g. SD(:g) and SD()), and default is the Distributions.jl prior used when no @priors line matches either. Consumed by resolve_priors at model() time, and by default_priors(lik, tbl) for introspection.
Example
using Matryoshka, Distributions
using Matryoshka: RandomInterceptComponent
priorslots(RandomInterceptComponent(:g, [1, 2, 1], ["a", "b"])) == [(SD(:g), SD(), Exponential(1))]Matryoshka.rebuild Function
rebuild(c::AbstractComponent, tbl) -> AbstractComponentRecompute c against new data tbl, keeping training-time artifacts (factor levels, contrasts) fixed. Used by model(m, newdata) to rebuild each component for prediction or refitting. Grouping variables reject unseen factor levels with an ArgumentError (allow_new_levels is not yet supported).
Example
using Matryoshka
using Matryoshka: InterceptComponent
rebuild(InterceptComponent(4), (y = [1.0, 2.0],)).n == 2Matryoshka.recipe Method
recipe(m::MatryoshkaModel)Return the Recipe stored in m.inner.args.recipe (components, schema, family, resolved priors).
Matryoshka.rekey Method
rekey(m::MatryoshkaModel, chn::FlexiChain{<:VarName}) -> FlexiChain{MatryoshkaParam}Convert a VarName-keyed chain sampled from m.inner into the selector-keyed chain that sample(m, ...) returns: vector-valued parameters are flattened into one scalar key per coefficient/level (FixedEffect(:x), RandomEffect(:g, :a), ...), with labels and order taken from m's recipe. Sampler extras and chain metadata are preserved. Errors if the chain contains a parameter the recipe cannot map — the escape hatch for chains sampled via m.inner directly.
Matryoshka.selectors Function
selectors(f::Family) -> NamedTupleExact selector for each of f's non-response parameters, keyed by parameter name — e.g. (sigma = Residuals(),) for NormalFamily. Used to resolve @priors targets and to key family parameters in sampled chains.
Matryoshka.submodel Function
submodel(c::AbstractComponent, prior) -> DynamicPPL.ModelBuild c's own @model, parameterised by prior (the resolved prior, or vector of priors, for c's slot(s) — as targeted by priorslots). The submodel samples its own parameter(s) and returns its n-vector contribution to the linear predictor. Both the sampled draws and the n-vector contribution returned to core_model are plain, unlabeled arrays; positional entries are mapped onto labels (FixedEffectsComponent.names, RandomInterceptComponent.levels) by rekey at chain-build time.
Example
using Matryoshka, Distributions, DynamicPPL
using Matryoshka: InterceptComponent
c = InterceptComponent(4)
m = submodel(c, Normal(0, 10))
m isa DynamicPPL.ModelMatryoshka.to_vnchain Method
to_vnchain(m::MatryoshkaModel, chn::FlexiChain{<:MatryoshkaParam}) -> FlexiChain{VarName}Inverse of rekey: reassemble the VarName-keyed chain the inner DynamicPPL model understands, using m's recipe for coefficient/level order. Needs the full sampled chain — errors if a selector key is missing (e.g. on a class-indexed sub-chain).
Matryoshka.@likelihood Macro
@likelihood(family, ex)Build a Likelihood spec from a family and one or more tilde-formula lines.
family names the response family as a Distributions.jl distribution type (Normal, Bernoulli, Poisson). ex is either a single lhs ~ rhs expression or a begin...end block of them: the first line is the response formula (e.g. y ~ x + (1 | g)); further lines target other family parameters (e.g. sigma ~ z) — supported by the design but not yet implemented in v0 (model() raises an ArgumentError if any are present). Term parsing delegates to StatsModels.@formula, so formula syntax (+, (1 | g), 0, ...) matches StatsModels exactly.
Example
using Matryoshka
lik = @likelihood Normal begin
y ~ x + (1 | g)
endMatryoshka.@priors Macro
@priors(ex)Build a Priors spec from one or more Selector(...) ~ Distribution lines.
Selector(...) is one of the MatryoshkaParam selector types (Intercept(), Residuals(), FixedEffect()/FixedEffect(:x), SD()/SD(:g), RandomEffect()/RandomEffect(:g)/RandomEffect(:g, :a)). A class selector (no argument, or missing fields) targets a family of parameters; an exact selector (all fields given) targets one parameter exactly. ex is a single expression or a begin...end block. Distributions are Distributions.jl objects — there is no string DSL. Targets are not checked against a model here; that happens later, in model() or default_priors(lik, tbl), where exact targets override class targets and unknown targets raise an ArgumentError listing the valid ones.
Example
using Matryoshka, Distributions
pri = @priors begin
Intercept() ~ TDist(3)
FixedEffect() ~ Normal(0, 1)
FixedEffect(:x) ~ Normal(0, 5) # overrides the FixedEffect() class for coefficient :x
SD(:g) ~ Exponential(0.5)
end