Changelog¶
All notable changes to the quivers library are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[0.25.2] - 2026-10-11¶
Added¶
- Pointwise PSIS-LOO is public.
quivers.diagnostics.loodelegates to ArviZ on its canonical log scale, supportspointwiseandvar_name, preserves every per-observationelpd_iandpareto_kvalue, and reports named errors for missing, ambiguous, or unknown log-likelihood variables.
Fixed¶
- MCMC convergence diagnostics follow the modern cross-chain definitions.
MCMCResult.r_hatnow reports the conservative maximum of rank-normalized and folded split R-hat per coordinate, detecting both location and scale disagreement.MCMCResult.essis explicitly the rank-normalized bulk ESS, with pooled autocorrelation that includes between-chain variance. - Model-comparison precision is caller-controlled.
quivers.diagnostics.compareexposes ArviZ'sround_to;"none"preserves full numeric precision, integers retain ArviZ's decimal-place semantics, and the option composes with every supported weighting method,var_name, andreference.
[0.25.1] - 2026-10-09¶
Added¶
- Explicit MCMC random streams.
MCMCaccepts either a reproducibleseedor a caller-owned CPUtorch.Generator, derives an independent local generator for every chain, and routes initialization, momentum, NUTS tree direction, multinomial subtree selection, and HMC acceptance through that stream without mutating Torch's global RNG state.
Fixed¶
- MCMC adaptation is independent across chains and runs. Every chain now starts with fresh dual-averaging, mass-matrix, and adaptation state instead of inheriting the preceding chain's tuned state; reusing an
MCMCdriver also starts a fresh adaptation lifecycle. - NUTS reports every divergent subtree. U-turn termination and divergence are represented separately, divergence propagates from either child at every tree depth, and an ordinary U-turn is not counted as a divergence.
- MCMC initialization and diagnostics preserve their contracts. Generated and guide-provided initial positions must be finite before sampling begins, and
MCMCResult.divergence_countsis an integer tensor containing one post-warmup count per chain.
[0.25.0] - 2026-10-07¶
Added¶
- A written, enforced export policy. The new Public API and Exports page of the developer guide states what Quivers exports and from where: every public module declares
__all__, a public package re-exports the names of each of its modules as the same objects, public signatures name only public types, every class the DSL compiler places in a compiled program is public, and every public name renders in the API reference.tests/test_public_api.pychecks the import-time rules over every module, andtools/check_api_reference.pychecks the built site in a new CI docs job, which also builds the documentation in strict mode on every pull request. - A supported Python surface for building programs without the DSL.
quivers.continuousexports the typed step records aMonadicProgramis built from (Draw,Observe,Let,Score, and theIndexedargument form for gathers such asmu[group]), together with every builder the compiler emits:FixedDistribution,MixedInlineDistribution, themake_fixed_*factories,PlateDraw,VectorisedObserve,FanOutMorphism,MarginalizedFactor,reading, and the family registry.MonadicProgram.stepsreturns a program's records andMonadicProgram.step_modulethe module a draw applies, so a compiled program can be inspected in the same vocabulary it is built from. The programs guide works through one program built both ways and checks that the two score identically. - A literal-parameter inline
LKJCholesky.sample L <- LKJCholesky(concentration=2.0)compiles through the newmake_fixed_lkj_choleskyfactory, and aPlateDrawover either it orLKJCorrelationFactordraws one Cholesky factor per group. - The module-level names newly exported under the policy, among them the bijectors, the measure algebra, the deterministic morphisms, the agenda and category types of
quivers.stochastic, the structural encoder helpers, the transformation and wiring morphisms ofquivers.core, the arrow instances, the flow transforms and MCMC adaptation helpers ofquivers.inference,loo_pitinquivers.diagnostics, and the effect-handler trace, conjugate-solver, and checked-program types ofquivers.effects.
Changed¶
MonadicProgramtakes step records rather than tuples. A draw isDraw(names=("x",), morphism=f, args=("a",))rather than(("x",), f, ("a",)), an observe isObserve(...)rather than a four-tuple ending inTrue, a let isLet(name="w", value=fn), and a score step isScore(name="s", score=fn). Two draws or observes that bind the same name are an error rather than a silent replacement of the first morphism.- Names that collided under a package's namespace were renamed. The bijectors carry a
Bijectorsuffix (ExpBijector,SoftplusBijector, ...); the family registry functions areregister_family,get_family_spec, andfamily_names; the deterministic builders arecumsum_morphism,softmax_morphism, andcholesky_quad_form_morphism, built on the publicDeterministicMorphism; the homomorphism singletons ofquivers.core.algebra_morphismsareLOG_PROB_HOMandMAX_PLUS_HOM;quivers.monadic.instances.ListisListMonad; the CLI check result types areCheckDiagnosticandCheckSeverity, withcheck_filethe per-file entry point;quivers.kernel.install.installisinstall_kernelspec; the QIEC execution trace recorder isExecutionTraceRecorder, and the static scope checking functions take is the publicStaticScope; and the transpile renderer context is the publicRenderContext; and the ordered-logistic morphism a~ OrderedLogisticdeclaration compiles to, which learns its cutpoints, isConditionalLearnedOrderedLogistic, distinct from theConditionalOrderedLogisticthat takes them explicitly. - Renderer helpers are private. Each renderer's
render_listandrender_matrixhelpers are internal,PyroRenderer.marginalizetakes no private context keyword, andformat_stanandturing.render_moduleare no longer exported. Renderer methods annotate only public IR types, and the fullirandqiec_irvocabularies are exported for renderer authors. check_monad_lawschecks the monad laws. It verifies left unit, right unit, and associativity throughpure,join, andfmap, and takes the instance and a carrier.LKJCorrelationFactoris the normalized LKJ density. It scores and samples throughtorch.distributions.LKJCholesky, honourssample_shape, and accepts flat or square factors.
Fixed¶
- Stacked parameters keep their dtype. Multi-argument inline parameters, and the log joint that accumulates their densities, stay in the widest floating dtype of their inputs instead of being cast to float32, so a float64 program agrees with a float64 reference to rounding.
OrderedLogisticis exact in both tails. A category's log probability is computed as a sum of log-sigmoids and alog1mexpterm rather than as the log of a difference of sigmoids floored at the smallest normal float, so log likelihoods below about -708 are no longer clamped.MixedInlineDistributionchecks its declared widths. The stacked columns must match the widths the parameter specification declares; at most one vector parameter may leave its width unresolved (None) and take the remaining columns. An inlineCategoricalorDirichletreads its parameter as a vector per row, which corrects categorical formula likelihoods that had scored only the first logit column, and an observe whose logits are a plate of per-category scalars, which had scored a one-category distribution.- Inline parameters of unknown width are read off their values. A family argument whose binding has no known type leaves its width unresolved, and the stacked arguments settle it, so a vector-valued
letpassed to a scalar family parameter supplies one parameter per coordinate. Literal parameters broadcast against such a vector, and its per-coordinate densities sum to one per row. The GRU, LSTM, and vanilla-RNN language models had drawn every candidate-state coordinate around the first coordinate of its mean; a gather such asemission_rows[state]is now one plate row wide rather than the whole plate. - A plate over a matrix-valued family sizes its rows by the matrix's element count, so a
PlateDrawover a Cholesky-factor family draws and scores whole factors. loo_pitmatches ArviZ 1.x, passingvar_namesand returning a dataset.
Removed¶
check_applicative_lawsandcheck_alternative_laws, which asserted nothing.- The unused
DirectBernoulli,DirectNormal, andDirectTruncatedNormalclasses. - Exports of internal machinery: parser walkers, compiler mixins and their private re-exports, elaboration and lowering helpers, program-encoding constants, the CLI subcommand
mainfunctions, andcommit_id_for.
[0.24.0] - 2026-10-03¶
Changed¶
- Distribution-family construction is keyword-only. Every QVR family application now names one complete parameter schema, such as
Normal(loc=mu, scale=sigma),Dirichlet(concentration=alpha), or eitherCategorical(probs=weights)orCategorical(logits=scores). Parameter names are structural syntax-tree nodes, are preserved through parsing, emission, validation, eager execution, QIEC elaboration, and transpilation, and are reordered only at positional target-API boundaries. Ordinary computation and morphism application remains positional.qvr migratenames legacy family arguments and gathers formerly splatted categorical and Dirichlet values into their vector parameter.
[0.23.0] - 2026-09-23¶
Added¶
- Identified categorical formula models.
family="categorical"now infers contiguous response labels, takes category zero as the reference, and emits class-specific fixed and non-centered random effects over the remainingK - 1logits. External neural predictors may return either reference logits or a fullK-logit matrix. - Configurable finite-mixture regressions.
family="mixture"accepts any integermixture_components=KwithK >= 2, shifts all Gaussian component locations by the formula predictor, and learns shared softmax weights, sum-zero component offsets, and positive component scales. An orthonormal Helmert basis mapsK - 1free contrasts into the sum-zeroK-dimensional subspace without a redundant direction; under the default isotropic Normal priors, the induced distributions are permutation symmetric. The likelihood integrates component assignments rather than adding discrete latent sites.
Fixed¶
- Scalar formula families match their documented parameters. Gamma converts mean and shape to concentration and rate, Beta converts mean and precision to two concentrations, Student-t uses
(df, loc, scale), and zero-inflated and hurdle Poisson use(zero_probability, rate). - Derived mixture vectors retain their component width. An indexed component draw now supplies the width of transformed weight and location vectors to the multi-vector inline likelihood, so formula-generated mixtures execute under SVI and MCMC.
- Python transpilers lower softmax on the category axis. Pyro, NumPyro, PyMC, and Edward2 emit their native softmax with
dim=-1oraxis=-1, allowing generated categorical and mixture formulas to cross those target boundaries. - Fitted QVR source preserves prior configuration.
BayesianFit.qvr_sourceanddump_qvrnow re-emit the fixed, random-scale, and named prior overrides used for the fit.
[0.22.0] - 2026-09-23¶
Added¶
- Formula-compiled ordinal mixed models.
family="cumulative"now learns an identified ordered cutpoint vector from positive spacings.thresholds_by="group"gives each level partially pooled spacing deviations, gathers its centered cutpoints per response row, and composes with the formula's fixed and random effects. - Differentiable external predictors.
fit(..., predictor=module, predictor_data=tensor, method="svi")adds a PyTorch module's per-row output to the formula linear predictor and optimizes its parameters jointly with the QVR program and variational guide. Emitted QVR exposes the attachment as the explicitneural_etahost input; frozen predictors also work with HMC and NUTS. - True binomial formula likelihoods.
binomial_trialsaccepts either one positive integer or a per-row data-column name and emitsBinomial(total_count, probs)rather than treating binomial as a Bernoulli alias.
Fixed¶
- Formula-family parameter semantics. Cumulative formulas now supply ordered cutpoints to
OrderedLogistic. Negative-binomial formulas convert their mean/concentration surface to QVR's(total_count, probs)convention withprobs = mu / (mu + disp). - Impossible log-probability cells have finite gradients.
LogProbAlgebra.joinretains-inffor an all--infreduction and defines its gradient as zero instead of inheritingNaNfromtorch.logsumexp. - The ArviZ dependency matches the DataTree API. The
diagnosticsandformulasextras now require ArviZ 1.0 or newer, the first stable line matching the adapter'sxarray.DataTreecontract.
Changed¶
- Binomial's inline family signature is explicit. Runtime and generated programs share the two-parameter
Binomial(total_count, probs)surface; the conditional morphism continues to learn probabilities for its configured trial count.
[0.21.0] - 2026-09-22¶
Fixed¶
- Nullary logical-form constants match deduction patterns. Inline and TSV-backed lexicons now encode a declared nullary constant as
("atom", name), the same representation the rule compiler emits. A rule such asspan(I, J, QNP, App(every_q, P))therefore matches an LF constructed from the lexical constantevery_q. - Complex TSV logical forms parse through the current grammar. File-backed lexicons use a valid synthetic QVR module before applying the same constant tagging, binder alpha-renaming, and scope validation as inline lexicons.
Changed¶
- Deduction terms use discriminated public leaves. A bound LF variable is now
("var", canonical_name)rather than(canonical_name,); a nullary LF constant is("atom", name)rather than(name,); and applications remain(constructor, *arguments). Consumers that inspect chart tuples should migrate one-tuple leaf checks to the corresponding tag.atomandvarare reserved in deductionatomsdeclarations. - The Montague NLI example factors syntax from quantificational force.
everyandsomeshare oneDetcategory and one determiner-composition rule; sentence rules distinguish them by matching their nullary LF constants.
[0.20.1] - 2026-09-22¶
Fixed¶
- Finite traversals cross the probabilistic target boundary. The program-plan walker reassociates the nested QIEC binds produced by a fixed
traverse, so sequenced collection calls insideprogrambodies transpile to all eight dynamic targets. The lexical-uncertainty RSA example now transpiles completely to Pyro and NumPyro rather than stopping at its state-likelihood traversal. - Checked integer values retain their target type.
int(real_value)renders as a real-to-integer cast on Pyro, NumPyro, PyMC, and Edward2 instead of failing during Python target emission, and integer-typed QIEC literals render as integer tokens rather than floating-point tokens. Tensor indices produced by finite traversal therefore remain valid target-language indices. - Target-aware tooling validates the executable plan.
qvr check --target, the REPL, and the LSP now derive the probabilistic plan after QIEC capability analysis and report plan-level refusals such as a non-log-sum-exp marginalization beforeqvr transpileis run.
[0.20.0] - 2026-09-21¶
Added¶
- Checked finite collection expressions.
map,fold,length, andlogsumexp_overnow lower from the shared pure-expression surface to typed QIEC terms.traversesequences a named computation across a tensor with a fixed leading extent, preserves source order and call provenance, and joins the callee's effects into the enclosing row. A local pure binding may name a lambda reused by these operations.filterremains available to the eager PyTorch evaluator and is rejected underqiec-collection:filter:dynamic-shapebecause its value-dependent result length is not a fixed QIEC tensor shape. - Collection documentation and executable examples. The QVR reference, program guide, formal semantics, and a finite-grid Bayesian tutorial state the pure/effectful distinction, shape rules, diagnostic boundary, and backend support. The tutorial source is checked and runnable through the reference machine.
Changed¶
- Multiline delimiters use a hanging-indent form. Nonempty parenthesized, square-bracketed, and brace-delimited lists may place one trailing-comma-terminated item per indented line. Calls, QIEC static applications and requests, constructor values, list literals, and indices share this grammar. The source protocol advances to
qvr-source/v0.20; the QIEC and JSON ABIs remainqiec-core/v1alpha1andqiec-json/v1. - Let-expression analysis is exhaustive and binder-aware. Free-name collection, recursive walks, and capture-avoiding substitution share one implementation across program elaboration, native compilation, QIEC lowering, and dependency resolution. Newly introduced AST variants therefore fail explicitly instead of disappearing from one of those passes.
- Incomplete program elaboration is an error.
qvr check, the LSP, and entry lookup reportqiec-program-gapwhen a source program cannot enter the checked module. Native-only builtins no longer make the checked path appear successful. - Builtin capabilities have one registry. Eager implementations and arities, purity, shape action, checked QIEC form, and execution routes are recorded together. Native-only calls report
qiec-builtin:host-only; eager execution no longer drops additional call arguments. - Collection calls highlight consistently. Pygments, the REPL/TUI, tree-sitter queries, and the packaged VS Code grammar classify the checked collection operations as builtins.
- The v0.19 grammar is frozen in the migration chain. Its released parser and schema identity now sit between v0.18 and
HEAD; the new hanging-delimiter grammar has its own verifiedHEADsnapshot and a validating, byte-preserving v0.19 migration hop.
[0.19.0] - 2026-09-19¶
Added¶
- QVR v0.19 indexed families and algebraic effects. The typed QIEC surface adds closed index sorts, indexed-family constructors, heterogeneous static telescopes, parameterized effect interfaces, lexical instances, open effect rows with
lacksconstraints, handler contracts with resumption grades, and checkedreturn,perform,bind,handle, and indexed-case computations. Type binders shadow same-spelled primitive types, and effect-interface applications instantiateEffectbinders under the same lexical kinding rules. - A stable indexed-effect kernel. QIEC modules lower through Didactic's exact
qvr-source/v0.19toqiec-core/v1alpha1route, serialize through the deterministicqiec-json/v1allowlist, and execute under a reference evaluator with deep lexical handlers. Deserialization validates every allowlisted field recursively, and module validation checks closed effect instances, request provenance, and escaped static variables. Runtime callables and mutable resources remain process-local attachments. - QIEC-aware developer tooling.
qvr runexecutes named computations with checked JSON arguments, closed static specializations, explicit runtime-provider configurations, and stable traces. The REPL and TUI expose the same boundary through:run,:runtime, and:detach, and the TUI status bar shows the active runtime and last result.qvr check --target,qvr lsp --target,workspace/didChangeConfiguration, and the VS Codeqvr.transpileTargetsetting provide live target-capability diagnostics. These surfaces also share QIEC symbols, completions, source ranges, and stable diagnostic codes. The Pygments and tree-sitter highlighters recognize the same v0.19 surface. The panproto migration chain gains a validating, byte-preservingv0.18.0 -> HEADhop because the new grammar is additive. -
A complete v0.19 documentation track. The QVR reference now covers indexed families, effects and handlers, computations, probabilistic-program elaboration, generated parser and structural graphs, entry execution, runtime providers, LSP behavior, transpilation, and grammar ownership. Five executable tutorials develop the new features from indexed recursion through target-aware release checks. The examples gallery gains a feature map, checked-runtime paths for the schema parser and term autoencoder, and an integrated Amortized Bayesian Semantics case study combining parser search, structural attachments, indexed missingness, the measure algebra, an authored handler, hierarchical effects, and grouped exact marginalization.
-
Programs are computations. A
programelaborates to a named computation over the module's canonicalrandomandscoreinstances: its data, observations, and fibrations are typed parameters,sampleandobservesteps performRandom.sampleandScore.add, plates are typed tensor shapes, and amarginalizeblock is a helper computation enumerated under the prelude's enumeration handler. The module records each program as aProgramEntry,run_programscores a program's log joint on the reference machine, and a program body binds a named computation's result withlet x <- helper(args), draws from another program withsample x <- suborsample (a, b) <- sub(each name one component of the pair the program returns), while a computation calls a program by name. - Literal family arguments fix a kernel. A declaration such as
morphism prior : A -> R ~ Normal(0.0, 1.0)now denotes that same input-independent family on the torch runtime and the QIEC reference machine. It carries no learned lookup table; a bare~ Normalcontinues to request an input-conditional parameter source. - Schema chart parsers are computations. A
parser(...)definition over declared schemas and bundles elaborates to a finite category item family, lexical and rule weights, a goal-directed recursive derivation, and aSearch/Weightentry computation.run_deductiongives the same inside score as the classicInsideAlgorithm, including the bounded unary closure, and transpile targets refuse the executable parser underqiec:capability:searchrather than treating its declarations as inert metadata. - Structural autoencoders are typed call graphs. A structural signature becomes an opaque term family indexed by its declared sorts. Encoders, decoders, and decoder negative log likelihoods become typed
Computecomputations, while a reconstruction loss calls the encoder and decoder computations and returns aLogWeight.Program.runsupplies the compiled PyTorch modules at the provider boundary, validates their results, preserves their autograd graph, and agrees exactly with the classic loss registry. The modules are registered under stable source-derived names so optimizers and checkpoints see every parameter. Targets without those attachments refuse the named computation underqiec:capability:neural-attachment. - Open input extents are static parameters. A program input whose shape no plate or annotation fixes, such as the concentration vector of
sample probs <- Dirichlet(alpha), is typed over an index variable the program's computation binds, so the entry point records oneNatbinder per open extent, a run reads each extent off the data, a call step from another program takes the extent as its own, and an authored computation names it with an index literal:prog[3](alpha). Integer literals are admitted wherever a static argument fills aNatbinder. - The reference machine scores every family. The reference distribution backend implements all fifty-two families of the semantic registry in plain Python, the matrix and compositional ones included, with each density held to the torch definition the transpile probes use; a program using any registry family runs end to end on the reference machine.
- The program protocol carries the computation graph.
QVR_PROGRAM_PROTOCOLgains vertex kinds for effect interfaces and their operations, lexical instances, handlers and their clauses, named computations and their parameters, and program entry points with their parameters and sites, joined byinstance_of,handles,row,calls,performs,installs,body,random, andscoreedges.extract_program_schemawrites the checked module beside the object and morphism declarations, so a mixed module diffs as one call graph. - Effect handlers are lexical kernel handlers. A
MonadicProgramruns on the reference machine as the kernel computationprogram_kernelencodes it to, whose draws areRandom.samplerequests on the program'srandominstance and whose densities areScore.addrequests on itsscoreinstance, with torch tensors as host values. EveryEffectHandleron the stack installs a prelude handler of one of those instances, soclamp,do,scale,mask,block,replay,lift,collapse, the reparameterizations, and the trace compose by the kernel's own rules: a handler ofrandomsees a site before any handler outside it and sees the outer answer flow back, and every handler ofscoretransforms every density on its way to the accumulator. A trace reads each site's density from the contributions that reached the accumulator under the site's provenance, and records the morphism the program declares beside the sampleable actually drawn from. The prelude gains theComputeandParaminterfaces and thedraw_scoring,intervene,reweight,block,compute, andparamhandler factories the stack is built from. - The measure algebra is a family.
Restrict(also spelledTruncate),Normalize,PointMass,Mixtureover a heterogeneous component list,Transformed(alsoPushforward) over a named bijector chain, andIndependentare registry families with reference densities and log masses, and the classic operator forms elaborate through them; an integer atom beside an integer family,Mixture([0.3, 0.7], [PointMass(0), Poisson(rate)]), samples anInt.ZeroInflatedPoisson,HurdlePoisson, andZeroOneInflatedBetajoin the registry. - Grouped marginalization in full. A
marginalizeblock elaborates with itsreduction(logsumexp,sum, ormean), a product group[over=[G, H]]with the product fibration[via=[g, h]]flattened row-major, and nesting: a grouped block inside a grouped block adds one aggregate per group position to the enclosing group's weights, its group either identified with the enclosing one position by position or a product refining it, whose aggregates and enclosing-group-shaped arguments are carried along the projection. Asampleinside a block that reads nothing the block binds is drawn once before it. A vector family's spread literals,Dirichlet(1.0, 2.0, 3.0), gather into its one parameter, and a single literal is the symmetric parameter at the dimension the plate or the program's codomain fixes; a: Aannotation beside anoverclause is the batch plate. The classic compiler accepts an indexed class prior,Categorical(theta[z]), in a grouped block and host-data product fibrations, and reports its own diagnostics before the elaboration's. - One lowered root.
Lowerderives a program's target plan from its checked computation rather than from the source steps: aRandom.samplerequest is a draw, a scored density at an observation an observation with its fibration, a pure binding a deterministic node, another scored weight a score, and a marginalization helper's scored call an integration scope, each with the support and plate read off the module's types, and the inputs are the entry point's parameters, named by the axes the elaboration recorded.IRProgramcarries the checkedIRQiecModuleas itsmodulebeside the plan, the renderers place the module's other computations withrender_computations_dynamicandrender_computations_static, and the transpile boundary is the lowering itself. A program the elaboration has no computation for is refused under the kind its gap names; an unknown call in aletis refused (let:call:unknown:<name>) rather than emitted as an unresolved name, and a lambda binding leaves no node since the elaboration inlines its uses. Program parameters record their axis names.sumand the other reductions of a tensor of rank two or more reduce along the last axis, as the torch runtime does; amarginalizeoverContinuousBernoulliorRelaxedBernoullienumerates the hard atoms under the Bernoulli masses, one latent per element of an annotated axis, as the torch runtime does;Paretobindsscalebeforealphaand the relaxed families take theirtemperaturefirst, as the torch constructors do; a zero-ratePoissonis a point mass at zero. - Scans are recurrences. A draw from a chain with
scan(cell)expands to a step program that draws the chain's elements before the scan at one position and applies the cell, and to a letscan(step, xs)over the chain's input, which elaborates to a recursive helper computation over the positions of an open extent the program binds; the step's sites are reached once per position andrun_programreplays a site'sn-th occurrence from the value keyed"<site>@<n>", so the reference machine scores the trajectory the torch runtime'sScanMorphismscores. A kernel morphism's heads follow the torch runtime's parameterization for every family it supports, a scalar family applied to a vector draws one coordinate per entry, and a program called with its domain arguments alone passes its remaining inputs through the caller. Every gallery model but the program templates elaborates in full; the transpile boundary keeps refusing scans. - Network kernels are computations. A draw through a morphism carrying
[param_source=mlp]elaborates to the family with its heads read off the last of a chain of affine maps undertanh, the hidden widths taken frommlp(a, b),hidden_dim, or the default two layers of sixty-four; the layers' weights and biases are typed program inputs whose shapes match the torch runtime'sMLPSourcelayer by layer. An embedding morphism,[role=embed], elaborates to a Normal kernel over its finite domain reading a table of centres and log scales. A kernel under a plate applies its map at every row, so an unbound conditioning name becomes a per-row data input. A step drawing from a composite expands into one site per factor:fanover a[replicate=k]morphism draws through each replica and bundles the draws, a kernel conditioned on a bundle reads its rows in order,stackcomposes copies declared afresh with parameters of their own, and a tensor product's branches at the head of a chain read their own factors of the program's domain, and the chain's head conditions on the row the step wrote. The neural gallery models elaborate and run on the reference machine, whose site densities agree with the torch runtime's morphisms. - Deductions are computations. A
deductionelaborates to a closed item family, a recursive derivation computation, and an entry that answers the inside weight of the goal in the declared semiring. Rules become branches of the derivation: it chooses an axiom or a group of rules of one premise shape on the module'sSearcheffect, derives each premise by recursion, matches it bycaseanalysis with the family's structural equality for shared pattern variables, instantiates the conclusion, and adds the rule's weight on aWeight[K]instance; a failed match performs an empty choice. Learned weights are read through aParaminstance under the names the agenda engine keys its parameters by, a span-shaped system derives by span, and any other takes its axioms and their weights as input. The entry handles the weight instance with a collecting handler and the search with a handler that resumes once per alternative and combines the shots by the semiring's addition, so the reference machine gives the number the agenda engine tabulates, in every semiring. A program calls a deduction withlet chart = parse(D, sentence)and scoreschart.goal_weight();run_deductionruns one on the reference machine anddeduction_itembuilds its items. A foreign clause may resume in tail position withTailResume, which the collecting handler does so a search inside its scope may resume any number of times, and a collecting handler configuredforkablegives each shot its own total. An integer operand beside a real one is converted in a program or computation body, the kernel term carrying the conversion. The agenda engine's non-learnable entries and rules contribute the semiring's one, so its Boolean and counting charts hold. - Programs call computations on every target. A program that binds a computation's result,
let c <- noisy(b), transpiles to each of the eight host-language targets as a call in the model body: the plan inlines a pure callee whose body is a chain of bindings, and renders every other call against the callee's generated entry point with the program'sRandomandScoreinstances bound to a native operation table built from the host's own primitives, so a draw inside the helper is a site of the model under the helper's label and a scored weight is a term of the joint. Pyro, NumPyro, PyMC, Edward2, Turing, Gen, WebPPL, and Church score a model calling an effectful helper to the reference machine's joint, the probe images checking each. The Python bridges spell the scalar primitives in the host's own library, so a helper's arithmetic on a drawn value stays on the host's array. Stan defines a needed pure computation as a user-defined function, with scalar primitives, conditionals, calls, and recursion, and calls it as a transformed parameter; BUGS and JAGS refuse a call ascall:graph:<callee>. A static target is judged by the computations the program's calls reach, and defines every computation it can represent; one the program never calls and the target cannot represent is left out of its output. - The WebPPL runtime is WebPPL. The QIEC runtime a WebPPL program carries is written in WebPPL's functional subset, with recursion for its loops and the global store for its serials, ambient handler stack, and handler cells, so a generated program compiles and runs under the
webpplcommand line, inference included, and the runtime tests run it there. A WebPPL host attachment holds a mutable binding in a global-store cell named by itscellfield. - Every target scores a
scorestep. Edward2 and Gen, which have no factor primitive, trace a scored weight as a choice of a one-point distribution whose log density is the weight, Gen under the:qvr_factoraddress namespace, and their probes read it as a term of the joint. Gen also marginalizes: its emission is the Turing renderer's, the per-row draws stacked into matrices and the reduced weight traced as a factor, andldaandzip_regressionscore the reference joint on Gen. Church binds a scored weight under its name before adding it. - Julia shapes follow the plan. Turing and Gen read whether a binding is per-element off the plate the plan gives it, which the elaboration reads off the binding's checked type, rather than off the sites that read it, so a scalar binding a plated observation reads,
cell0 = cell_score[0, 0], is one value shared across the rows; BUGS and JAGS write a plated list binding, such as a case table over a finite object, one cell per relation. - One entry-point invocation. A checked module's executable entry points are its
definecomputations and its programs, listed byentry_pointsand invoked byinvoke_entry, whichqvr run, the REPL's:run,Program.run, andCompiler.entryshare, so an entry validates its arguments, selects its providers, traces, and fails with the sameqiec-run-*codes however it is reached. A program runs forward on the reference machine throughsample_program: the sites given with--siteare replayed and scored, every other site is drawn and scored under the core provider'sdraw-scoringhandler kind, its parameters are given positionally or by name with--data, and--seedseeds the reference generator the draws use; the run reports the program's value and its log joint.qvr run FILEwith no entry name, or:runalone, lists the entry points with their signatures. Each invocation owns its observations, parameters, generator state, score accumulator, and trace, and no handler stack outlives a call. - Programs run on the reference machine.
MonadicProgram.rsampleandMonadicProgram.log_jointrun the program's kernel encoding under the active effect handlers, so the jointlog_jointreports is the onetraceaccumulates; each host step is encoded over the values it reads, a step closure declaring its names throughreading, and every handler a run installs resumes in tail position, so a program's encoding and its evaluation grow linearly with its length. A program a host step runs in turn is a separate invocation the enclosing run's handlers neither observe nor condition. A program that calls a named computation compiles to aCheckedProgram, whosersampleandlog_jointare the module's ownsample_programandrun_programon its entry point. A deterministic morphism drawn as a step is contracted against its input as anapplystep of the encoding. - Nested grouped marginalization scores as stated. An inner grouped block contributes one aggregate per position of its plate to the enclosing block, identified with the enclosing plate position by position or summed along the projection from a product plate onto it, as
programs.md§2.7 states;marginalize_groupedreturns the per-group aggregates underper_group=True. A nested plate of another extent is refused at compile time. - Every target keys a grouped block by group. A
marginalizeblock whose observed rows fibre into its groups through aviaindex sums each atom's per-row log-likelihoods within the group before the reduction over the atoms, asprograms.md§2.7 states, on Pyro, NumPyro, PyMC, Edward2, Turing, Gen, WebPPL, Church, and JAGS, which selects a group's rows through an identity matrix subscripted by the fibration and adds the per-group density through the zeros trick. BUGS refuses such a block (marginalize:grouped-fibration) and every scope that is no categorical mixture (marginalize:no-collapse), since the language has no statement that adds a free log-density to the joint and a declared latent would denote a measure on a larger space than the source integrates. Church integrates a marginalized latent out through its runtime'slog-sum-exp,group-sums, andfactor, binds each observe and factor under a name so a definition may follow it in the body, and binds a call's static environment and local aliases once per body. - Static arguments parse in the kernel's form.
parse_static_argumentsreadsn=S(S(Z))as the index term a checked type carries, a nullary constructor being the sort's literal, so a specialization at a user index sort validates the values the module's own constructors build; a diagnostic names an escaping local instance by its source name. - A program draws from a program.
sample theta <- school_effects(0.6, School)andsample (u, v) <- subrun the program in place: the objects and scalars the draw names substitute for the program's template parameters, each name the program binds is renamed under the binder (zbecomestheta$z, the returned name becomestheta, a pattern's names take the returned names positionally), and every target spells a renamed site with__(theta__z), since no target admits$in an identifier. A program over object parameters is itself a computation whose telescope binds one index variable per object, listed and invocable as an entry point with its statics;let h <- cell(x, h_prev)calls a program as the computation it is, which the plan refuses (qiec:call:program:<name>) since the callee's sites are its own. A morphism parameter takes a declared morphism at the draw,with_prior(Subj, my_prior), the template's steps drawing through it; such a template is no entry point of its own, a morphism being no static argument. A plate over a finite object drawn through a kernel on that object conditions each row on its own element. Ascorestep on JAGS binds its zeros-trick carrier in the emitteddatablock, and the BUGS probe supplies the carrier the BUGS emission leaves to the host. - One check path on every surface.
qvr check, a REPL:load, the TUI's diagnostics panel, and the language server report call-graph, row, handler-body, recursion, and target-capability errors with the same codes and positions, and a row or type in a diagnostic renders in the surface spelling with the instances' source names (!{random : Random}) rather than as a dataclass. The REPL's:typeresolves an operation through its interface or an instance with the instance's static arguments substituted,:kindand:effectsreach the prelude's interfaces,:effectsprints a computation's inferred row after call expansion and a program's checked row, completion offers a prelude instance's operations, and the TUI status bar names the last entry point with its kind, value, and log joint, or a failed run's diagnostic code.:set target=andqvr repl --targetreport a transpile target's capability diagnostics on load, asqvr check --targetdoes. - Calls and clauses in the language server. Hovering a call shows the signature the call instantiates its callee at and the callee's inferred row; hovering a request's operation shows the instance's instantiated signature above the interface's; a computation's hover carries its inferred row.
textDocument/renameandprepareRenamerename a declaration at every reference and refuse prelude names; document symbols nest a computation's parameters, locals, scoped instances, branch binders, and call sites, a handler clause's binders and calls, and a program's steps; a clause's binders resolve inside the clause alone. A configuration change re-runs only the capability pass over the already checked module. The REPL and TUI highlighters take their token classes from the Pygments lexer, which marks a clause's operation and a call's callee as functions, a resumption grade as a number, and a handler option key as a keyword; the tree-sitter query and the TextMate grammar agree on the same corpus. - Distributions, tensors, and sites are values. Applying a family of the semantic registry builds a typed
Sampleable, a list literal is aTensor,site("x")names a sample site, andlog_prob(d, x)is aLogWeight; a construction may be plated over batch and event axes with parameters broadcast along them. The pure expression layer gains elementwise math over tensors, whole-tensor reductions, last-axis operations,factorcomprehensions, gathers, segment sums, kernel matrices, affine parameter maps, and table-indexed parameter maps, which a kernel over a finite domain reads its heads from at the element the program is applied to. Every dynamic transpile target spells a first-class distribution in its own library with that target's parameterization conventions, and probe images check each host's log density against the registry.
Changed¶
Horseshoe(scale)is refused by every transpile target. The family denotes the marginal of a normal draw over a half-Cauchy local scale, which has no closed form and no library distribution on any target. Each target refuses the family with a diagnostic naming the explicittau * lambda * zdecomposition, which every target expresses and the gallery already uses. The torch runtime and the reference machine score the marginal itself.- Effect declarations are unversioned source contracts. An
effectcontains only its name, static telescope, and operation signatures. QIEC ABI and package compatibility remain external to authored QVR declarations. - Indexed declarations use Didactic's GADT API directly. QVR families and constructors are projected through Didactic's public
GADTAPI and compiled by Panproto before the QIEC-specific checker completes the exact lowering route. QIEC retains stable identities, effect rows, and the distinction between uniform family parameters and refinable indices. - The current QVR grammar ships with Quivers and loads fail-closed. Editable checkouts use their generated
grammars/qvr/src; wheels carry the same generated parser source.panproto-grammars-allcontinues to provide the eleven transpiler-target grammars but is no longer the QVR source of truth. Panproto validates persisted historical objects against the enum payload read from disk, so the migration chain retains its content-addressed fixture identities without rewriting stored hashes. The floors move todidactic>=0.17.1,panproto>=0.74.4, andpanproto-grammars-all>=0.74.4. - Platform wheels load QVR without a host compiler. Each Linux, macOS, and Windows wheel contains a native current parser plus native libraries for every packaged migration snapshot. Source-binding manifests cover the generated parser and tree-sitter metadata as well as the library bytes; a missing or inconsistent pair fails closed. Source distributions and editable grammar development still compile during build or into the development cache, but an installed platform wheel does not compile on first parse, highlight, or migration.
- Probabilistic transpilers execute the QIEC boundary. Pyro, NumPyro, PyMC, Edward2, Turing, Gen, WebPPL, and Church emit named QIEC functions through corresponding stable-ID ABI implementations, including lexical handlers, effect requests, indexed cases, attachments, and checked resumption grades. Target reparse, external-syntax, and available-runtime execution tests exercise these implementations without claiming an equivalence proof. Stan admits closed, monomorphic, effect-free scalar computations of bindings, primitives, conditionals, calls, and recursion, with named
Bool,Int, andRealvalue parameters; BUGS and JAGS admit parameterlessReturn/Bindcomputations. A computation the target lacks a feature for receives feature-level diagnostics. - The transpiler has one renderer path. The unused PyMC-only AST adapter and the public
SchemaTransform/realizeexports are removed; every target enters through the registered Module → IR → renderer boundary.
[0.18.0] - 2026-09-10¶
Added¶
- Transpilation to eleven probabilistic programming languages.
transpile(module, target=...)emits a parsed QVR module as source for BUGS, Church, Edward2, Gen, JAGS, NumPyro, PyMC, Pyro, Stan, Turing or WebPPL;available_targets()lists them. A previous release shipped the target-agnostic scaffold, the IR and the lowering, and left the per-target renderers out; this release supplies them, soavailable_targets()returns the eleven names rather than nothing. The emitted program denotes the same measure as the QVR module, up to a point-independent additive constant that reflects the target's base measure, anddocs/transpile-support.mdrecords the measured constant for every cell that carries one. - A construct the target cannot express is refused rather than approximated.
UnsupportedConstructnames the construct, says why the target has no form for it, and gives the ways to write the model that do transpile. The message is prose assembled from the module's own AST; the structured tag that identifies the rule is still carried for programmatic dispatch. Two classes of refusal appear. Some constructs no backend expresses,scan(...)among them: a scan denotes one draw per sequence position over intermediate states the program never names, over an axis that arrives with the data, so there is no loop bound to emit and no name to bind the states to. Others are target-specific, and each turns on a measure the target cannot represent rather than on effort: Gen has no way to add a free log-density term to a trace, so it cannot integrate amarginalizeat all, and BUGS, Church, Edward2, JAGS, PyMC and Turing cannot express the accumulated reduction an ungroupedmarginalizedenotes. qvr transpileon the command line and:transpilein the REPL. The subcommand takes a.qvrfile with--to <target>or--to-all, and the REPL command completes on the available target names.ConditionalBetaBinomialandConditionalHalfStudentT. Conditional forms of two families whose unconditional versions were already available, for counts with overdispersion and for positive scales with heavy tails.- Measured transpilation support for every backend.
docs/transpile-support.mdreports all 46 gallery programs and all 46 construct fixtures across all 11 backends. It distinguishes constructs no backend supports from target-specific refusals and records each raised message. A test regenerates the page and requires the committed copy to match byte for byte, so changes to measured support require a matching page. - Per-backend correctness notes and an architecture guide.
docs/semantics/transpile-correctness/states, for each target, the measure its emitted program denotes and the base measure that separates it from the QVR reference.docs/semantics/transpile-architecture.mddescribes the lowering and the renderer surface. - Five gallery examples. A beta-binomial A/B test, a half-Student-t hierarchical model, a Kumaraswamy bounded-outcome model, an LKJ-Cholesky correlation model, and a logistic-noise regression.
Changed¶
- An ungrouped
marginalizereduces over the body, not over each row. A block that carries no index and noover =clause declares one latent, and the rows of a platedobserveinside it are conditioned on that single draw, so its density accumulates the rows before reducing over the latent (programs.md§2.6). The compiler reduced each row on its own and summed, which gives every row a draw the source never declares; on the gallery'shmmthe two differ by 1.5 nats at the ground-truth point and the difference moves with the data. Log densities of ungrouped marginalize models change accordingly. A block that carries a grouping plate is unaffected. - The vanilla-RNN transition is explicit.
vanilla_rnn_lmpreviously hid its transition in a morphism carrying[param_source=mlp]; the model-internal weights prevented every backend from transpiling it. The cell is now a program with a declared step. Its update uses one linear parameter map followed by an affine transform of aLogitNormaldraw; the previous version used a two-hidden-layer network.
[0.17.0] - 2026-07-22¶
Added¶
- A zero-one-inflated beta family for proportions that pile up at their endpoints. Rates, shares, and probabilities are frequently recorded with exact zeros and ones. A beta component has no point mass at either endpoint, so it cannot assign positive probability to these boundary observations.
ZeroOneInflatedBeta(mu, phi, zoi, coi)mixes point masses at 0 and 1 with a beta on the open interval, takingzoias the probability that an observation is an endpoint andcoias the probability that such an endpoint is 1. The continuous part uses the mean-precision parameterisation, somuremains the mean of the beta component andphiits precision. The formula frontend names itzero_one_inflated_beta, with a logit link on the mean and on both inflation probabilities and a log link on the precision.
[0.16.0] - 2026-07-15¶
Changed¶
- A kernel's parameter source defaults to a linear map. A morphism declared
f : X -> Y ~ Familyover continuous spaces reads its distribution parameters from a singlenn.Linear, which is how its arrow reads. A model whose parameters depend nonlinearly on the input requests that with[param_source=mlp], so the fact appears in the source instead of in a default: previously every such kernel carried a two-hidden-layer tanh MLP, and aReal 1 -> Real 1kernel silently held 4418 weights. The gallery is now what it says it is.linear_gaussian_ssmis linear, and an LSTM, GRU or vanilla-RNN gate is one matrix under a cell whose nonlinearity is the sigmoid its program body applies.deep_markov, the VAE's encoder and decoder bodies, the transformer and seq2seq feed-forward sublayers, and the recurrent cells that carry their own tanh now name the MLP they rely on. - Morphism options are checked against the role that reads them. Options were checked against the union of every role's keys and then read per role, so an option belonging to another role passed validation and was dropped in silence.
[scale=]on a family-backed kernel is the case that bit: nothing reads it there, and all 34 uses across the examples configured an init that never existed. Each role now declares the keys its lowering consumes, and a key outside that set is rejected with an error naming the role that does read it. - A default coefficient prior is autoscaled to its column. A column enters a formula's linear predictor as
beta * column, so a prior on the coefficient alone states the scale of its contribution, and one fixedNormal(0, 5)thus means something different for every column. The default's scale is now divided by the column's root-mean-square, which states it in contribution space; an explicit per-name prior is emitted as written. Coefficients stay on their own column's scale, so nothing is transformed back.
Added¶
- Inline distributions take any number of vector-typed parameters. A family may declare vector parameters in any position, and the stacked input is split at each one's declared dim rather than by a single trailing parameter eating the remaining columns.
MixtureNormal(weights, locations, scales), whose three per-component vectors are each shared across the response plate, is expressible. [param_source=<kind>(...)]carries its arguments. The grammar has always parsed the call form andparam_source_from_optionhas always read parenthesised hidden widths, but the compiler decoded the option as a bare identifier and raised on anything else, so[param_source=mlp(16, 8)]died at compile time and the width parser was unreachable from QVR.hidden_dimtakes a sequence of widths. One entry per hidden layer, so[param_source=mlp, hidden_dim=[64, 32, 16]]builds those three; a barehidden_dim=64is the one-layer case. A single number could say how wide an MLP's layers are but not how many of them there were.
Fixed¶
- Every family that has a parameter source can select it.
[param_source=...]is accepted by the compiler for any family-backed kernel, and exactly one of the 28 conditional families could receive it:_make_continuous_morphismpassed the keyword into a constructor that onlyConditionalNormaldeclared, so the other 27 died on aTypeErrorraised inside__init__, with no line or column on it. Every family whose parameters come from a source now takesparam_sourceandparam_source_optionand hands them to the one seam that builds it. The four whose parameters do not (Horseshoe,GaussianProcess,Independent,Transformed) refuse the option at compile time, naming the family. hidden_dimis read or refused, not dropped. It was ignored wheneverparam_sourcewas given, so[param_source=mlp, hidden_dim=32]silently built the default width. It now reaches the source that has layers to apply it to, and a width aimed at a source with none is an error: asking a single matrix how wide its hidden layers should be has no answer, and silence would read as one.- A scalar response is scored against its codomain's event axis. A conditional family's parameters for a
d-dimensional codomain arrive as(N, d). An(N,)response against ad = 1codomain, which is the shape the response placeholder implies, broadcast to(N, N), and because the observe step sums its score the result was a finite log-joint wrong by a factor ofN. The axis is restored, and a response that cannot carry the codomain's event shape is rejected rather than broadcast into one. An inline family keeps the plate layout its parameters already align with. - An unsupplied
viafibration index names itself. A groupedmarginalizereads its index from the observations dict like any covariate, so omitting it is a user error; it surfaced as a bareKeyError. quivers.__version__reads the installed distribution's metadata. It was a literal, and it had drifted a release behind.
Documentation¶
- The Bayesian neural network is a neural network. The example composed four latent morphisms under an algebra whose composition is matrix multiplication, so with no pointwise nonlinearity the chain collapsed to a single linear map; its fit drove a learnable input morphism at pure noise and reported a residual worse than predicting zero, its NUTS block named an
xand anobservationsno earlier block defined, over a composite carrying no likelihood to sample, and its walkthrough placed matrix-normal priors through a syntax that does not parse. It is now a Bayesian MLP for nonlinear regression whose conditional-Normal kernel emits both a mean and a log-scale, fit to a sine wave that no linear model can represent and scored against the best least-squares line under a matching Gaussian. - The Gaussian mixture is scored per row. The example grouped a per-row component assignment under a
marginalizeblock keyed by a fibration index, which read as an item-grouped mixture and carried a discrete latent no guide could biject.MixtureNormalintegrates the assignment out in closed form. ParamSourceis documented. Its eight sources and the[param_source=...]option had no API page, despite being where a kernel's dependence on its input is computed.
[0.15.0] - 2026-07-02¶
Changed¶
Grammar surface¶
- Optional option blocks and a default role.
morphismandcontractiondeclarations no longer require an option block, and a morphism withoutrole=is a kernel. The other roles (latent,observed,embed,discretize,let) remain explicit.morphism f : A -> Bis now a complete declaration. - Brace-delimited constructor options. Continuous-space constructors take keyword options in braces:
Real 1 {low=-1.0, high=1.0}. A trailing[...]always belongs to the enclosing declaration, so the constructor-versus-declaration attachment of an option block is decided by syntax rather than by parse-order luck, and the grammar is now conflict-free. - Signed and scientific numeric literals. Option values, option-list items, option-call arguments, and constructor keyword values accept signed numbers; floats admit trailing-dot (
1.), leading-dot (.5), and exponent (1e-3,2.5e-3) forms. - One turnstile. Top-level
ruledeclarations state their conclusion with|-(or⊢), the same marker deduction rules use. - Homogenized declaration headers.
bundle NAME : [...]anddecoder NAME : SIGuse the same:connective as every other declaration; the composition level moves into the option block (composition NAME [level=algebra]). definebinds morphism expressions. The top-level value binding isdefine NAME = EXPR, withwhereblocks of nested defines scoped to the binding; the program-stepletbinds tensor arithmetic. The two binding forms no longer share a keyword.- Plural-name declarations.
object A, B : V,morphism f, g : A -> B, and lexicon entries"a", "an" : CAT = LFdeclare one item per name with shared shape and independent parameters, matching the existingcategorylist form. - Parenthesized variable patterns.
sample (a, b) <- fandreturn (a, b)share one tuple shape;observeaccepts the same pattern and reports a clear arity error for tuples, which its runtime does not support. - Keyword-led encoder rules. Encoder constructor rewrites carry a leading
op(op App(fun, arg) |-> ...), so an operator nameddimorinitcannot shadow the sibling entry keywords. - Composition operators. Sequential composition is
>>and<<(the same pipeline written right to left), transformation composition is>>>, and the tensor product is@. The algebra-tagged operator family (>=>,*>,~>,||>,?>,&&>,+>,$>,%>) is gone; algebra-tagged composition is expressed with.change_base(...)or acompositiondeclaration. - Migration.
qvr migrate --from v0.14.0 --to v0.15.0rewrites sources across all of the above with byte-preserving span edits; the hop is registered on the migration chain with full coverage of the removed rules, and every repository.qvrfile and fenced doc block is migrated. When a source cannot be migrated (a removed compose operator has no rewrite), the CLI reports the location and exits non-zero rather than raising.
Packaging¶
- The QVR grammar ships vendored. Parsing goes through the
qvrgrammar bundled inpanproto-grammars-all; the floor moves topanproto>=0.58.0andpanproto-grammars-all>=0.58.0, which vendor the current surface and surface tree-sitter's inserted (MISSING) tokens to the walker.
Diagnostics¶
- Malformed input is rejected, never reinterpreted. Parsing fails loudly on any damaged span anywhere in the tree, with the innermost offending token's line, column, and source snippet. Inputs that previously parsed to a silently different model, such as
[low=-1.0]dropping its sign,[scale=.5]reading as5.0, a mis-bracketedsamplestep vanishing from the program, or a declaration truncated by an unbalanced{or[, now raiseParseErrorat the exact position. - Closed option-key sets with suggestions. Every declaration and step kind validates its option keys; an unknown key raises at the entry's own position with a did-you-mean suggestion and the valid set, and a constructor key such as
lowon a declaration adds the hint to attach it with braces on the codomain. - Named failure for a missing
return. A program body without a return step reports that directly instead of leaking an internal registry message, and aProgramholding only parametric templates raises a typed error naming the template and its instantiation call when.domainis touched.
Added¶
- A complete source emitter.
module_to_sourcecovers every AST node kind, and a round-trip suite asserts that parsing, emitting, and re-parsing reaches a byte-identical canonical fixed point over every repository.qvrfile and fenced doc block. LSP formatting uses it directly. - Four gallery examples with end-to-end tests. A schema-bundled categorial chart parser (
schema,bundle,parser(...),chart_fold(...)), a bilinear tensor contraction (contractionwith operadic three-way wiring), a term autoencoder (signature,encoder,decoder,loss), and parametric partial pooling (typed program parameters, labeled return tuples,scoresteps,exportselection). Tests also cover file-loaded and plural-word lexicons,define ... where, doc comments,.curry_left/.curry_right/.trace, andfrom_datatensors flowing through inference. -
A diagnostics regression suite pinning the rejected-input catalogue and message quality, including line and column accuracy.
-
Editor and highlighting support. The tree-sitter corpus (fifty cases), the VS Code and Zed extensions, and the Pygments lexer that renders
qvrblocks in the docs all track the current surface.
Fixed¶
parser(...)andchart_fold(...)keyword arguments. The walkers read argument keywords from anonymous-token field constraints; every argument was previously dropped, so no surface form of either expression could compile.<<composition compiles as the reversed pipeline of>>, and reverse chains expand right to left in the transpiler.- Cyclic deductions converge in linearly many agenda pops. The FIFO agenda merges contributions pushed for an item that is already pending via the semiring's plus, so a contractive cycle reaches its
tolerancefixed point with at most one queue entry per item per wavefront; previously every contribution was enqueued separately and the pending-entry count grew geometrically with derivation depth. boundedrule weights carry a joint sub-stochastic cap. Each bounded rule's per-firing factor is strictly below one over the count of the deduction's bounded rules, so the total mass an item can push through them stays below 1 and interlocking cycles (e.g. an introduction / elimination pair over nested constructors) stay contractive for every parameter value; the previous per-rule cap of 1 left such systems free to diverge under fitting.
[0.14.1] - 2026-07-01¶
Fixed¶
- v0.11.0 -> v0.14.0 grammar migration hop. The
CHAINinsrc/quivers/cli/migrations/__init__.pynow includes"v0.14.0", with the accompanying hop modulev0_11_0_to_v0_14_0.pyand a matching entry inMIGRATORSandCOVERAGE.qvr migrate --from v0.11.0 --to v0.14.0composes cleanly;qvr migrate --checkreportsadded_rules: family_call_arg, list_arganduncovered_removed: []for the pair. The hop is byte-identity: v0.14.0 adds two productions (family_call_arg,list_arg) to the_draw_argchoice as strict grammar extensions, so every source that parses under v0.11.0 also parses under v0.14.0. Thegrammars/qvr/vcs/parsers/v0.14.0/tree-sitter parser snapshot and the panproto VCS object store undergrammars/qvr/vcs/.panproto/are rebuilt viapython grammars/qvr/vcs/build_schemas.py --resetandpython grammars/qvr/vcs/build_parsers.py.
[0.14.0] - 2026-06-30¶
Added¶
Distribution families¶
-
OrderedLogisticdistribution + DSL inline observe surface. PyTorch ships noOrderedLogistic. The hand-rolledOrderedLogisticimplements the cumulative-link form \(P(Y=k \mid \eta, c) = \sigma(c_k - \eta) - \sigma(c_{k-1} - \eta)\) via sigmoid-difference probabilities gathered at the observed category. Broadcasting handles three cutpoint shapes uniformly: shared(K-1,), per-row(batch, K-1), and arbitrary leading batch dimensions. Wired into the inline path via_FAMILY_BUILDERS["OrderedLogistic"]with a(predictor, cutpoints)param schema and_FAMILY_SUPPORTS["OrderedLogistic"] = nonnegative_integer. The canonical ordinal-mixed-model program (per-participant cutpoints gathered through a participant index) compiles and traces:program ord : Resp -> Resp sample eta <- Normal(0.0, 1.0) let row_cuts = cutpoints[participant_idx] observe y : Resp <- OrderedLogistic(eta, row_cuts) return y
The host supplies cutpoints as a (num_participants, K-1) tensor and participant_idx as a per-row long tensor; the inline log-prob broadcasts the gathered per-row cutpoints against the predictor.
- ZeroInflatedPoisson and HurdlePoisson distributions. Two count families brms / Stan users routinely reach for, neither shipped by torch.distributions. ZeroInflatedPoisson(pi, lambda) implements the mixture \(P(Y=0) = \pi + (1-\pi)\,e^{-\lambda}\), \(P(Y=k) = (1-\pi)\,\text{Poisson}(k\mid\lambda)\) for \(k > 0\). HurdlePoisson(pi, lambda) implements the two-stage hurdle \(P(Y=0) = \pi\), \(P(Y=k\mid Y>0) = \text{Poisson}(k\mid\lambda)/(1 - e^{-\lambda})\) for \(k > 0\). Both ship log_prob, sample, and mean over arbitrary batch shapes; both register inline and conditional (ConditionalZeroInflatedPoisson, ConditionalHurdlePoisson over MLP(x) -> (sigmoid pi, softplus rate)).
- MixtureNormal distribution. Finite Gaussian-mixture with per-row weights, locations, and scales. log_prob uses torch.logsumexp over log w_k + log Normal(loc_k, scale_k), sample dispatches through a Categorical / Normal pair. Conditional path ConditionalMixtureNormal(num_components=K, hidden_dim=...) parameterises all three slots through a single MLP head with softmax / identity / softplus on the appropriate slice.
- Poisson, NegativeBinomial, Binomial registered as conditional families. ConditionalPoisson, ConditionalNegativeBinomial, ConditionalBinomial are now wired into _FAMILY_REGISTRY; morphism f : A -> B [role=kernel] ~ Poisson and the like compile. The formula frontend's family="poisson" | "negative_binomial" | "binomial" paths route through them cleanly, and matching zero_inflated_poisson, hurdle_poisson, and mixture entries land in quivers.formulas.family.families.
Compositional measure algebra¶
- Compositional measure algebra at the DSL and Python surfaces. Distribution families are built by composing five primitive operators from the (sub-)Giry monad rather than by enumerating a Cartesian product of (operator x base family):
PointMass(x)— Dirac measure atx, the unit \(\eta\) of the Giry monad.Restrict(D, low, high)— restriction ofDto[low, high], the sub-Giry monad's natural operation. Does not renormalise.Pushforward(D, b)— pushforward through aBijector, the Giry monad's functoriality on measurable isomorphisms.Mixture(weights, components)— n-ary convex combination, the unique algebra structure on the Giry monad's Eilenberg-Moore category.Independent(D, n)— declare the lastnbatch dims as event dims (strong monoidal product).Normalize(D)— collapse a sub-measure to a probability measure; right adjoint to inclusion.
Lazy normalisation: every operator returns a Measure whose symbolic log_normalizer() propagates through composition, and Normalize (or the implicit observe / sample boundary) collapses it. The discipline is the partial Markov categories axiomatisation made operational; the closure-under-composition story is the Hakaru / MeasureTheory.jl / Scibior et al. 2018 insight that unweighted measures provide the universal type.
-
Bijectorlibrary atquivers.continuous.bijectors. Each bijector exposesforward/inverse/forward_log_det_jacobian/inverse_log_det_jacobian, all in log space and stable in tails.Identity,Exp,Log,Sigmoid,Logit,Softplus,Affine,StickBreaking,Compose,Inverse. Engineering pattern follows TensorFlow Probability'sBijector. -
QVR grammar extension for distribution-valued draw args. The
_draw_argrule grows two productions: -
family_call_argfor nestedFamily(...)expressions:observe y <- Mixture([0.3, 0.7], [PointMass(0.0), Poisson(2.0)]) observe y <- Restrict(Normal(0.0, 1.0), 0.0, 1.0) observe y <- Pushforward(Normal(0.0, 1.0), Exp) -
list_argfor[item, item, ...]literals in mixture weights and component lists.
The parser walks these to new tagged DrawArg AST variants (DrawArgDist, DrawArgList, DrawArgName, DrawArgScalar) so the compiler recurses on call-shaped args and builds the inner measure before passing it to the outer family's constructor.
- Compiler sugar table at
quivers.dsl.compiler.sugarthat desugars the brms-style named families to the canonical operator form at parse time when every argument is a constant literal: TruncatedNormal(μ, σ, a, b)→Restrict(Normal(μ, σ), a, b)HalfNormal(σ)→Restrict(Normal(0, σ), 0)HalfCauchy(σ)→Restrict(Cauchy(0, σ), 0)HalfLaplace(σ)→Restrict(Laplace(0, σ), 0)HalfStudentT(ν, σ)→Restrict(StudentT(ν, 0, σ), 0)
Sugar calls with free-variable arguments route through their dedicated inline family entries (ZeroInflatedPoisson, HurdlePoisson, MixtureNormal, etc.), which compose the same Mixture / Restrict / PointMass operators internally. Source can be either form; the compiler canonicalises to the operator form when it can and the pretty-printer re-sugars on the way out.
- Four compiler rewrite rules on
Mixture, each backed by a literature result: Mixture.flatten()— Giry monad associativity (\(\mathrm{Mix}(\alpha, \mathrm{Mix}(\beta, A, B), C) \equiv \mathrm{Mix}(\alpha\beta, \alpha(1-\beta), 1-\alpha; A, B, C)\)). Closes the PyMC nested-mixture gap.Mixture.pushforward_inside(b)— functoriality of the Giry monad on isos: \(g_*(\sum_k \pi_k\,\mu_k) = \sum_k \pi_k\,g_*\mu_k\).Mixture.restrict_to(low, high)— the correct Mixture-Restrict non-commutation identity, reweighting by per-component truncation mass and wrapping the per-component restrictions inNormalize. Surfaces the modelling distinction Welsh et al. 1996 flag at the operator level.Mixture.lift_point_masses()— surfacesPointMasscomponents as the Bernoulli-style branch the ZIP / hurdle canonical factorisation expects (the Lambert 1992 / Mullahy 1986 shape derived from the operator combination rather than baked per family).
Pluggable parameter sources¶
-
ParamSourceABC atquivers.continuous.param_source. EveryConditionalXfamily accepts aparam_source=override in place of the default two-layer MLP. Concrete sources:LinearSource(matches the transpile backends' single-matmul emit exactly),MLPSource(hidden_dims, activation)(the parameterised default),LookupSource(n_entries, param_dim)(discrete-domain table),EmbeddingSource(n_entries, embed_dim, head)(embedding + downstream head),AttentionSource(num_heads)(self-attention over the input dimension),IdentitySource(parameters supplied as data),FunctionSource(fn, param_dim)(wraps anynn.Moduleor callable),ComposeSource(outer, inner)(categorical composition). The DSL surface reads[param_source=<kind>[(...)]]onmorphismdeclarations:morphism trans : State -> State [role=kernel, param_source=linear] ~ Normal morphism trans : State -> State [role=kernel, param_source=mlp(64, 64)] ~ Normal morphism attn : Token -> Hidden [role=kernel, param_source=attention(heads=4)] ~ Normal
-
Parameter transforms unified with the bijector library.
quivers.continuous.param_transformsshipsTRANSFORM_TO_BIJECTORandINLINE_CLAMP_TO_BIJECTORregistries mapping every historical string key ("id","sigmoid","softplus","softplus_shifted","exp") to aBijectorinstance.ParamSpec.transformaccepts a string key or aBijectordirectly._make_familyroutes raw parameter tensors throughbijector.forward, soinverse,forward_log_det_jacobian, andinverse_log_det_jacobianare available on the guide and pushforward paths.
Variational objectives¶
- Three new objectives on the
ObjectiveABC. Each is a subclass alongsideELBO,IWAEBound,RenyiBound,VRIWAEBound; each accepts the sameGradientEstimatorstrategy attribute as the existing objectives.ChiVIminimises the chi-squared upper bound onlog p(y)via the CUBO surrogate (Dieng et al. 2017). Useful for posterior calibration and for sandwich estimates with the ELBO.RWSimplements reweighted wake-sleep with a wake-theta phase on the model and a wake-phi phase on the guide (Bornschein and Bengio 2015). Handles discrete latents where the reparameterization trick does not apply.DReGsBoundpairs the IWAE bound with the doubly-reparameterised gradient surrogate (Tucker et al. 2019). Removes the score-function term whose signal-to-noise ratio collapses withKin naive reparameterised IWAE. Distinct from theDoublyReparameterizedgradient-estimator strategy: the estimator is the scalar gradient rule,DReGsBoundis the (bound + estimator) pair for callers that want to switch both together.
Autoguide combinators¶
AutoGuideListconcatenates disjoint per-block guides so a single model can hold, for instance,AutoNormalon local variables andAutoMultivariateNormalon a small global block.AutoStructuredadmits per-site conditional choice (delta / normal / mvn) plus per-edge dependencies (linear affine or a user-supplied callable) walked in ancestral order.- Pyro-style short-name aliases (
AutoNormal,AutoMultivariateNormal,AutoLowRankMVN,AutoDelta,AutoLaplace,AutoIAFNormal) re-exported fromquivers.inference.guidesas thin aliases for the pre-existingAuto*Guideclasses.
Algebraic effect handlers¶
quivers.effectspackage:EffectHandlerABC with a thread-local handler stack and three-phaseapply_stack(msg, default=...)dispatch, plusrun_programthat walks aMonadicProgramthrough the active stack. The design mirrors the Pyropoutine/ NumPyrohandlersMessengerprotocol (Plotkin and Pretnar 2009, Scibior et al. 2018).- Handlers:
TraceHandler,clamp(data)(poutine-style pin of sample sites; the name avoids the collision with the top-levelconditionfactory that returns aConditionedmodel wrapper),do(data)(Pearl intervention),mask(mask_tensor),scale(factor),block(names),replay(trace),lift(prior_scale),collapse(). The handlers compose:clamp + scale + maskis the subsampled-mini-batch likelihood pattern;do + traceis intervention-then-record. -
The
EffectHandlerABC is distinct fromquivers.monadic.algebraic.Handler.EffectHandleris a mutable-message dispatcher for runtime interception of aMonadicProgram;monadic.algebraic.Handleris a free-monad-over-signature interpreter that folds a bounded-depth signature tree into a target monad. Different territories; both remain. -
Reparameterisation strategies at
quivers.effects.reparamwrap sample sites and rewrite their distributions:LocScaleReparamfor the centred / non-centred rewrite of Normal(loc, scale) sites,TransformReparam(bijector)driving change-of-variables through the bijector library,NeuTraReparam(autoguide)warping HMC geometry through a trainedAutoIAFGuide,ConjugateReparamfor analytic conjugate collapse. Thereparam({name: strategy})orchestrator wires per-site strategies into the effect stack:with reparam({"theta": LocScaleReparam(), "z": NeuTraReparam(guide)}): samples = nuts.run(model, x, observations)
Diagnostics for any fit type¶
to_datatree_from_sviandto_datatree_anyatquivers.diagnosticsdispatch on fit type. Every ArviZ downstream (loo,waic,plot_trace,compare) works uniformly onMCMCResult,Guide/Predictivedraws, or(samples, log_densities)tuples.
Extensible transpile IR¶
quivers.transpileships the target-agnostic scaffold: theIRNodetagged-union IR withConstraintSpectaxonomy,IRArgvariants,Dim/Plateshape metadata; theLowerAST-to-IR mapping; theRendererBaseshared machinery; theFAMILY_METAper-family transpile metadata registry; and therenderer_registryemit-hook table. Target-specific backend renderers live on their own branches; they register per-(backend, IRNode)emits via@emit_hook(backend_name, node_type).
Changed¶
-
SampleStep.args/ObserveStep.argstype signatures widen fromtuple[str | float, ...]totuple[DrawArg, ...]over the new tagged AST union. The compiler's inline path unwrapsDrawArgName->strandDrawArgScalar->floatat entry so the existing arg-shape machinery continues to handle them;DrawArgDistandDrawArgListare recognised by the operator-family dispatch. -
ZeroInflatedPoisson,HurdlePoisson,MixtureNormalinternals route through the measure algebra: each user-facing class delegateslog_prob/sampleto aMixtureofPointMassand (for hurdle)Normalize(Restrict(...)). The user-facing API is unchanged; the renderer-side IR sees a single Mixture-with-PointMass shape rather than per-family hand-rolled emit logic. -
_make_sourceinquivers.continuous.morphismsaccepts aparam_source=override; every family that constructs its source through_make_sourcegains the same kwarg plus aparam_source_option=string form. -
quivers.inference.trace.tracebecomes a thin wrapper that pushes aTraceHandlerand delegates torun_program. The API is unchanged.
Fixed¶
-
Restrict.log_normalizer()for continuous bases usescdf(low)for continuous bases andcdf(low - 1)(or the survival-sum path for distributions without acdfmethod) for integer-supported bases, matching the standard truncation-mass formula for each support type. -
HurdlePoissonlog-density handles the survival-sum formula forPoisson-restricted-to-{1, 2, ...}via1 - e^{-\lambda}plus a general pmf-sum for arbitrary integer intervals. Works aroundtorch.distributions.Poisson's absentcdf. -
OrderedLogistic.sampleaccepts anySequence[int]forsample_shape.torch.distributions.Distribution.sample's contract admits tuple, list, ortorch.Size;sample_shapeis coerced throughtorch.Sizeat the top of the method so every shape form works. -
Inline DSL
observe ... <- OrderedLogistic(predictor, cutpoints)routes a shared cutpoints vector as a distribution parameter.quivers.continuous.inlineships a_PARAM_EVENT_RANKStable declaring each family parameter's event rank (0 = per-row scalar, 1 = shared-or-per-row vector)._resolve_inputreads the table through the wrappingVectorisedObserve._param_event_ranksproperty and broadcasts rank-1 shared vectors to per-row shape before stacking;MixedInlineDistributiontakes aparam_event_ranksargument and lets the trailing vector-typed slot consume the remaining columns of the stacked input. CoversOrderedLogistic(predictor:(N,), cutpoints:(K-1,) or (N,K-1))plusMixtureNormal,ZeroInflatedPoisson, andHurdlePoisson. -
DSL quick-start docstring uses grammar keywords.
quivers.dsl's module docstring'sloads(...)example compiles against the canonicalKIND NAME : SIGNATUREsurface (program NAME : A -> Bwithsample/observe/returnsteps and a top-levelexport NAME), nolatent/output/alias(none of which are grammar keywords). -
REPL/LSP guide examples use the current morphism surface.
docs/guides/repl-and-lsp.mdshowsmorphism NAME : ... [role=latent]for latent-role morphisms andobject NAME : FinSet Nfor finite-set declarations.
[0.13.0] - 2026-05-22¶
Added¶
- GHCi-style
:typeand:kindcommands in the REPL.:typeaccepts an expression (morphism, program, deduction, scoped sample / observe / let / marginalize / return site, contraction, rule, encoder / decoder / loss, bundle, transformation) and prints the GHCi-shaped one-line signaturename :: A -> B(orname :: (Real, Real) => Word -> Wordfor parametric programs, with the Π-bound parameter universes in a Haskell-style constraint context).:kindaccepts a type-level binding (object, space, signature, category) or a bare type expression (FinSet 3,A * B) and prints the kind. Each command rejects the wrong sort with a redirect::type Doc→ "Doc is a type, not an expression; use :kind Doc". The bare-expression fallback tries:typefirst, then:kind, so users can keep typing a name on its own. Aliases:tand:kwork as before. - User-given type aliases preserved across the REPL. The compiler internally resolves
object Item : FinSet 200toFinSet(name='_FinSet_200', cardinality=200). The REPL now builds anid(obj) → user_namemap per session and renders morphism signatures through it, so:type XprintsX :: Item -> H_inrather thanX :: FinSet 200 -> FinSet 4. The map recurses into product / coproduct components, so a mixedDoc * Topicstill surfaces both user names. render_signature(compiler, name)shared helper inquivers.cli.repl_session. Single entry point for the GHCi-style signature line, used by both the REPL's:type/:kind/ bare-expression fallback and the LSP's hover panel — so a binding looks the same in the TUI and in an editor.- LSP hover leads with a
**Type**/**Kind**block. Hover now shows the GHCi signature first (same string:type/:kindwould print), then the doc comment, then the verbatim QVR source slice, then the collapsed AST. Editor users get the one-glance signature that REPL users already had. - New semantics chapter:
docs/semantics/typing.md. Chapter 8 in the semantics development, presenting the QVR type system as a proof calculus: syntactic categories (kinds, type expressions, families, term expressions, statements, programs); contexts (\(\Gamma\), \(\Delta\), \(\Phi\)); judgments (\(\Gamma \vdash \tau : \kappa\), \(\Gamma; \Phi \vdash e : A \rightsquigarrow B\), \(\Gamma; \Phi \vdash F(\bar a) : \mathsf{Kernel}[\Phi, B]\), \(\Gamma; \Phi \vdash s \dashv \Phi'\), \(\Gamma \vdash p : (\Delta) \Rightarrow A \rightsquigarrow B\)); the full inference-rule set; the four structural lemmas (Weakening, Substitution, Type Uniqueness, Inversion); the Soundness theorem against the denotation with proof sketches for the representative Compose / Bind / Marginalize / Prog cases; the Subject-Reduction theorem for parametric instantiation; a Completeness theorem (every phrase with a defined raw denotation has a derivation) with a Decidable-Type-Membership corollary on the non-residuated fragment; the bidirectional algorithm underlying the compiler's typechecker; and a worked example deriving the LDA program's full judgment.
Changed¶
- Constraint context in parametric programs drops binder names. A program with typed parameters renders as
lda :: (Real, Real) => Word -> Word, notlda :: (alpha : Real, beta : Real) => Word -> Word— Haskell convention. Parameter names are accessible through:info lda. :typeof a parametric program no longer curries the parameters with the kernel arrow. Perdocs/semantics/programs.md §3a, typed parameters denote a dependent family \(\prod_{p:P} \mathbf{Kern}(\mathrm{dom}, \mathrm{cod})\) — they live in a \(\Pi\), not in the kernel's domain. Rendering them asReal -> Real -> Word -> Word(the previous "GHCi-curried" form) conflates the \(\Pi\)-context with the kernel arrow.- Compiler-driven expression typing in
:type. The expression-fallback path now drivesCompiler._compile_exprdirectly on the already-compiled session state instead of re-running the full module compile for every:typequery. - Auto-generated placeholder names stripped from output. The compiler assigns synthetic names like
_FinSet_20and_Real_8to anonymous FinSets and Euclidean spaces. They no longer leak into:type/:kindoutput asDoc : _FinSet_20; the stripper renders them asDoc : FinSet 20. - Help-text rewrite for
:typeand:kind. Both commands' inline:helpentries and detailed:help :type/:help :kindtext now match the GHCi-style semantics.
Documentation¶
docs/semantics/typing.mdadded as chapter 8; index renumbered.- Semantics corpus audit (substantive). After threading
typing.mdinto the development: - Cross-link
setting.md §6,morphisms.md, andprograms.mdinto the new typing chapter at their natural touch-points. morphisms.md §1.1gets a "Categorical structure" proposition under the strict-quantale hypothesis, proving \(\mathcal{V}\text{-}\mathbf{Rel}\) is a symmetric monoidal category using the join's universal colimit property, \(\otimes\) associativity, \(\bot\) absorption, and the distributivity law; flags the lax cases of \(\mathcal{V}_{\mathrm{pf}}\) / \(\mathcal{V}_{\mathrm{L}}\) explicitly.programs.md §5upgrades the monad-law table to a "Monad laws for QVR programs" theorem with proofs derived from the Giry monad's universal property (Giry 1982, Kock 1972, Fritz 2020). The commutativity equation is now stated with an explicit double-strength pair \(\mathrm{dst}_1\) / \(\mathrm{dst}_2\) rather than the previous ambiguous one-line formula, identifying Fubini–Tonelli as its substance.adequacy.md §3.4upgrades the one-liner "the proofs are straightforward" to a full "Adequacy of@" proposition with term-by-term unfolding; the remaining five tensor combinators get a table whose every row makes the proof template explicit.effects.md §4aadds explicit inference rules for the four chart firings (Base,Lift_T,Handle_{T→S},Eliminate_T,Swap_{T|U}), turning the previously prose-only joint type-and-effect dispatch into a proof system.- Stale class-name references across the corpus updated:
TypeExpr→ObjectExpr,TypeProduct→ObjectProduct,TypeEffectApply→ObjectEffectApply(the AST class was renamed and the docs had not tracked it). adequacy.md §3.7references the real method names (_compile_program, not_compile_program_body; dispatch keycompose, notcompose_deductions).adequacy.md §4: drop references totests/test_resolution_lenses.py, which no longer exists.- British → American spelling sweep across all 14 semantics chapters. marginalisation → marginalization, realise → realize, behaviour → behavior, catalogue → catalog, modelling → modeling, analyse → analyze, etc.
Fixed¶
Compiler.signatures,Compiler.categoriesnow exposed to the REPL's bare-name lookup. Previously,:type SomeSignaturereturned "unknown name" because signatures and categories were not in either the value-level or type-level buckets the REPL consulted. They are now classified as type-level (a signature denotes a generalised algebraic theory; a category denotes an atom universe) and respond to:kind.- Contraction and rule signatures render correctly under
:type. Contractions get an operadicc :: (A_1, …, A_k) -> Bsignature; rules get ther :: prem |- conclschema form.
[0.12.0] - 2026-05-21¶
Added¶
::scope paths everywhere a binding name is taken.:info,:type,:doc,:browse,:plate,:graph,:where,:effects,:shapeall accept a::-separated scope path. The leftmost segment is a top-level binding; each subsequent segment looks up a named child in that binding's scope. Coverage across every container kind:program(typed parameters + sample / observe / let / marginalize / return steps; recursing into nested marginalize bodies),deduction(rules / atoms / lexicon entries),signature(sorts / constructors / binders / vertex_kinds / edge_kinds),encoder(op_rules / init_rules / message_rules / update_rules / var_inits),decoder(per-constructor heads),bundle(member rule names),contraction(input parameter names). Examples:lda::thetaresolves to the sample site inside the lda program;lda::z::wto the observe site inside the lda program's marginalize block;LF::Termto the Term sort inside the LF signature;CCG::fwd_appto the forward-application rule inside the CCG deduction.quivers.analysis.scopemodule. NewScopedRefvalue type,resolve_scoped_path(compiler, path)resolver,scope_children(ref)view,find_all_references(compiler, name)reverse lookup. Foundation for every scope-aware REPL feature.- Env-tree click navigation on every nested node. Each env-tree node now carries its full
::path onnode.data. Clicking any node (top-level binding, program step, deduction rule, signature sort, bundle member) fires:info PATHagainst that exact binding. Previously only top-level bindings were clickable; nested children either did nothing or raised "undefined morphism" errors when the click handler tried to dispatch against the rich signature label. - Scope-aware tab completion.
lda::completes against the lda program's children;lda::z::completes against the marginalize block's inner scope; a bare-name prefix likethetsurfaces every scoped descendant whose final segment matches (lda::theta). quivers.analysis.plate_graphmodule. Extractor that walks a compiled QVR program and builds aPlateGraphcapturing nodes (latent / observed / marginalized / deterministic), plates (with nested parents), and dependency edges. The grouping plate of a marginalize block propagates correctly to nested observes via the[over=G]axis; the[via=idx]fibration index is correctly treated as a routing label rather than an additional plate. Handles nested marginalize blocks recursively.:plate PROGRAMmeta-command with five output formats: default Rich table (kind / variable / family / plates / parents),--mermaid(Mermaidgraph TDsource withsubgraphclusters per plate),--dot(Graphviz DOT withcluster_*subgraphs),--tikz(LaTeX TikZ + bayesnet snippet),--daft(Python script using thedaftlibrary), and--open(renders via daft or graphviz to a temp PNG and launches the system default viewer; falls back to Mermaid source if no renderer is installed).:graph PROGRAMmeta-command. Vertical step-flow view of a program: one row per sample / observe / let / marginalize / return step with its dependency parents on the side. No 2D edge routing in the in-TUI view so the output is robust on any terminal width. Same--mermaid/--dot/--openflags as:plate.:where NAMEmeta-command. Lists every scope path in the loaded module whose final segment isNAME, sorted by depth. Useful for finding cross-references between programs, deductions, and other declarations.:effects PROGRAMmeta-command. Prints the program's declared[effects=[...]]set alongside the effect set inferred from the body (Sample / Score / Marginal / Pure based on the step kinds present). Flags leak (body uses effects not declared) and unused (declared but not used) divergences.:shape PROGRAMmeta-command. Pretty-prints the program'sChainShape: per-step depth, kind, intermediate axis size, source location. Useful for auditing chain depth before fitting.- Modal
:helpdialog. Promotes:helpfrom "write to the output log" to a proper TextualModalScreenoverlay with its own focus, ESC-to-close, and a live filter input. Lists every meta-command grouped by category (loading / inspection / program exploration / live editing / control) plus the full keybinding table. The TUI'sF1binding pushes the same modal.
Changed¶
- TUI env-tree builder unified. Replaced the 12 per-kind
_children_for_*builders with a single uniform walker that consultsscope_children(). Each node's display label comes from a single per-kind type-line renderer; each node'sdatais its full scope path. Result: every container kind expands to its declared children in a single layered tree without duplicate per-kind logic. Compiler.compile_env()returns every populated bucket. Addsprograms,deductions,signatures,encoders,decoders,losses,bundles,contractions,transformationsto the union, so consumers that walk the env dict see every declared atom.
Fixed¶
Compiler.lossesandcompile_env()calledreg.entries()(parentheses).LossRegistry.entriesis a plain list attribute, not a method. The misplaced call broke every module that declared aloss. The accessors now read the attribute directly.
[0.11.5] - 2026-05-21¶
Fixed¶
qvr replrendered every token in the same colour on customised terminal palettes. TheSTYLE_TABLEused palette-indexed colour names ("bold magenta","bold blue","bold cyan","default"). Terminals whose palettes mapped those four into the same warm hue (common in custom themes) collapsed every span into a single colour, so:info's body looked uniformly red. Switched the table to truecolor#RRGGBBcodes (One-Dark inspired) so palette mapping does not apply and the keyword / type / operator / variable distinctions remain visible regardless of the user's terminal theme.- Clicking a program / deduction / signature / encoder / decoder node in the env tree raised a "compile error: undefined morphism" or did nothing at all. The click handler read the node's display label (
lda(alpha : Real, beta : Real) : Word -> Word) and dispatched:infoagainst that whole string. After 0.11.3 added nested-children expansion, expandable nodes also stopped firing the handler entirely because of anallow_expandgate. The handler now reads the bare binding name from a dedicatednode.dataslot populated at tree-build time, and theallow_expandgate is gone, so a click on any named declaration fires:info NAMEcorrectly.
Added¶
- Comprehensive TUI regression suite at
tests/cli/test_repl_tui.py. 20 tests covering: click-target resolution under every node shape (root / category heading / named binding / nested step / non-identifier data); theSTYLE_TABLEtruecolor invariant; each per-kind nested-children builder; the rendered ANSI for a real:infobody (truecolor codes only, distinct codes per token class, keyword / type colour distinct); the full:typeand:browsesignature lines including typed parameter lists; the completion engine surfacing program names with the correct kind detail; and the TUI module's import-cleanliness on a minimal install.
[0.11.4] - 2026-05-21¶
Fixed¶
:typeon a parametric program dropped its parameter list. Typed program parameters (alpha : Real,beta : Real,G : FinSet,k : Mor[A, B]) live on the AST'stype_paramsslot, not onparams(which holds bare names). The REPL session and the TUI env-tree builder were both reading onlyparams, so loadinglda.qvrand running:type ldaprintedprogram lda : Word -> Wordinstead ofprogram lda(alpha : Real, beta : Real) : Word -> Word; the env tree's program node had the same problem. Both call sites now walk both slots and render the typed parameter list with itsScalarParam/ObjectParam/MorphismParamannotation.
[0.11.3] - 2026-05-21¶
Fixed¶
- REPL environment was missing eight declaration categories.
qvr repl,:browse, and the TUI env browser only surfacedobjects/spaces/morphisms/rules— every other top-level declaration (program,composition,deduction,signature,encoder,decoder,loss,bundle,contraction) compiled successfully but was invisible at the user-facing surface. Loadinglda.qvrshowed three objects and "0 programs" despite the declaredprogram lda. The compiler now exposes public@propertyaccessors (programs,deductions,signatures,encoders,decoders,losses,bundles,contractions,transformations) andcompile_env()returns the full set; the REPL session,:browse, the completer, and the TUI status bar / env tree all surface every populated bucket.
Added¶
- Nested env-tree rendering. The TUI env pane now expands structural declarations to their internal AST shape on click: a
programopens to itssample/observe/let/marginalize/returnstep list (withmarginalizerecursively opening to its body); adeductionopens to itsrules(each labelled with itspremises |- conclusionline) plussemiring/tolerancemetadata; asignatureopens to itssorts/constructors/binders/vertex_kinds/edge_kindstables; abundleopens to its constituent rule names. The plain-text:browsemeta-command renders the same hierarchy as indented text so plain-mode users (qvr repl --plain, Jupyter kernel) see the same structure the TUI shows.
[0.11.2] - 2026-05-21¶
Changed¶
panproto>=0.48.4andpanproto-grammars-all>=0.48.4. Upstream now vendors the homogenized QVR grammar (the unifiedKIND NAME : SIGNATURE [k = v, ...] [~ INIT] [BODY]surface, thecomposition NAME as <level>keyword, the[role=...]option-block roles, the indented-body deductions / signatures / encoders / decoders / marginalize blocks). TheQVR_USE_LOCAL_GRAMMARshim is no longer required for the runtime parser: a fresh wheel install parses every gallery example out of the box.
Fixed¶
qvr repl,qvr-lsp,qvr-kernelparse-error on every.qvrload. 0.11.0 / 0.11.1 againstpanproto-grammars-all 0.48.3(the latest at release time) used a pre-homogenization grammar whoseobject_declnode had noinitfield; the in-tree parser walker raisedParseError: object_decl missing valueon the first homogenized declaration. Withpanproto-grammars-all>=0.48.4the vendored grammar matches the walker's expectations and every example indocs/examples/source/loads cleanly.
[0.11.1] - 2026-05-20¶
Fixed¶
- Wheel shipped without
grammar.json._grammar_introspectionwalks the tree-sittergrammar.jsonto derive the keyword / punctuation / builtin sets the Pygments lexer, the REPL completer, the TUI highlighter, and the LSP semantic-tokens legend all consume. The canonical file lives atgrammars/qvr/src/grammar.json(regenerated bytree-sitter generate), which sits outsidesrc/quivers/and so was excluded from the wheel build. The 0.11.0 install crashed on first import ofquivers.dsl.pygments_lexerwithFileNotFoundError: grammars/qvr/src/grammar.json not found above ..., which in turn blockedqvr repl,qvr-lsp, andqvr-kernel. A package-local copy atsrc/quivers/dsl/_grammar_data/grammar.jsonis now committed and included in the wheel;_grammar_json_path()prefers the package-local copy and falls back to the in-tree source for editable installs.tools/sync_grammar_data.pykeeps the two copies in lockstep, with a--checkmode CI can gate on.
[0.11.0] - 2026-05-20¶
Added¶
- Homogenized DSL surface. Every declaration follows the unified
KIND NAME : SIGNATURE [k = v, ...] [~ INIT] [BODY]skeleton:morphism,object,composition,program,deduction,signature,encoder,decoder,loss,category,bundle,schema,rule,let,export. Per-role morphism keywords (latent,kernel,observed,embed,discretize) collapse intomorphism X : ... [role=ROLE]. Brace-bodied declarations (algebra X { ... },deduction X : ... { ... },signature X { sorts { ... } },marginalize ... in { ... },atoms { ... },lexicon { ... }) become indented bodies under the unified header. Effect signatures live in the option block as[effects = [Sample, Score, Marginal, Pure]]rather than! E1, E2, .... Observation fibrations are[via = idx]and grouping plates are[over = G, reduction = R]in the option block instead of keyword clauses. Morphism replication is[replicate = N]. The pragma surface is#[k = v, ...](outer, decorates the next declaration) and#; per-line rule and lexicon pragmas (#[learnable],#[bounded],parent = ...) replace the legacy@ learnablemarker. composition NAME as <level>keyword. A single surface for algebraic composition rules (algebra / semigroupoid / bilinear_form / rule), with either a registry lookup (composition product_fuzzy as algebra) or an indented inline body (composition my_godel as algebrafollowed bytensor_op(a, b) = ...).ContinuousSpacefamily registry. Eleven continuous-space constructors (Real,Simplex,PositiveReals,UnitInterval,Sphere,Ball,Covariance,Correlation,CholeskyFactor,Orthogonal,Stiefel,LowerTriangular,Diagonal) ship asContinuousSpacesubclasses dispatched by the homogenizedDiscreteConstructor/ContinuousConstructorAST nodes. Discrete and continuous space initialisers share the sameobject X : <init>surface.quivers.inference.liftsmodule. Four general-purpose Bayesian-lift helpers:bayesian_lift_parameterslifts every learnablenn.Parameterinto a Normal-prior sample site, returning aMonadicProgramthat SVI and NUTS consume uniformly. The newadditional_latentsargument lifts intermediate program latents as joint NUTS variables via an exact placeholder-cancellation construction; the lifted log-density equalslog p(theta) + log p_inner(z, y | x, theta)pointwise, with no Monte-Carlo noise across leapfrog steps. The module docstring includes a methodological-notes section justifying the Normal-prior choice (maximum entropy on R^n; equivalence to L2 regularization; computational fit with NUTS) and the placeholder-cancellation theorem with proof.lift_to_bayesian_programlifts a parameter-only morphism plus anytorch.distributions.Distributionobservation family into a Bayesian program, with a user-suppliedlocation_fnso the same lift works forrsample(x)-style,tensor-attribute, andprog(x)-forward shapes.lift_from_log_problifts a parameter-only model whose forward is already alog_prob(x, y)function (the VAE / encoder-decoder pattern).monte_carlo_log_jointwraps a program so itslog_jointMC-draws named intermediatesamplesites and returns the conditional data likelihood, with the prior-cancellation correction applied automatically. Documented honestly as a single-sample MC estimator valid for SVI as a stochastic gradient estimator; the function's docstring notes that for rigorous NUTS the joint lift viaadditional_latentsis the correct route.quivers.stochastic.deductionpackage. Reorganised the deduction-system surface from the previous flatquivers.deduction_fitmodule into a coherent package with one canonical import path. Subpackages:primitives(the abstractAxiom,Deduction,Goal,Schedule,DeductiveSystembuilding blocks),fit(point-estimate gradient fitting viaadam_fit_deduction),bayes(posterior wrapping vianuts_program_from_deduction),sample(exact length-conditional forward sampling viasample_corpus). The model typeDeductionSystemis re-exported from the package root alongside the operations andbayesian_lift_parameters(re-exported fromquivers.inference.lifts).ScanMorphism.log_jointaccepts a dict signature. The recurrent-cell scan morphism'slog_jointnow accepts the hidden-state trajectory either positionally as a tensor or via a{state_key: tensor}dict (defaultstate_key = "h"), so the standard inference contractlog_joint(x, observations: dict)works without an adapter at the call site.qvr migratesubcommand. Forward-migrates user.qvrsource files across the QVR grammar release chain (v0.2.0 through HEAD). The CLI composes the adjacent-pair hop migrators inquivers.cli.migrationsautomatically;--from VERand--to VERpin the boundary,--dry-runreports without writing,--output DIRwrites migrated copies.--checkmode validates the migration chain against the panproto VCS schema diff and reports any rule removed in an adjacent grammar revision that the corresponding hop migrator has no converter for.- In-tree panproto VCS for the grammar.
grammars/qvr/vcs/.panproto/holds a content-addressed schema chain with one tagged commit per grammar release (v0.2.0throughv0.11.0).grammars/qvr/vcs/build_schemas.pywalks tagged grammar revisions, builds apanproto.Schemakeyed by rule name, and commits each as a tagged VCS commit. The migration system uses these commits to derive coverage reports and detect uncovered removed-rule gaps in any adjacent-pair migrator. - Comprehensive
## Try itsections in every example doc. All 28 gallery examples (regressions, latent-variable models, state-space and time-series, language models, encoder-decoder, weighted deductions) ship a verified three-block runnable Try-it section: synthetic-data generation under known true parameters, an SVI fit, and a Bayesian posterior block. Every block executes in CI viatest_gallery_try_it_blocks_execute. Illustrative-budget caveat at the top of each section explicitly notes the SVI / NUTS budgets are sized for tens-of-seconds CI runs, not production convergence. - Example walkthrough alignment. All 28 example docs'
## Overviewand## Walkthroughprose audited against the actual.qvrsource; the 13 docs with prose-vs-source drift (most severelypcfg.mdandmontague-nli.md, whose embedded## QVR Sourceblocks described entirely different models than the actual source) were rewritten in place. - Examples gallery index restructured as a flat grouped list of one-line example descriptions linking to each gallery doc.
- API reference structure expanded. New pages for
api/inference/lifts,api/inference/mcmc,api/inference/elbo,api/continuous/scan,api/continuous/inline,api/stochastic/agenda,api/stochastic/deduction(+ four subpages:primitives,fit,bayes,sample). Theqvr migrate,qvr repl, andqvr lspsubcommands are documented inapi/cli.md. - Denotational semantics extensions.
docs/semantics/programs.mdgains a §7 "Bayesian lifts" with five propositions and proofs (placeholder-cancellation for the parameter lift, the observation-family lift, the log-prob lift, the single-sample MC bias bound via Jensen, the CRF-vs-PCFG distinction fornuts_program_from_deduction).docs/semantics/adequacy.mdgains a §3.7b on adequacy of the lift theorems.docs/semantics/types-and-spaces.md§1 and §4 cover all 13ContinuousSpacevariants with set-builder denotations.docs/semantics/grammar.mdrewritescomposition_rule_declEBNF to match the unified surface.docs/semantics/expressions.md§3.4 notes the dict-signature route onScanMorphism.log_joint. qvr migratedocumentation.docs/guides/migration.mdcovers the--from/--to/--dry-run/--output/--checkflag set with worked examples.
Changed¶
quivers.deduction_fitmodule removed. Imports move toquivers.stochastic.deduction(orquivers.inference.liftsforbayesian_lift_parameters). Pre-1.0 clean break, no compat shim. The 20+ docs and tests that referenced the old path are updated.- Docstring cross-references converted to mkdocstrings autorefs. All 1103 Sphinx-style
:func:X/ `:class:`X/:mod:X/ `:meth:`X/:attr:X/ `:data:`X/:exc:X`` role markers in source docstrings (132 files) are now either mkdocstrings autorefs (for internal symbols the docs build resolves) or plain inline code (for external / unresolved references). Sphinx role markers no longer render as ugly grey code spans in the API docs. - DSL surface drift across the entire test and doc corpus. Every fixture, every embedded source string, every walkthrough prose reference is on the current homogenized surface.
mkdocs build --strictproduces zero warnings;pytest tests/reports 2009 passing, 140 skipped, zero failing.
Fixed¶
- Grammar parser-recovery bugs.
encoder_declandcomposition_declpreviously usedoptional(body), $._newline, which let the trailing$._newlineswallow the next top-level statement after an indented body. Replaced withchoice(body, $._newline)so the$._dedentproperly terminates and the following statement parses.parser.c,grammar.json, andnode-types.jsonregenerated. ScanMorphism.log_jointdict-vs-positional adapter unified into the morphism itself so call sites no longer need aDictWrapshim._TruncNormdistribution shim inquivers.continuous.inlinenow carriesevent_shapeandbatch_shapeattributes so the generic family-log_probpath treats it uniformly with stocktorch.distributionsfamilies.- Formula compiler (
quivers.formulas.compile) emitsobject Resp : FinSet Nvia the homogenizedDiscreteConstructorinitialiser and accepts both old and new shapes on the reverse-pass decoder. - Dataset schema (
quivers.data.schema) emitsobject X : FinSet Nin the prelude returned fromDatasetSchema.declarations_qvrand thecomposewrapper. - Compiler template-locals walker (
quivers.dsl.compiler.programs._collect_template_local_names) now walks the surfaceSampleStep/ObserveStep/MarginalizeStepnodes (was only walking the legacyBindStepIR), so locally-bound names in parametric program templates are correctly captured during alpha-renaming. - Compiler structural pass (
quivers.dsl.compiler.structural._compile_signature) readsdimandvocabfrom each sort and vertex-kind'soptionsblock (the homogenized surface) rather than from removed direct attributes._compile_lossaccepts numeric-literalweight = Nin the loss option block. docs/developer/homogenization-plan.mdremoved. Internal design-tracking doc with phase counts and version anchors; not appropriate for shipped documentation.
Editor packages¶
- VS Code extension
editors/vscode-qvrbumped to 0.6.0. Syntax highlighting refreshed for the homogenized grammar (the unified declaration keywords, the option-block surface, the new pragma markers, thecomposition NAME as <level>keyword, thesample/observe/score/marginalize/bindersprogram-step keywords). Tracks the new sharedSTYLE_TABLEso VS Code, the REPL TUI, the Pygments lexer, and the LSP semantic-tokens legend all paint the same source the same colour. - Zed extension
editors/zed-extension-qvrbumped to 0.2.0. Tree-sitterhighlights.scmregenerated from the homogenized grammar via the sharedgrammars/qvr/queries/_generate.pyscript, so the Zed mirror tracks the canonical highlights file automatically.
[0.10.0] - 2026-05-17¶
Added¶
qvr repl— GHCi-style interactive type explorer. Four-pane Textual TUI (top status bar; input editor with bracket pairing and auto-indent; eval log with live syntax highlighting; environment browser with namespace tree, fuzzy filter, and click-to-:info; auto-hiding watches and diagnostics strips), plus a prompt_toolkit single-line fallback (--plain/ non-TTY). Meta-commands:load,:reload,:type,:kind,:info(--pythonopts in to the didactic AST),:doc,:browse,:dump(--jsonopts in tomodel_dump_json),:edit(opens$EDITORand splices the result back),:trace,:save,:watch,:unwatch,:set,:help,:quit, with the short aliases GHCi users expect (:l,:r,:t,:k,:i,:b,:s,:w,:h,:q). Bare lines compile as appended statements into the live module or fall through to:type. Persistent input history at~/.config/quivers/history(Ctrl-Up/Ctrl-Downwalks it).Tabcycles through env, grammar-keyword, builtin, and path completions.Ctrl-Popens a Textual command palette over every meta-command.Ctrl-G/Ctrl-O/F8evaluate; the bindings are picked to clear every layer of OS, terminal, XON/XOFF, andTextAreakeymap interference on macOS, Linux, and Windows. Install withpip install 'quivers[repl]'.- Live syntax highlighting shared with the editor. A single
STYLE_TABLEand tree-sitter-driven tokenizer (quivers.cli.repl_highlight) feeds the TUI'sRichLog, the prompt_toolkit lexer, the Jupyter kernel's mimetype hint, and the LSP'ssemanticTokens/fulllegend. The tokenizer is env-aware: identifiers the grammar can't classify in an ERROR context are upgraded to the env-known kind (type/function/namespace), so the same name reads the same colour across every surface. Adding a keyword togrammars/qvr/grammar.jspaints it everywhere automatically. - File-watcher auto-reload. The TUI polls the loaded file's mtime once per second and re-runs
:reloadwhen it advances, printing the diff. Pinned:watchexpressions re-evaluate against the new env. Off via:set autoload_on_save=false. - Pinned watches.
:watch EXPRadds EXPR to a dedicated panel (auto-hidden when empty) and re-evaluates after every recompile;:unwatch EXPRremoves one,:unwatchalone clears all. - Click-to-info and click-to-edit. Identifiers in rendered eval-log output (anything the env classified as a type / function / namespace) are clickable links that fire
:info NAME. The-- declared at PATH:LINE:COLfooter underneath each:infobody is also clickable; it launches$EDITOR +LINE PATH. - Inline diagnostic markers. Compile, parse, and constraint errors that carry a known location move the input pane's cursor selection to the offending span so the user sees where to look without leaving the editor.
- Jupyter kernel.
qvr-kernel install(orqvr kernel install) registers aquiverskernelspec whose cell evaluator is the sameReplSessiondriving the REPL. Cells with a leading:dispatch to meta-commands; blank lines split independent statement chunks within a cell. Tab-completion (do_complete) and Shift-Tab inspect (do_inspect→:info) share the REPL's completer and renderer. The kernelspec advertisespygments_lexer: qvrso notebook frontends highlight cells via the bundled Pygments lexer. qvr-lsp— Language Server (LSP 3.17). pygls 2.x server with incremental document sync,publishDiagnostics(parser + constraint solver + compiler with source ranges),semanticTokens/full(driven by the sharedSTYLE_TABLE),hover(QVR declaration sliced verbatim from the original source plus the didactic AST stacked beneath under a collapsed<details>, pretty-printed one field per indented line),definition,references,documentSymbol(Classfor objects/spaces,Functionfor morphisms,Variableotherwise),completion(same engine as the REPL), andformatting(canonical re-emission viamodule_to_source). Install withpip install 'quivers[lsp]'. New console scriptsqvr-lspandqvr-kernel; new subcommandsqvr repl,qvr lsp,qvr kernel.- First-party VS Code / Cursor extension.
editors/vscode-qvr(v0.5.0) bundles avscode-languageclientbridge toqvr-lspplus the existing TextMate grammar. Auto-discovers the server in (1)qvr.lsp.path(with${workspaceFolder}expansion), (2)<workspace>/.venv/bin/qvr-lsp, (3)<workspace>/.venv/Scripts/qvr-lsp.exe, (4)$VIRTUAL_ENV/bin/qvr-lsp, (5)$PATH. Configurable viaqvr.lsp.enabled,qvr.lsp.path,qvr.lsp.args. - First-party Zed extension wiring.
editors/zed-extension-qvraddsqvr-lspas alanguage_serverentry; Zed spawns it automatically on.qvrfiles. :save FILEmeta-command. Writes the live module (including any statements appended in the session) back to disk viamodule_to_source. Defaults to the loaded path when no argument is given.- Optional dependency groups.
[repl](Textual, prompt_toolkit, rich, ipykernel, jupyter_client) and[lsp](pygls, lsprotocol).pip install quiversalone keeps the core dependency list unchanged — none of the interactive tooling is mandatory. - Comprehensive interactive guide. New
guides/repl-and-lsp.mdcovers every meta-command, every keybinding (with the cross-platform reasoning), the layout, the file watcher, semantic highlighting, themes, the Jupyter kernel, the LSP capabilities, and per-editor wiring instructions. Cross-linked from the README,docs/index.md,getting-started/installation.md,getting-started/quickstart.md, andgetting-started/highlighting.md.
Changed¶
- panproto bumped to >=0.47.3 and panproto-grammars-all to >=0.47.3. The current QVR tree-sitter grammar (with the
alias,export,program, kernel-decl forms) is now vendored upstream; the local-grammar override (QVR_USE_LOCAL_GRAMMAR=1) is no longer necessary for either the runtime or the CI test suite.
Fixed¶
- pygls 2.x API migration.
LanguageServer.publish_diagnosticswas removed upstream in favour oftext_document_publish_diagnostics(PublishDiagnosticsParams(...)). The previous call surfaced as anAttributeErrordialog in VS Code on every save.
[0.9.1] - 2026-05-16¶
Fixed¶
MonadicProgram.log_jointrank-promotes upstream continuous values for single-arg steps. When a draw step'sargswas a single name,_resolve_inputreturned the env tensor verbatim, so a 1-D continuous draw (the shapeGuide.rsampleemits for a 1-DEuclideancodomain) fednn.Linearas a(B,)row vector and crashed the downstream_NeuralSourcematmul withmat1 and mat2 shapes cannot be multiplied (1xB and 1xH). Single-arg continuous inputs are now promoted to(B, 1)via the same rule_stack_tensorsalready applied for the multi-arg branch, with integer-dtype inputs (which feednn.Embeddinglookups) left untouched._NeuralSource.forwardalso unsqueezes a stray rank-1 input as a belt-and-braces guard against customContinuousMorphisms regressing the same way. Affects every shippedConditional*family sitting downstream of a 1-D continuous step; surfaced by directMonadicProgram(steps=[...])users on the first batchedsvi.step.
[0.9.0] - 2026-05-15¶
Added¶
- Comprehensive PyTorch primitive surface for let-expression bodies. The let-expression compiler now resolves 86 named primitives drawn from
torch.nn.functionalandtorch: every standard activation (relu,gelu,silu/swish,mish,elu,selu,leaky_relu,prelu,rrelu,hardtanh,hardshrink,softplus,softshrink,softsign,hardsigmoid,hardswish,sigmoid,logsigmoid,tanh,tanhshrink,threshold,glu), simplex maps (softmax,log_softmax,softmin,normalize), pointwise transcendentals (exp,expm1,log,log1p,log2,log10,sqrt,rsqrt,square,abs,neg,sign,reciprocal,clamp), trig / hyperbolic (sin,cos,tan,asin,acos,atan,sinh,cosh,asinh,acosh,atanh), rounding (floor,ceil,round,trunc), special functions (erf,erfc,erfinv,lgamma,digamma),dim=-1reductions (sum,mean,var,std,min,max,argmin,argmax,prod,amax,amin,logsumexp,norm), cumulative / ordering ops (cumsum,cumprod,cummax,cummin,flip,sort), and training-mode primitives (dropout,alpha_dropout,layer_norm,rms_norm). Reductions takedim=-1by convention; reductions over a named axis go through the typedcontractionsurface. - User-defined programs callable from encoder bodies. Encoder / decoder / loss rule bodies (and any let-expression context) can now invoke top-level
programdeclarations,let-bound morphisms,encoders,decoders, anddeductions by name. The call formf(arg, ...)resolves first against the primitive table, then against the structural-globals dict, then against the user-declared constructor set. A deterministic program is a Dirac Kleisli arrow embedding Smooth into Kleisli(Giry); calling it from an encoder body composes the two Smooth pieces and stays in Smooth. - Compile-time arity checking on let-expression calls. For user-defined callables (
Morphism,MonadicProgram, plain Python callables) the compiler verifies the positional argument count at compile time and raisesCompileError("call to 'f': expected N positional argument(s), got M"). Variadic callables (*args) skip the check.Morphismis unary;MonadicProgramarity equalslen(params)when present, else 1. - Runtime tensor-shape errors wrapped at call sites. A
RuntimeErrororTypeErrorraised inside a user-defined callable surfaces asCompileError("call to 'f' failed: ..."), so shape mismatches name the call site instead of producing a bare PyTorch trace. - DSL
[init=auto]annotation on latent declarations.latent f : A -> B [init=auto]overrides the defaultrandn * scaleinit with the algebra's initialization recipe. ThroughLatentMorphism's sigmoid bijector (for ProductFuzzy / Boolean / Gödel / Łukasiewicz / Probability) the raw parameter is set vialogit(value); for other algebras the recipe is applied to the raw parameter directly. The annotation rides on the existingoption_blockgrammar; no new keywords. - Encoder factory form:
encoder NAME over SIG using FACTORY [k=v, ...]. An alternative to the explicit{ ... }body that invokes a shipped builder fromquivers.structural.shapes(rnn_encoder,transformer_encoder,bow_encoder,tree_lstm_encoder,gnn_encoder) with optional kwarg overrides. The two forms are mutually exclusive on one declaration and can coexist in the same module. The registry mapping factory name → import path lives inquivers.dsl.compiler.structural._ENCODER_FACTORY_REGISTRY. quivers.analysispackage — algebra-guided training tooling.ChainShape.from_module(module)walks a compiled QVRModuleand produces a per-step record (StepShape) with bound variable name, source line / col, chain depth (1-indexed at the first stochastic bind), governing algebra, and inferred intermediate axis size.Algebra.init_spec(depth, intermediate_size) -> InitSpecreturns the closed-form initialization recipe per algebra (product-fuzzyp ≈ ln(2)/k, Łukasiewiczp ≈ 1/k, log-prob-ln k, Markov logits at 0, tropical/max-plus/real1/√k, Boolean/Gödel idempotent at 1/2, probability/counting1/k).recommend_initmaps every latent in a module to itsInitSpec;apply_init_specmaterializes the recipe onto a learnable tensor viatorch.nn.init.saturation_warnings(module)returns source-keyedSaturationWarningdiagnoses for every latent whose recipe differs materially (> 20% in location or scale) from a defaultNormal(0, 1).- Type-driven contraction wiring.
contraction NAME(args) : DOM -> CODblocks no longer require a hand-written einsum string. The compiler infers the einsum from the typed signature: axes that appear in the output propagate; axes that appear in ≥ 2 inputs but not the output get contracted via the rule's join; axes that appear in exactly one input but not the output are flagged with a source-keyed error pointing at the disambiguators. Two opt-in clauses for the cases inference can't derive:share T1, T2, ...keeps the listed axes element-wise even when shared across inputs;wiring "<einsum>"remains as the explicit escape hatch (diagonal extraction, reorderings, etc.).
[0.8.0] - 2026-05-15¶
Changed¶
- BREAKING:
fit(..., sampler=...)renamed tofit(..., method=...). SVI is not a sampler (it fits a variationalGuideby optimization); the parameter selects an inference algorithm. The literal values ("nuts","hmc","svi") are unchanged. Update call sitessampler="..."→method="...". - BREAKING: random-effect terms emitted in non-centered form by default.
(slope | g)formulae now compile to a standard-Normal plate drawz_g_<slope> : g <- Normal(0, 1)plus a deterministiclet alpha_g_per_row = sigma_g * z_g_<slope>[g_idx], rather than the textbook centeredalpha_g : g <- Normal(0, sigma_g). Same posterior mathematically; the funnel that wrecks centered hierarchical sampling under HMC / NUTS and under-shrinks variance components under VI is gone. Passfit(..., reparameterize="centered")to recover the previous behaviour.
Added¶
fit(..., guide=...)exposes guide selection for SVI (previously hard-coded toAutoNormalGuide). Accepts anyGuidesubclass:AutoMultivariateNormalGuide,AutoLowRankMultivariateNormalGuide,AutoLaplaceApproximation,AutoIAFGuide, etc. The class is constructed internally with(program, observed_names=...).fit(..., reparameterize=...)controls the parameterization of random effects."noncentered"(default; recommended by Stan / brms) or"centered".formula_to_qvrtakes the same parameter;BayesianFit.reparameterizerecords the choice soqvr_sourceechoes the actual emitted form.- Tutorial 8: Analysis pipelines. Step-by-step walkthrough of the formula → fit → diagnostics surface on synthetic data: Gaussian SVI fit, hierarchical SVI, NUTS Bernoulli + PSIS-LOO model comparison, ArviZ posterior-predictive checks, the lens round-trip. Every Python block verified end-to-end against the running runtime.
Fixed¶
- NUTS / HMC on hierarchical formula models no longer dies with
Sizes of tensors must match except in dimension 1.MonadicProgram._stack_tensorsbroadcasts size-1 leading dims against the maximum batch in the input list, so a scalar latent (sigma) and a per-row tensor (mu) can be concatenated ahead of an indexed observe.
[0.7.1] - 2026-05-15¶
Changed¶
FormulaToQVRModulecomplement isFormulaData, notFormula. The lens was declared asLens[Formula, Module, Formula]— the lazy function-lens idiom in which the entire source rides as complement andbackwardisconst. The honest setup isLens[Formula, Module, FormulaData]: the structural fields of the formula (which columns exist, intercept flag, random-effect group / slope structure, family choice, response identifier) are recoverable from the emittedModulevia a new_decode_modulewalker, and only the un-decodable fields ride in the complement (per-row data arrays, original pre-_qvr_nameidentifier names,term/namepresentation labels, the original formula string).backward(module, complement)now performs a real decode + fuse pass. GetPut holds for every formula. New public symbol:FormulaDatainquivers.formulas.formula.
[0.7.0] - 2026-05-15¶
Changed¶
- BREAKING:
quantale→algebraacross DSL, Python API, and docs. Thequantalekeyword and everyQuantale-named symbol overclaimed the algebraic structure: seven of the eleven built-in cases fail strict quantale-distributivity in Kelly's sense. The new term algebra names the structure \((V, \otimes, \oplus, \mathbf{1})\) honestly; the subclass that does satisfy the strict laws keeps the name strict quantale and is identified inline in Algebras §2. Migration:
| Old | New |
|---|---|
quantale X (DSL keyword) |
algebra X |
Quantale (abstract base) |
Algebra |
BooleanQuantale, LukasiewiczQuantale, GodelQuantale, TropicalQuantale, MaxPlusQuantale, LogProbQuantale, RealQuantale, ProbabilityQuantale, CountingQuantale, MarkovQuantale |
BooleanAlgebra, LukasiewiczAlgebra, GodelAlgebra, TropicalAlgebra, MaxPlusAlgebra, LogProbAlgebra, RealAlgebra, ProbabilityAlgebra, CountingAlgebra, MarkovAlgebra |
ProductFuzzy |
ProductFuzzyAlgebra |
DualQuantale, CustomQuantale, QuantaleFromCallables |
DualAlgebra, CustomAlgebra, AlgebraFromCallables |
QuantaleHomomorphism |
AlgebraHomomorphism |
Algebra.compatible_quantales() |
Algebra.compatible_algebras() |
_QUANTALE_REGISTRY, _register_extra_quantales |
_ALGEBRA_REGISTRY, _register_extra_algebras |
compiler env binding __quantale__ |
__algebra__ |
QuantaleDecl AST node |
AlgebraDecl |
quivers.core.quantales module |
quivers.core.algebras |
quivers.core.quantale_morphisms module |
quivers.core.algebra_morphisms |
quivers.stochastic.quantale re-export |
deleted; import MarkovAlgebra, MARKOV from quivers.core.algebras |
quivers.Algebra (Eilenberg-Moore monad algebra) |
quivers.MonadAlgebra (the public re-export; inside quivers.monadic.algebras the class is still named Algebra) |
Singletons (PRODUCT_FUZZY, BOOLEAN, LUKASIEWICZ, GODEL, TROPICAL, MAX_PLUS, LOG_PROB, REAL, PROBABILITY, COUNTING, MARKOV, REICHENBACH, MATERIAL_IMPLICATION) keep their names. Registry key strings ("product_fuzzy", "boolean", ...) keep their names.
Added: analysis pipelines (formula → fit → diagnostics)¶
quivers.formulas: brms-style formula frontend. A typed AST lens compilesy ~ x + (1 | g)formulas through the existing QVR DSL without touching source strings: the lens emits aquivers.dsl.ast_nodes.Moduledirectly and the existing Compiler consumes it. R / brms behaviour is faithful: orthogonal polynomials by default (poly(x, k)matchesstats::poly), one coefficient per design-matrix column (poly(x, 2)producesbeta_poly_x_2_1/beta_poly_x_2_2), R-style numeric transforms wired in (log,exp,sqrt, ...), and the family registry (gaussian,bernoulli,binomial,categorical,poisson,negative_binomial,gamma,beta,student_t,cumulative) drives the inverse-link + observe step. One-line entry isfit("y ~ x + (1|g)", data=df, family=, sampler=, ...)returning aBayesianFitdx.Model;formula_to_qvremits the same module as canonical.qvrsource. Gated behind theformulasextra.quivers.data: DataFrame schema bridge.DatasetSchematurns a pandas / polars / Narwhals-compatible dataframe into the object cardinalities, observations dict, and plate-index codes a QVR program needs.compose(qvr_body, schema)prepends inferredobject X : Ndeclarations to a user-written program body so the user only writes the program body. Four missing-data policies:raise,drop,impute,mask. Gated behind thedataextra.quivers.diagnostics: ArviZ adapter. Glue layer between quivers' inference records and the ArviZ 1.xxarray.DataTreesurface.to_datatreewraps quivers' per-site sample tensors, log-densities, acceptance rates, and divergences into the canonical ArviZ groups (consumable byplot_trace,plot_forest,plot_ppc,loo,compare,hdi, ...).comparedelegates to ArviZ's PSIS-LOO ranking (Yao et al. 2018).posterior_predictive_checkcomputes the canonical posterior-predictive p-value for a user-chosen test statistic. Gated behind thediagnosticsextra.quivers.dsl.emit: AST →.qvrsource serializer.module_to_sourcewalks aModuleAST and produces canonical.qvrsource; the round-triploads(module_to_source(m)) == mis exercised on every formula in the test suite.TransformedMorphism.Morphism.change_base,.dagger,.trace, and.refactornow return aTransformedMorphismwhose.tensoris recomputed from the source morphism's tensor on every access, keeping autograd graphs alive across transforms.- Posterior-recovery test marker. Ten end-to-end recovery tests under
tests/test_formulas.py::TestPosteriorRecoveryfit synthetic data with known true coefficients (Gaussian / Bernoulli / Poisson / Gamma, hierarchical random intercepts, multi-predictor regression, log transforms, polynomial terms, interactions, random slopes); marked@pytest.mark.slowand deselected by default (pytest --runslow tests/to include).
Changed: regression examples¶
- Drop the spurious
x : Resp <- Normal(0, 1)declarations from every regression example (bayesian_regression,beta_regression,dirichlet_regression,horseshoe_regression,negbin_regression,zip_regression): predictors are exogenous data and flow through the host-data channel as free variables inletexpressions, not as latent draws. Gallery markdown explainers updated to match.
[0.6.0] - 2026-05-14¶
Added¶
- Factor expressions:
let f = factor v_1 : I_1, ..., v_n : I_n in <body>. A new let-expression form that assembles an indexed tensor of shape(|I_1|, ..., |I_n|, *body_shape)by evaluating<body>once per cell of the Cartesian product of binder indices. The categorical content is the left adjoint of multi-axis indexing: factor is to indexing as a (co)limit cone is to its components. A single-binder pattern-match formfactor v : I in { 0 -> e_0, 1 -> e_1, ... }is also supported for cell-structured priors (label coverage and in-range labels are checked at compile time). Closes #19.
[0.5.0] - 2026-05-13¶
Headline additions¶
- Axis-role surface on every distribution clause. Kernel
declarations, latent priors, sample steps, and observe steps
accept an optional
over <axes> [iid over <axes>]post-clause.overnames the event axes of the family; remaining axes are iid (categorically a product of independent distributions). Axis names resolve against the named factors of the surrounding morphism's dom/cod;dom/codare shortcuts when that side is a single unfactored object. The axis count must match the family's declaredevent_rank(0 / 1 / 2 for scalar / vector / matrix families); mismatch is a compile-time error rather than a silent reinterpretation, preserving the categorical distinction between a dense MVN overdim(A)*dim(B)and aMatrixNormalwith Kronecker structureV (X) U. - Unified
kernelkeyword. Thecontinuousandstochasticdeclaration keywords are removed. Akernel f : A -> Bdeclaration without a~clause is a finite-set lookup-table kernel; with~ Family [options] [axes]it is a parametric kernel whose family parameters come from the input by a neural parameter network at sample time. - Latent morphism priors. A
latent f : A -> B ~ Family(args) [options] [over <axes>]clause puts a prior on the morphism's representing tensor (factor-analysis / PPCA / BNN idiom). - Four new conditional families.
ConditionalMatrixNormal(Kronecker covariance, event_rank 2);ConditionalInverseWishart(conjugate covariance prior, event_rank 2);ConditionalGaussianProcess(RBF / Matern 5/2 / linear kernels with learnable length scale and amplitude, event_rank 1); andConditionalHorseshoe(Carvalho-Polson-Scott global-local shrinkage with a 16-point Gauss-Legendre quadrature giving the exact marginal log-prob, event_rank 1). - Example gallery, 36 examples. Regression (Bayesian, beta, Dirichlet, negative-binomial, horseshoe, ZIP); latent-variable (factor analysis, PPCA, LDA, IRT-2PL, PMF, BNN, GMM, VAE); time-series and state-space (HMM discrete, continuous HMM, linear-Gaussian SSM, deep Markov, AR(1), stochastic volatility, changepoint, Weibull survival); sequence architectures (vanilla RNN, LSTM, GRU, bidirectional RNN, transformer; all as language models; one seq2seq with encoder + decoder); formal grammars (PCFG, CCG, Lambek calculus, multimodal TLG, custom rules, Montague NLI, quantifier scope, event-structure latent class). Each example uses at least one distinguishing quivers feature (axis-role priors, marginalize, change_base, encoder block, scan, deduction declarations) and runs end-to-end on synthetic data.
Refactors¶
src/quivers/continuous/bayesian.pyrenamed toplate.pyand further split:_DeterministicMorphism,cumsum,softmax,cholesky_quad_formmoved toquivers.continuous.deterministic;CholeskyFactorcontinuous space moved toquivers.continuous.spaces;LKJCorrelationFactorandTruncatedmoved toquivers.continuous.families;plate.pynow strictly holds the plate / vectorized-observe / grouped-marginalize machinery.src/quivers/dsl/compiler.py(~6300 LOC) split into a packagesrc/quivers/dsl/compiler/with one module per concern (_prelude,core,declarations,programs,structural,deductions,resolution,expressions). Public import paths unchanged.
A substantive expansion of the inference layer plus the full
implementation of scoped grouped marginalize blocks (issue #9),
a unified distribution family registry with every torch.distributions
family exposed inline, the Tier-1 through Tier-6 benchmark grid
emitting docs/developer/inference-benchmarks.md, and the
categorical refinements from issues #15-#17 (algebra duality,
shape-aware change-of-base, storage-level shape compatibility).
Pre-1.0 clean break: no backwards-compatibility shims; user code
that relied on the SVI(loss=...) keyword should switch to
SVI(objective=...), Predictive(guide=...) should use the
positional posterior argument, and imports from
quivers.core.extra_algebras move to quivers.core.algebras.
Added: algebra duality and user-defined algebras¶
Algebra.dual()returns aDualAlgebrawhosetensor_opandjoinswap roles under the de-Morgan involution.ProductFuzzy.dualgives the canonical Reichenbach-flavor probabilistic-implication composition (⊗ = noisy-OR,⋁ = product reduction);Boolean.dualgives(OR, AND);Lukasiewicz.dualgives the bounded-sum t-conorm pair;Godel.dualgives(max, min).- Named singletons
REICHENBACH,BOOLEAN_DUAL,DUAL_LUKASIEWICZ,DUAL_GODELexported fromquivers.core.algebrasand registered with the DSL algebra catalog so users can writealgebra reichenbach. CustomAlgebraaccepts user-suppliedtensor_op/join/unit/zero/negatecallables for arbitrary user-defined algebras, with construction-time sanity checks on the identity / absorbing axioms.
Added: functorial change-of-base¶
MorphismTransformationABC inquivers.core.morphism_transformations: shape-aware change-of-base for transformations that don't factor pointwise through a algebra homomorphism. Concrete subclasses:Softmax(axis_object),L1Normalize(axis_object),L2Normalize(axis_object),BayesInvert(prior).Morphism.change_basenow dispatches on either aAlgebraHomomorphism(pointwise) or aMorphismTransformation(shape-aware). The latter may swap the morphism's domain and codomain (used byBayesInvert).
Added: first-class transformations in the DSL¶
- Trans values are now full DSL values: a transformation (a
AlgebraHomomorphismorMorphismTransformation) can be let-bound, composed with>>>, and passed tochange_base. The transformation namespace is disjoint from the morphism namespace; let-binding routes based on the RHS shape. - Constructors are bare-verb names (
softmax(B),l1_normalize(B),l2_normalize(B),bayes_invert(prior)). The Python-side factory names inquivers.core.morphism_transformationsfollow the same spelling,softmax,l1_normalize,l2_normalize,bayes_invert, replacing the previous*_oversuffixes. - Singletons (
expectation,log_prob,material_implication,boolean_embedding,probability_clamp,probability_to_real,counting_from_real,counting_to_real,threshold,max_plus) resolve by bare name, both directly insidechange_baseand as let-bindings (let phi = expectation). t1 >>> t2composes two transformations; nested chains flatten and each adjacenttarget/sourceboundary is type-checked at compile time. Inline or via let:
Pre-1.0 clean break: the previous mini-surface
change_base(softmax_over(B)) is removed. Users on 0.4.x
rename softmax_over → softmax, l1_normalize_over →
l1_normalize, l2_normalize_over → l2_normalize (DSL and
Python API both).
Added: multi-observe grouped marginalize blocks¶
A grouped marginalize block now supports multiple observe
steps in the same body, each carrying its own via <idx> clause.
The runtime scatter-sums each observe's (N_m, K) per-row
per-class log-likelihood into the same (|G|, K) accumulator
before the log-sum-exp; categorically the right Kan extension
along the coproduct fibration \(\coprod_m r_m : \coprod_m
\mathrm{Resp}_m \to G\). The single-observe case is the unary
slice; the multi-observe case unblocks hierarchical-Bayes
models where multiple heterogeneous response axes share a
per-group class indicator (the canonical pattern in the Stan
likelihoods that motivated the work).
- Surface change: the
via <idx>clause moves from themarginalizeheader to eachobservestep. The marginalize header now declares only the grouping plate (over Gorover G * H); each observe inside the body carries its ownvia <idx>(orvia product(idx_a, idx_b)) clause. A grouped body whose observe lacks aviais rejected at compile time with a typed error. - Runtime:
quivers.continuous.bayesian.marginalize_groupedaccepts either a single(N, K)log-likelihood tensor and fibration (single-observe path) or a parallel list of(N_m, K)tensors and fibrations (multi-observe path). - Pre-1.0 clean break: the previous
marginalize ... over G via idx in { ... }header form is removed. Users on 0.4.x with a grouped marginalize block move thevia <idx>clause from the header onto the observe inside the body; a single per-block fibration becomes a single observe carrying that fibration. Multi-block patterns where two marginalize blocks shared a class prior collapse to a single block with two observes (per the canonical pattern in the issue that motivated this). src/quivers/dsl/examples/event_structure.qvrupdated to the single-block-with-two-observes form.
Added: documentation overhaul¶
- Two-track tutorial structure: a new QVR DSL track (seven chapters, model development to inference, side-by-side with PyMC / NumPyro / Stan) and a refreshed Python API track (two new chapters covering first-class transformations and the composition-rule hierarchy).
- New denotational-semantics page,
docs/semantics/composition-rules.md, formalizing theCompositionRule → Semigroupoid → Algebrahierarchy, operadic n-ary contractions via flat wirings, and the sortTrans[V, W]for first-class transformations. - New conceptual guide
docs/guides/transformations.mdcovering the transformation surface and the composition-rule hierarchy end-to-end. - Updated
docs/semantics/grammar.mdanddocs/guides/dsl.mdEBNF productions to reflect 0.5.0 surface:>>>,change_base, composition-rule keywords, contraction declarations, groupedmarginalize, the full compose-operator family. README.mdanddocs/index.mdrewritten with separate framings (probabilistic-programming user vs library developer); architecture and inference-stack diagrams render in Mermaid.- New module
quivers.core.transexposing theTransSeqclass andcompose_transfunction as the Python-side surface for the DSL's>>>operator;Morphism.change_baseacceptsTransSeqnatively. - QVR syntax highlighting refreshed across all three rendering
surfaces: the Pygments lexer
(
src/quivers/dsl/pygments_lexer.py), the tree-sitter highlights query (grammars/qvr/queries/highlights.scm), and the VSCode TextMate grammar (editors/vscode-qvr/syntaxes/qvr.tmLanguage.json). - Restored the MkDocs hook
docs/hooks/register_qvr_lexer.pyand wired it throughmkdocs.yml; Mermaid rendering enabled viapymdownx.superfencescustom-fence classifier plus amermaid-init.jsthemer.
Added: shape compatibility under product / factored codomains¶
- Storage-level init check:
observed f : A -> B = from_data("KEY")now matches the init tensor against the declared codomain on numel rather than per-axis shape. A flat init whose total cardinality matches a declared product codomain is accepted and reshaped to the factored layout. Morphism.refactor(domain=..., codomain=...)exposes the reshape as a user-facing method: switch a morphism's view between flat and product factorings of isomorphic objects.
Changed: module consolidation¶
quivers.core.extra_algebrasis deleted. All its classes (Lukasiewicz, Godel, Tropical, MaxPlus, LogProb, Real, Probability, Counting) and singletons now live inquivers.core.algebrasalongside ProductFuzzy and Boolean. Internal imports updated; user code that imported fromextra_algebrasmust update to import fromalgebras.
Added: distribution-family wrappers¶
ConditionalMixturewraps aConditionalXfamily in a K-component mixture with learnable mixture logits.ConditionalIndependentreinterprets a base distribution's trailing batch dim as an event dim (analogue oftorch.distributions.Independent).ConditionalTransformedpushes a base through a chain of bijectors, applying the log-det-Jacobian correction inlog_prob.
Fixed: HMC at constrained-support boundaries¶
- HMC's potential function now catches
torch.distributions' support-validation errors and returns-inf; the kernel reads non-finite log-densities as divergent transitions and rejects them in the Metropolis step. Eliminates the prior ERROR cells in the benchmark matrix at the Gamma / InverseGamma / HalfNormal support boundaries.
Changed: codebase-wide American spelling¶
- All British spellings (centered / normalize / parameterize /
optimize / reparameterise / recognize / factorize / initialise
/ specialize / organize / minimize / maximize / discretize and
their derived forms) converted to American spellings.
eight_schools_centred.qvrrenamed toeight_schools_centered.qvrwith the program names updated to match.
Added: unified distribution family registry¶
FamilySpecandParamSpecinquivers.continuous.family_spec, a single source of truth per family. Both the conditional path (_IndependentConditionaland the hand-writtenConditionalXclasses) and the inline path (FixedDistribution/MixedInlineDistribution) read from the same record. Replaces the previous duplication betweenfamilies.pyandinline.py.- Inline coverage for 11 previously registry-only families:
Cauchy, Laplace, Gumbel, StudentT, Chi2, InverseGamma, Weibull,
Pareto, Kumaraswamy, ContinuousBernoulli, FisherSnedecor. Every
family declared via
_make_familynow auto-registers its fixed-inline factory, mixed-inline builder, and support constraint from the spec. - New families: ConditionalPoisson, ConditionalGeometric, ConditionalNegativeBinomial, ConditionalVonMises plus their inline factories. Brings the registry to 34 distinct distribution families.
_IndependentConditionalis shape- and discreteness-aware: continuous-reparameterised, continuous-non-reparameterised (VonMises), and discrete (Poisson / Geometric / Bernoulli / Categorical) all share the same generic conditional class via adiscreteflag plus a fall-back to.sample()when the underlying distribution lacksrsample.
Added: benchmark suite expansion¶
- Tier 1 conjugate: normal_inverse_gamma (joint mean / variance recovery), gamma_exponential, bayes_linear_regression (well-conditioned).
- Tier 2 hierarchical: eight_schools_centred and eight_schools_noncentred (Rubin 1981 / Gelman et al. 2013) with cached NUTS-derived posterior moments.
- Tier 3 hard geometry: neal_funnel (scale-of-scale dependency, Neal 2003), ill_conditioned_mvn (per-dim posteriors with eigenvalues across four decades).
- Tier 6 constrained support: half_normal_scale (positive support), truncated_normal_recovery (unit interval).
- Metrics extension: gaussian_kl (closed-form KL between two Gaussians), wasserstein_2_1d (sorted-quantile W₂), total_variation_grid (TV against a quadrature-supplied density), split_r_hat and effective_sample_size (Vehtari et al. 2021).
- Runner (
tests/benchmarks/runner.py) drives an algorithm × problem grid across AutoNormalGuide, AutoMultivariateNormalGuide, AutoLaplaceApproximation, HMCKernel, NUTSKernel and emitsdocs/developer/inference-benchmarks.md. Capture problems document expected failure modes (mean-field underfit on correlated MVN / funnel posteriors).
Added: earlier 0.5.0 work¶
Added: inference layer¶
LatentRegistry: per-site introspection helper that every guide and MCMC kernel consumes. Walks the model's_step_specsonce at construction and exposes per-site (support, dims, plate vs scalar, bijector, flat-vector offsets) plus flatten / unflatten between site dicts and a single flat unconstrained vector.- Transforms / flows:
TransformModulecooperative base inheriting bothtorch.distributions.transforms.Transformandtorch.nn.Module. Primitives:AffineCouplingTransform(RealNVP),MaskedAutoregressiveTransform(MAF),InverseAutoregressiveTransform(IAF),NeuralSplineCouplingTransform(NSF rational-quadratic spline coupling),LULinearTransform(Glow),BatchNormTransform, plusMADEand helper masks. - Variational objectives:
ObjectiveABC and four implementations:ELBO,IWAEBound(Burda-Grosse-Salakhutdinov 2016),RenyiBound(Li-Turner 2016),VRIWAEBound(Daudel-Douc-Roueff 2023). Each accepts a pluggableGradientEstimatorstrategy:Reparameterised(default),StickingTheLanding(Roeder-Wu-Duvenaud 2017),DoublyReparameterised(Tucker-Lawson-Gu-Maddison 2019; default for IWAE),ScoreFunction. - Guide zoo:
AutoMultivariateNormalGuide(full-rank Cholesky Gaussian),AutoLowRankMultivariateNormalGuide(Σ = W W^T + diag(σ²) via Woodbury),AutoNormalizingFlow(user-supplied transform stack),AutoIAFGuide(preconfigured IAF stack with reverse permutations),AutoNeuralSplineGuide(NSF coupling stack),AutoLaplaceApproximation(two-phase: SVI MAP then Hessian-derived Gaussian),AutoMixtureGuide(Gumbel-Softmax mixture of component guides). - MCMC:
HMCKernelandNUTSKerneloperating on the flat unconstrained latent vector via theLatentRegistry. Both support identity / diagonal / dense mass matrices, Nesterov dual-averaging step-size adaptation (Hoffman-Gelman 2014 Alg 6), and Welford-online mass-matrix adaptation.MCMCdriver orchestrates parallel chains with warmup-then-sample phases and producesMCMCResultcarrying per-site posterior draws + split-R̂ + ESS diagnostics. - Hybrid samplers:
AutoDAIS(differentiable annealed importance sampling guide; Geffner-Domke 2021 / Zhang et al. 2021) andWarmupThenHMC(SVI warmup then MCMC seeded from the guide's posterior mean). Predictivenow accepts either aGuideor anMCMCResultas its posterior; the MCMC overload iterates the recorded posterior draws.
Added: issue #9: scoped grouped marginalize blocks¶
marginalize <v> : K <- F(probs) over G via idx in { ... }with the full feature surface from issue #9:- Arbitrary nesting of grouped marginalize blocks. The runtime distinguishes the innermost level (which has a row axis and a fibration to scatter along) from outer levels (which consume already-reduced contributions broadcast over the surviving outer class axes), so a stack of N blocks composes to the correct hierarchical log-mixture. Tested at depths 3-7.
- Product fibrations via
via product(idx_a, idx_b, ...)paired withover G * Hproduct grouping plates. reduction = logsumexp | sum | meanper block.- Continuous latents in scope inside the body; the compiler
binds the latent name to
torch.arange(K)so let-arithmetic referencing the latent broadcasts across the class axis. - Body vectorization: the body's terminal
observestep is captured by the compiler (GroupedBodyObserveStepIR) and its per-row per-class log-likelihood is stored at the latent's environment slot for the marginalize callable to consume. No user-sideletboilerplate. quivers.continuous.bayesian.marginalize_groupedgeneralized to accept multi-axis broadcast inputs, product fibrations, and the three reduction modes.dsl/examples/event_structure.qvrnow uses realobservesteps inside the grouped blocks.
Added: synthetic benchmark suite¶
tests/benchmarks/with Tier-1 conjugate (Beta-Bernoulli, Normal-Normal) and Tier-3 hard-geometry (correlated MVN) benchmarks. Models live intests/benchmarks/models/*.qvras canonical didactic models; dataset / reference modules are plain functions returning(model, observations, true_params). The Tier-3 suite includes a capture test for the mean-field failure mode on a correlated posterior.
Added: V-Cat categorical surface¶
- Backend-agnostic morphism →
nn.Moduleadapter (quivers.core.morphisms.as_torch_module). Every binding site that callsadd_module(MonadicProgramstep list,FanOutMorphism, parametric programs) funnels through the adapter so non-Module morphisms (LatentMorphism,ComposedMorphism,ProductMorphism, …) bind without crashing. The wrapper attaches the original categorical morphism on_morphismfor runtime recovery;MonadicProgram.rsampledetects V-Cat steps and computes them as deterministic tensor applications. - Multi-algebra composition with one operator per algebra.
>>(ProductFuzzy default),<<(reverse),>=>(Kleisli),*>(Markov sum-product),~>(LogProb log-space),||>(Gödel min/max + Heyting),?>(Viterbi max-plus),&&>(Boolean),+>(Łukasiewicz),$>(Real sum-product),%>(Probability sum-product). Each operator carries its algebra; cross-operator chains require explicit.change_base(φ). Five new algebra classes:MaxPlusAlgebra(Viterbi / MAP),LogProbAlgebra(log-space sum-product),RealAlgebra(sum-product on ℝ),ProbabilityAlgebra(sum-product on [0, 1]),CountingAlgebra(sum-product on non-negative integers). The last three mirror the corresponding arcweight weight-set semirings (RealWeight,ProbabilityWeight,IntegerWeight) and round out the built-in algebra catalog to eleven distinct algebras. - Algebra homomorphisms (
quivers.core.algebra_morphisms) for change-of-base:Expectation,LogProb,MaxPlus,Threshold,MaterialImplication,Embedding,IdentityHom,ProbabilityClamp(Real → Probability),CountingFromReal(Real → Counting via floor),ProbabilityToRealandCountingToReal(sub-algebra inclusions). Module-level singletons (EXPECTATION,LOG_PROB_HOM,MAX_PLUS_HOM,MATERIAL_IMPLICATION,PROBABILITY_CLAMP,COUNTING_FROM_REAL,PROBABILITY_TO_REAL,COUNTING_TO_REAL); factory helpersthreshold(tau)/embedding(src, tgt); aHOMOMORPHISM_REGISTRYkeyed by(source.name, target.name); andlookup_homomorphism(). Morphisms expose.change_base(phi)to apply a homomorphism; the DSL surface isf.change_base(name)with the catalog wired into the compiler (expectation,log_prob,max_plus,material_implication,threshold,boolean_embedding,probability_clamp,probability_to_real,counting_from_real,counting_to_real). - Compact-closed surface on V-Cat morphisms:
f.dagger(transpose),f.trace(A)(categorical trace),cup(A)andcap(A)(unit / counit). Each operation is well-defined for every algebra; the semantic interpretation depends on the active algebra (ProductFuzzy: tensor transpose; Markov: Bayes inversion; Viterbi: max-plus reversal; Boolean: relational converse). - Data-derived and expression-derived initialisers for
morphism declarations.
observed f : A -> B = from_data("KEY")binds the morphism's tensor from a runtime-supplied data dictionary (passed via thedata=keyword onquivers.dsl.loads/load, orCompiler.bind_data(...)).inner.freezeis a postfix that materialises an expression's tensor with.detach().clone()and wraps the result as a parameter-freeObservedMorphism. Expression-derived initialisers without.freezepropagate gradient lineage; the declaration's type-check accepts shape-compatible inits and re-tags them with the user-declared domain / codomain.
Changed¶
SVItakes anobjective: Objectiveinstead ofloss: ELBO.Predictivetakes a positionalposterior: Guide | MCMCResultinstead of aguide:keyword.- The variational machinery moved off
PlateDraw(kl_to_priorremoved; the deeper migration of_mean/_log_scalehappens in v0.5.0 alongside the rest of the refactor). MonadicProgrambinding sites no longer crash on V-Cat morphisms; the runtime applies them as deterministic tensor steps.FanOutMorphismaccepts both ContinuousMorphism and V-Cat morphism components; non-continuous components are wrapped inDiscreteAsContinuousso the fan-out loop dispatches uniformly.
Removed¶
PlateDraw.kl_to_prior(no callers; the Guide hierarchy owns variational distributions now).quivers.inference.elbo(re-exported fromquivers.inference.objectivesasELBO).
[0.4.1] - 2026-05-12¶
Three connected bug fixes that unblock hierarchical-Bayesian
workflows under the 0.4 surface. No grammar or parser changes;
panproto does not need to revendor panproto-grammars-all for this
release.
Fixed¶
-
Variational guides respect constrained supports.
quivers.inference.AutoNormalGuideandquivers.inference.AutoDeltaGuidepreviously sampled in unconstrained real space for every latent and fed the result into the prior'slog_prob, which raisedValueError: Expected value to be within the support of the distributionfor any constrained family (HalfNormal,Beta,Uniform,Exponential,Gamma,LogNormal,LogitNormal,HalfCauchy,Dirichlet, …). EveryContinuousMorphismnow exposes asupport: Constraintproperty; the inline distributions (FixedDistribution,MixedInlineDistribution,DirectBernoulli,DirectTruncatedNormal) and the family-conditional distributions inquivers.continuous.familiesoverride it with the correct constraint. The auto-guides samplez ~ Normal(loc, scale)in unconstrained space, push throughbiject_to(support)to land on the constrained side, and evaluatelog_probwith the Jacobian correction (log N(z) + log|det J_{T^{-1}}(v)|). The simplex case routes throughStickBreakingTransformand accounts for the d ↔ d-1 dimension reduction. This is the same construction Pyro'sAutoNormaluses. -
condition(model, data)exposes host data tolet-expression gather, and plate latents are batch-invariant. Keys in the conditioning data dict that do not match a declared sample / observe site are pre-populated into the trace environment as deterministic values, visible tolet-expression evaluation. Free variables inletexpressions (variables not bound by any sample / observe / let / lambda step) are no longer rejected at compile time; the runtime resolves them against the conditioning data dict. Together with the batch-invariant plate semantics below, this unlocks the canonical crossed-random- effects idiom:Plate draws (program p : Resp -> Resp by_subj : Subj <- Normal(0.0, 1.0) let mu = sigmoid(by_subj[subj_idx]) observe r : Resp <- Bernoulli(mu) return mu cond = condition(p.morphism, {"subj_idx": idx, "r": y})v : A <- F(args)) are now batch-invariant: the latent is a single shared tensor of shape(|A|, *B.shape): the standard Pyro / NumPyro semantic, instead of being replicated against the program input's leading batch axis. The gatherby_subj[subj_idx]along the plate axis then produces a per-row predictor of shape(N_resp,)that broadcasts cleanly against an observedResp-plate kernel. Scalar-per-row plates (Normal,HalfNormal, …) drop the trailing length-1 axis so the latent has the natural(|A|,)shape. Both :class:AutoNormalGuideand :class:AutoDeltaGuidewere updated to advertize the same shape on the variational side: plate latents are stored as(|A|, unconstrained_dim)parameter tensors and sampled batch-invariant so ELBO substitution into the model's log-joint env aligns shape-by-shape with the model's :class:PlateDrawoutput. (Without this, the SVI step ran into anIndexErrorfrom the plate-axis gather even thoughcond.trace(...)on the same model succeeded.) -
Inline
Dirichletaccepted as a prior with scalar or vector concentration.pc <- Dirichlet(α)andpc <- Dirichlet([α_1, …, α_K])both compile. For scalarα, the simplex dimension is inferred from the program's declared codomain (dimfor aContinuousSpace,cardinalityfor aSetObject, or 2 as a minimum); for a per-component vector, the simplex dimension is the number of literals. Themake_fixed_dirichletfactory accepts both single-element sequences (treated as symmetric) and per-component sequences. Previous behavior was to raisedistribution family 'Dirichlet' is not supported as an inline distribution; declare it as a continuous morphism instead, or to crash on the vector form withTypeError: make_fixed_dirichlet() takes 2 positional arguments but 4 were given.
Internal¶
ContinuousMorphism.supportdefaults toconstraints.real; every constrained-output subclass overrides it. Variational guides consume this throughtorch.distributions.constraint_registry.biject_to._FAMILY_SUPPORTSinquivers.continuous.inlinemaps each inline family to its support, applied bymake_inline_distributionwhen constructing aMixedInlineDistribution.UniformandTruncatedNormalspecialize to the actual interval when both bounds are literal.trace()pre-populatesenvwith the non-site keys of the observations dict before the program's steps run._validate_let_expr_varstreats unbound names as deferred host references; the eval-time evaluator raises a clearKeyErrorif the value is missing.PlateDraw.rsampleis batch-invariant: returns(|A|, *B.shape)regardless of the program input's batch axis; scalar-per-row plates squeeze the trailing length-1 dimension.PlateDraw.log_probaccepts either the natural plate-latent shape or the legacy flat(batch, |A| · prod(B))shape for back-compat._VECTOR_PARAM_FAMILIESinquivers.continuous.inlinelists the inline families whose all-literal factory takes a single vector argument rather than splatting positional floats; the parser's flattened literal sequence is re-bundled into a list before the factory call. Currently{"Dirichlet"}; the mechanism is ready forMultivariateNormal,Wishart, andLKJCorrelationFactoras those land.make_fixed_dirichlettreats a single-element concentration sequence as a scalar (symmetric Dirichlet), broadcast to the codomain's simplex dimension; multi-element sequences must match the codomain dimension exactly.
Tests¶
tests/test_inference_constrained.py (21 cases): every supported
constrained family under both auto-guides, host-data passing
through condition, the end-to-end Bernoulli hierarchical-
regression observation kernel that exercises the plate-gather →
observe-plate composition, an SVI-step regression that verifies
the guide / model plate shapes align and the ELBO descends with
loc_by_subj driven negative by all-zero responses, and inline
Dirichlet with both scalar and vector concentrations.
[0.4.0] - 2026-05-12¶
This release lands three deeply-interconnected bodies of work in a single minor bump, all motivated by the goal of making quivers a unified surface for probabilistic, weighted-deductive, and neural-symbolic programs:
- DSL surface homogenization, one Kleisli-bind sigil
<-, type-annotated indexed binds, scopedmarginalize, and!-prefixed effect signatures. - Agenda-based weighted-deduction framework, a single
engine subsuming CKY, Earley, Viterbi, inside-outside,
semi-naive Datalog, A* parsing, Knuth's algorithm, and MLTT
proof search, declared via
deduction { … }blocks with first-class differentiable charts. - Hierarchical-Bayesian + arrow / algebraic-effects substrate
, plate draws, vectorized observations, marginalization,
LKJ priors, Cholesky factor spaces, the Hughes arrow tower
(
Arrow,ArrowChoice,ArrowApply,ArrowLoop,ArrowZero,ArrowPlus), stdlib monads / monad transformers, algebraic effects + handlers viaFreeMonad. - Structural compression: signatures, encoders, decoders, losses, a uniform algebraic interface for compressing arbitrary structured objects (sequences, trees, graphs, charts, typed lambda terms) to fixed-length vectors and decoding them back under a learned distribution. Realizes transformers, tree-LSTMs, graph-NNs, autoregressive LMs, VAEs, and vector-inside-outside parsers as instances of one F-algebra / F-coalgebra pattern.
Changed (breaking, pre-1.0 clean cut)¶
Surface DSL homogenization. The program-block surface is reorganized
around a single Kleisli-bind sigil <-, type-annotated indexing on
the binder, scoped marginalization, and !-prefixed effect
signatures. The categorical denotation is unchanged; only the surface
forms shift to a Haskell-PPL aesthetic.
draw v ~ F(args)→v <- F(args): thedrawkeyword is retired in favor of the unique Kleisli-bind sigil<-. The surface-arrow alternative (the v0.4 do-notationv <- Fform) is unified with the new sigil; there is now one and only one way to introduce a random variable.draw v : A -> K ~ F(args)→v : A <- F(args): indexed (plate) binds use a type annotation on the binder rather than a separate keyword family. The per-fiber codomain is taken from the family; the: Aannotation declares the index set.observe v ~ F(args)→observe v <- F(args): scored binds retain theobserveprefix; the rest of the line matches the Kleisli-bind shape.observe r[n] ~ F(args) for n in N→observe r : N <- F(args): the vectorized-observefor n in Nshape collapses into the type-annotated form. Bracket-indexed family argumentstheta[N]annotate that an argument is a section of an N-indexed family.marginalize c(trailing) →marginalize c : A <- F(args) in { … }: marginalization is always scoped; the integration target and the scope are visible at the binding site. The categorical pushforward becomes visually local to its binding.posterior name (model) [params] : dom -> cod→program name (params) : dom -> cod ! Pure over model: posterior blocks are encoded asprogramdeclarations with a! Pureeffect signature and anover modelmodifier. The parser routes such programs to the posterior registry and the compiler enforces the determinism constraint.!effect signature on programs:program P : X -> Y ! Sample, Scoredeclares the body's capability set. Effects:Sample,Score,Marginal,Pure. The compiler verifies the body's actual effects are a subset of the declared set (or rejects any effect whenPureis declared).output X→export X: module-level exports replaceoutput; multipleexportdeclarations per module are allowed.- Drop labeled-tuple return form: the v0.4
return (a: x, b: y)form was purely syntactic rebinding without semantic effect; it is removed as dead surface.
Internal¶
BindStepunifies the four old step shapes (DrawStep,PlateDrawStep,VectorisedObserveStep,MarginalizeStep) at the AST level; the old shapes remain as compiler-internal IR consumed by the runtime step-builder. The parser emits onlyBindStepandLetStep; the compiler's_expand_bind_stepspass translates to the internal IR.ProgramDeclgainseffects: frozenset[str] | Noneandover_model: str | Nonefields.MonadicProgramgains aneffect_set: frozenset[str] | Nonefield for introspection by downstream inference / dispatch code.ExportDeclreplacesOutputDecl. The first declared export is the module's primary output; subsequent exports are accessible as additional module bindings.- The DSL's
<-arrow now binds withprec.right, eliminating the previous GLR ambiguity between draw-args and following steps.
Migration guide¶
For most programs the mechanical translation is:
| pre-0.4 | 0.4 |
|---|---|
draw v ~ F(args) |
v <- F(args) |
draw v : A -> K ~ F(args) |
v : A <- F(args) |
observe v ~ F(args) |
observe v <- F(args) |
observe r[n] ~ F(args) for n in N |
observe r : N <- F(args) |
marginalize c |
marginalize c <- F(args) in { … } |
posterior P (M) [v] : … body |
program P(v) : … ! Pure over M body |
output E |
export E |
Tests¶
- All example
.qvrfiles migrated to the 0.4 surface. - Tree-sitter corpus covers the new surface shapes (20/20 parses).
Agenda-based weighted-deduction framework¶
0.4 also lands the full agenda-engine substrate
underneath a declarative deduction { … } block. The framework
subsumes CKY, Earley, Viterbi, inside-outside, semi-naive Datalog
evaluation, A* parsing, Knuth's algorithm, depth-first MLTT
proof search, and edit-distance dynamic programming as parameter
settings on a single engine.
-
Surface form:
Single-uppercase-letter pattern names (deduction CG : Atom -> Atom [semiring=LogProb, start=S, depth=4] atoms NP, S, VP rule fwd_app : X/Y, Y |- X rule bwd_app : Y, Y\X |- XX,Y) bind as wildcards; non-wildcard atoms match literally. The block declares the seven irreducible parameters of an agenda-based deduction (item algebra via atoms, rule set, semiring, axiom source, goal predicate, start symbol, depth bound). Concrete parsing strategies are selected by the compiler from these parameters. -
Charts as first-class differentiable values: each deduction's runtime view exposes
chart.weight(item),chart.enumerate(pattern),chart.derivations(item),chart.goal_weight(), all returningtorch.Tensorvalues whose gradients flow back through the agenda's semiring operations to anyrequires_grad=Trueaxiom / rule weight, enabling end-to-end gradient-based learning over deduction systems (the Goodman 1999 semiring framework lifted to PyTorch tensors). -
Pre-registered stdlib deductions (
quivers.stochastic.stdlib):CCG,Lambek,STLC,MLTT,Datalog,Dijkstra,HMM,ViterbiHMM,EditDistance. Users import and run them directly: -
Agenda strategies:
cky_agenda(),earley_agenda(),viterbi_agenda(priority_fn),astar_agenda(g_plus_h),knuth_agenda(),depth_first_agenda(),semi_naive_agenda(). Strategy independence (Goodman 1999 §3) is verified intest_agenda.py: under idempotent semirings, chart values agree across all strategies. -
panproto integration:
QVR_DEDUCTION_PROTOCOLandextract_deduction_schema(compiler)make deduction systems first-class panproto schemas. Schema morphisms over the protocol correspond to deduction-system specializations (e.g., CCG ⊂ Lambek ⊂ MultimodalLambek).
Structural compression: signatures, encoders, decoders, losses¶
The release lands a uniform algebraic interface for compressing
arbitrary structured objects to fixed-length vectors and decoding
them back under a learned distribution. Categorically: every
constructor algebra a user declares is the initial Σ-algebra T_Σ
of a multi-sorted signature; a encoder is a Σ-algebra
homomorphism T_Σ → Vec_D, and a decoder is a Kleisli
coalgebra Vec_D → Kern(T_Σ). The recursion / corecursion is
supplied by the framework; the analyst supplies only the
per-operation parametric functions. This single abstraction
subsumes RNN / transformer / tree-LSTM / graph-NN encoders,
autoregressive LM and variational decoders, and the proposal's
vector inside-outside parser (the chart's item signature is Σ, the
parser's combine / split^L,R are the per-operation encoder
functions, the attention-weighted aggregation lives entirely
inside the encoder, outside the chart's role, so the
semiring abstraction is not broken).
- Signature blocks declare sorts, constructors, binders, and (for graph signatures) vertex / edge kinds:
signature LF
sorts
Term : object [dim=64]
Type : object [dim=32]
Name : data [dim=32, vocab=["dog", "cat", "every"]]
constructors
Const : Name -> Term
App : Term, Term -> Term
binders
Lam : binds (x : Term : ty : Type) in (body : Term) -> Term
object (recursively decoded), data
(opaque raw values; data sorts may declare a closed vocabulary
via vocab { … } of string / integer / float literals),
index (de-Bruijn slots). The reserved BoundVar op is a
built-in de-Bruijn reference; binders thread a typed context Γ
carrying (var_sort, embedding, type_term) per scope entry.
Binder variables may carry an annotation sort via
binds (x : Term : ty : Type), the variable's type is
structurally tracked through the de-Bruijn context.
- Encoder blocks declare an F-algebra homomorphism
T_Σ → Vec_D. Per-constructor bodies are user-supplied or
scaffolded as 2-layer MLPs by the compiler with correct
per-arg dimensions. Sequence sugar, Cons(head, tail) recurrent
state |-> body for left-folds and Cons(head, tail) attention
prefix |-> body for iterative outside-in walks that thread a
running prefix list, handles RNN / transformer-shaped
encoders uniformly. Graph signatures use a
message_passing-shaped body (init[V], message[E],
update[V], readout, iterations N).
- Decoder blocks are Kleisli coalgebras Vec_D → Kern(T_Σ)
with sample(vec) -> Term and log_prob(term, vec) -> Tensor.
Per-sort structure / primitive / factor / binder_select
heads are scaffolded as learnable neural networks; the
corecursion (structure choice, factor split, recursive descent
with extended Γ at binders, BoundVar fallback to in-scope
variables, depth-bounded termination) is supplied by the
framework. No silent type coercion or sentinel value: an
observed term whose shape doesn't match the canonical form
raises with a typed diagnostic.
- var_init per (var_sort, annot_sort) pair: multiple
var_init Term from Type as ty |-> body declarations per
encoder, one per pair of sorts the signature's binders
introduce; the compiler scaffolds defaults for omitted pairs.
- Stdlib shapes (quivers.structural.shapes): Seq[A] with
rnn_encoder, transformer_encoder, bow_encoder,
ar_decoder; Tree[L, B] with tree_lstm_encoder and
tree_decoder; Graph[V, E] with graph_signature and
gnn_encoder (per-edge-kind message MLP, per-vertex-kind
GRU update, mean / sum / max readout).
- Deduction integration: a deduction block may declare an
item signature and attach a encoder; the chart's
embedding(item) query returns a differentiable vector
computed by the attached encoder's algebra-homomorphism
recursion over the chart-item term.
- Loss attachments (loss <name> [weight ...] [on <site>] { body }):
attachable at global, program <name>, deduction <name>,
encoder <name>, decoder <name>, rule <name> in <D>,
and chart of <D> sites. Rule-attached losses fire on every
rule application during chart construction (the agenda's
_fire path invokes a registered rule_callback with the
full antecedent list); chart-attached losses fire once on the
completed chart. LossRegistry.evaluate_on(kind, target, env)
returns the weighted partial sum for a given attachment site;
ChartView.attached_loss exposes the accumulated rule + chart
losses fired during a deduction's run.
- Optional export: a module with only signatures /
encoders / decoders / losses (no top-level morphism) now
compiles into a Program(None) container; the artifacts are
reachable through prog.signatures / prog.encoders /
prog.decoders / prog.losses. The previous no export
declaration found hard error is replaced by a precise
diagnostic on forward().
- Strict declaration discipline: every sort referenced in a
constructor's domain or a binder's variables / scoped
arguments / codomain must be declared in the signature's
sorts { … } block, no silent auto-registration. Every sort
with no inline dim must have its dim supplied by every
encoder / decoder over the signature. Reserved op names
(BoundVar, Data) are rejected as user-declared constructors
or binders. vocab clauses are only valid on data sorts;
duplicate vocabulary entries are rejected.
- Public surface: quivers.structural exports Signature,
Sort, Constructor, Binder, BinderVarSpec,
BinderArgSpec, VertexKind, EdgeKind, Term, Context,
EMPTY_CONTEXT, DataLeaf, Encoder, Decoder,
LossEntry, LossRegistry, bound_var, make_term.
quivers.structural.shapes exports the canonical sequence /
tree / graph factories.
- 27 new tests in tests/test_structural.py cover every
surface form, every strict-rule diagnostic, the typed-binder
discipline, end-to-end compression and decoding for sequences /
trees / graphs, rule-attached and chart-attached loss firing,
per-pair var_init overrides, recurrent / attention modes, and
the data-sort vocabulary pipeline.
Hierarchical-Bayesian primitives and arrow / effects tower¶
Added¶
- Hierarchical-Bayesian modeling primitives in
quivers.continuous.bayesian, each carrying its categorical denotation in Kern: PlateDraw(index_size, family, domain), finite-domain-indexed draw realized as a Kern-morphismA → Bby the natural isomorphismKern(1, B^A) ≅ Kern(A, B); subclass ofContinuousMorphismso it threads through the existingMonadicProgramstep machinery.VectorisedObserve(family, response), batched-observation kernelΦ → G_{≤1}(Φ)with score∏_n p_F(r_obs(n); θ(n, φ)).marginalize_categorical(log_probs), program-level pushforward throughπ_{Φ\C}realized aslog_sum_expover the class axis.LKJCorrelationFactor(K, eta), LKJ prior onCholeskyFactor(K)via the Lewandowski-Kurowicka-Joe onion method; analyticlog_probmatches Stan'slkj_corr_cholesky_lpdf.Truncated(base, lower, upper), generic interval-truncation combinator (rejection sampling with Monte-Carlo truncation-mass estimation).cumsum(K),softmax(K), deterministic morphisms for monotone splines and simplex projection.cholesky_quad_form(K), covariance reconstructionΣ = diag(s) L L^T diag(s).CholeskyFactor(K)ContinuousSpace, manifold of K×K lower-triangular factors of correlation matrices.- Surface syntax for hierarchical-Bayesian models in
.qvr: draw v : A -> K ~ Family(args), finite-domain-indexed plate draw.observe r[n] ~ Family(args) for n in N, vectorized observation.marginalize c, program-level discrete-latent marginalization.arr[idx], Kleisli pullback gather expression insidelet-bodies.posterior name (model) : domain -> codomain { steps return ... }, deterministic post-conditioning block whose body consumes posterior latents.- Parametric programs:
program name (G : FinSet, scale : Real, prior : Mor[A, B]) : dom -> cod ..., programs polymorphic over objects, scalars, and morphisms; denote dependent kernelsΠ(p:P).Kern(dom(p), cod(p)). Instantiated at each call sitedraw v ~ name(args)by parameter substitution + α-renaming, so each call contributes fresh latent factors to the caller's joint kernel. Supports the random-effects reuse story without tying latents across call sites. - Let-expression builtins:
cumsum,softmax,cholesky_quad_formjoin the existingsigmoid/exp/log/abs/softplus. - AST nodes in
quivers.dsl.ast_nodes:PlateDrawStep,VectorisedObserveStep,MarginalizeStep,LetExprIndex,PosteriorDecl; each docstring carries the Kern denotation. - Stan-model port at
src/quivers/dsl/examples/event_structure.qvr, a faithful translation of the four-class telicity × durativity latent-class model from~/Projects/supertelicity/analysis/event-structure-induction/models/event-structure-model.stan, demonstrating crossed random effects, ordinal monotone splines, vectorized observations, andmarginalizeover the discrete latent class. tests/test_bayesian.py: 15 tests covering every new primitive and every new AST node's parse / compile round-trip, plus a compile-time smoke test on the Stan-model port.
Changed¶
_walk_program_stepreturn type widened fromDrawStep | LetStepto theProgramStepunion root.- Tree-sitter grammar regenerated (
grammars/qvr/src/parser.c,grammar.json,node-types.json) to recognize the new program steps and top-level declarations.
Added¶
- Pattern-polymorphic
schemadeclarations:schema r[X, Y : Cat] : (X/Y) * Y -> X. Subsumesrulewith explicit parameter types and a unified domain/codomain shape. - New SetObject variants:
EnumSet(name, elements)for named-element finite sets,FreeResiduated(generators, depth, ops)for residuated category universes. New surface syntaxobject Atoms = {NP, S, VP}andobject Cat = FreeResiduated(Atoms, depth=4, ops=[slash]). FreeMonoidsurface form:object Free = FreeMonoid(X, max_length=4).TypeSlashandTypeEffectApplyTypeExprvariants, residuated patterns and effect-typed types are first-class. TheCatPatternAST family is removed (folded intoTypeExpr).chart_fold(lex=, binary=, unary=, start=, depth=, effect_depth=, handlers=)primitive expression, desugared form ofparser(rules=...).unary=is wired through the inside algorithm's reflexive-transitive unary-rule closure.handlers=post-composes effect handlers on the parser output as log-space transition morphisms..curry_right/.curry_leftpostfix combinators witnessing the residuation isomorphisms; backed byquivers.core.morphisms.CurriedMorphism.- Typeclass tower in
quivers.monadic.typeclasses:Functor,Applicative,Monad,Alternative,MonadPlus,Foldable,Traversable,MonadTrans. Concrete monads (FuzzyPowersetMonad,FreeMonoidMonad,GiryMonad) subclassMonaddirectly. - Stdlib effect instances in
quivers.monadic.instances:Identity,Maybe,Alternative_,Continuation,State,Reader,Writer,List. All operations (pure,fmap,apply,join,bind,lift_a2,empty,alt,foldr,traverse) are concrete V-relation realizations; function-space-dependent operations encode[A → B]as a finiteFinSetof cardinality|B|^|A|. Monad transformers inquivers.monadic.transformers:StateT,ReaderT,MaybeT,ContT,WriterT. - Algebraic effects + handlers in
quivers.monadic.algebraic:Operation,EffectSignature,Handler,FreeMonad.FreeMonadcarrier is the bounded-depth signature-tree set realized as a flatFinSetwith structural decomposition via_decompose_carrier_index/_compose_carrier_index;pure,fmap,join,bind,lift_a2satisfy the monad laws up to truncation.Handler.runis the post-order tree fold interpreting each leaf throughreturn_clauseand each operation node throughoperation_clauses.EffectSignature.to_theory()andHandler.as_theory_morphism()realize the panproto-side theory and theory morphism. quivers.monadic.bridges:Kleisli,ArrowMonad,CoKleisli,kleisli,arrow_monad,cokleisliconnecting the monad and arrow towers.Kleisli.composeis fmap-then-join with structural recovery of the underlying B;Kleisli.firstis realized via the canonical monad strengthσ = (pure × id) >> lift_a2(id_{A⊗B});Kleisli.approutes through the Applicative apply.ArrowMonadprovidesfmap/pure/apply/join/bind/lift_a2via the underlying arrow'sarr/id_arr/app/compose.CoKleisliis registered asCategory_; promoting toArrowrequires an explicit comonad costrength supplied viafirst_via_costrength(f, C, costrength).quivers.arrowspackage, Hughes-style arrow hierarchy (Category_,Arrow,ArrowChoice,ArrowApply,ArrowLoop,ArrowZero,ArrowPlus) with panproto-theory mirrors. Newquivers.arrows.instanceswithVRel,Function,Stochasticarrow instances;loop_arrrealized via the V-quantale iterative trace (Joyal-Street-Verity 1996, §3).quivers.stochastic.effect_lifts.class_directed_lifts: class-driven schema lifting for effect-typed parsers.make_swap_schema/swap_rule_setemitswap_TUschemas from registeredDistributiveLawinstances for commutation firings.quivers.core._factoriesmodule, concrete morphism constructorsinj,case,pi,pair,parallel,terminal,constant,distrib_right,coproduct_map. The algebra on which the stdlib monads, arrows, and algebraic-effects layer are built.- New tree-sitter grammar at
grammars/qvr/with regenerated parser; the unified_type_exprfamily subsumes the prior_cat_patternproductions. - Local-grammar override at
quivers.dsl._dev_grammar(activated byQVR_USE_LOCAL_GRAMMAR=1) using panproto 0.47's first-classAstParserRegistry.override_grammar()API. Compiles the in-tree grammar and installs it into the standard registry when the upstreampanproto-grammars-allbundle hasn't yet vendored the latest grammar source. docs/guides/effects.mdanddocs/semantics/effects.md, user guide and formal denotational layer for the typeclass + algebraic-effects framework.quantifier_scope.qvrexample demonstrating Charlow-style scope-taking viaContinuation.
Changed¶
- The
MonadABC is the typeclass-tower one (quivers.monadic.typeclasses.Monad); the previous parallel ABC inquivers.monadic.monadsis removed. Concrete monads provide both the typeclass operations (pure,apply,join) and the Eilenberg–Moore aliases (unit,multiply). RuleDeclpremises and conclusion are typed atTypeExpr(previouslyCatPattern).ObjectDecladmits both: type_exprand= initializerforms.parser(...)infers category atoms from a uniquely-declaredFreeResiduatedin scope when nocategories=argument is supplied.QVR_PROGRAM_PROTOCOLextended withenum_set,free_residuated,schema_declvertex kinds.InsideAlgorithmaccepts an optionalunarymorphism; the chart fills with reflexive-transitive unary-rule closure at each cell.chart_fold(effect_depth>0)no longer raisesCompileError; the parameter flows through as informational metadata and the caller-suppliedbinarymorphism (typically built vialift_rule_setover declared effects) provides the lifted firings.handlers=are post-composed via_ChartHandlerCompositelog-space transitions.- Denotational-semantics docs: corrected marginalization formulas (proper handling of residual input
Y), Kleisli composition ordering inprograms.md(s_1 ⋄ ⋯ ⋄ s_n ⋄ ret, not the reverse), scan formula typing inexpressions.md, profunctor typing ingrammar.md, the row-stochastic/column-stochasticdistinction inmorphisms.md, thearrow_monad ∘ kleisli ≅ idnatural isomorphism ineffects.md.
Fixed¶
FreeMonadcarrier no longer collapses underCoproductSetauto-flattening when the leaf type is itself a coproduct; the flat-FinSetrepresentation preserves the recursive leaf-vs-operation structure.FreeMonad.lift_a2is the correct free-monad applicative recursion (bi-depth(d_a, d_b, d_c)tracking with proper continuation re-encoding), replacing a prior block-identity rule that misrepresented op-summand handling.FreeMonad.joinsplices outer trees correctly through_carrier_op_offset, replacing a prior block-identity that mapped op-summand indices without accounting for the differing inner/outer continuation cardinalities.CoKleisli.firstwas type-incorrect (W(A) × C → B × Cvs the requiredW(A × C) → B × C); now registered asCategory_only, withfirst_via_costrengthfor promotion toArrowwhen a comonad costrength is supplied.List.fmap_objaccepts non-FinSetinputs by re-encoding via cardinality;List(List(A))now type-checks and the monad laws hold on its own image.TypeCoproductwas already correctly handled atresolution.py:119; documented as such.- Bridge round-trip claim in
effects.md§7 corrected from=to≅(Hughes 2000 proves natural isomorphism via the1 ⊗ A ≅ Aunitor, not equality).
Upstream¶
- panproto/panproto#89 closed and shipped in panproto 0.47.0, first-class runtime grammar override via
AstParserRegistry.override_grammar(). - panproto/didactic#38 closed and shipped in didactic 0.7.0,
tuple[Model, ...]field types accept anydx.Modelelement directly (the workaround tuple-of-TaggedUnion-roots is no longer needed). - panproto/didactic#39 closed and shipped in didactic 0.7.0,
dx.field(opaque=True)for fields typed at typeclass ABCs (Monad,ArrowApply, etc.); opaque fields preserve in-process identity throughwith_but drop toNoneon JSON round-trip.
Dependencies¶
panproto >= 0.47.0(was>= 0.45.0).panproto-grammars-all >= 0.47.0(was>= 0.45.0).didactic >= 0.7.1(was>= 0.6.0).
[0.3.0] - 2026-04-12¶
This release provides effects integration: a typeclass + algebraic-effects substrate underneath the DSL, joint type-and-effect schema lifting in the chart parser, and a substantially expanded surface for categorial grammars driven by it.
Surface¶
- Unified type-expression family. The categorial-pattern
sublanguage (
CatPattern,CatPatternSlash,CatPatternProduct) is folded intoTypeExpr. Slash patterns (X/Y,X\Y) parse asTypeSlash; effect-typed applications (T(X),Cont_S(NP)) parse asTypeEffectApply. Rule and schema premises / conclusions are typed atTypeExpruniformly. schemadeclarations. Pattern-polymorphic morphism schemas with explicit parameter types and a unified domain / codomain shape:schema forward_app[X, Y : Cat] : (X/Y) * Y -> X. Arity is derived from the domain shape, a 2-componentTypeProductproduces a binary chart-rule, otherwise unary.EnumSetandFreeResiduatedobject initializers.object Atoms = {NP, S, VP}declares anEnumSet;object Cat = FreeResiduated(Atoms, depth=2, ops=[slash])declares the residuated category universe over anEnumSetof generators, closed under the listed connectives up to a bounded nesting depth.FreeMonoidobject surface.object Strings = FreeMonoid(X, max_length=4)parses, walks, and compiles to a runtimeFreeMonoidcarrier.chart_fold(...)primitive.chart_fold(lex=…, binary=…, unary=…, start=…, depth=…, effect_depth=…)exposes the inside algorithm as a first-class morphism expression. Accepts unary binaries via the reflexive-transitive closure of unary chart cells.- Residuation-witness combinators.
.curry_rightand.curry_leftpostfix methods realize the right / left residuation isomorphism on a binary morphism. Forward / backward application become theorems derivable from identity + curry once joint type-and-effect dispatch fires. aliasdeclarations. Object-shaped aliases (alias Pair = X * Y) bind a resolvedSetObjectin the compiler environment; residuated aliases (alias VP = S \ NP) are stored for syntactic substitution at schema-pattern use sites. Duplicate-declaration and shadowing diagnostics included.bundledeclarations.bundle CCG = [forward_app, backward_app, harmonic_composition]names a tuple of rule references thatparser(rules=…)andchart_fold(binary=…)splice into the rule list. Bundles can reference other bundles; the expander detects cycles.- Doc comments. Lines starting with
##attach to the next declaration that carries a docs field (object,morphism,schema,program,alias,bundle). Plain#line comments continue to be dropped at parse time.
Categorial-effects substrate¶
- Typeclass tower (
quivers.monadic.typeclasses).Functor/Applicative/Monad/Alternative/MonadPlus/Foldable/Traversable/MonadTrans. Each ABC documents its laws; the runtime law-check scaffold lives inlaws.py. - Arrow tower (
quivers.arrows).Category_/Arrow/ArrowChoice/ArrowApply/ArrowLoop/ArrowZero/ArrowPlus(Hughes 2000 §3).ArrowApplyis bridged toMonad;ArrowLoopis the denotational target ofchart_fold's loop. - Stdlib effect instances (
quivers.monadic.instances).Identity,Maybe,Alternative_(Hamblin powerset),Continuation(answer),State(state),Reader(env),Writer(monoid),List(max_length). Each is adx.Modelregistered against its appropriate ABC viaABC.register(...), with concrete V-relation realizations ofpure/fmap/apply/join/bind/lift_a2. - Monad transformers (
quivers.monadic.transformers).StateT(state),ReaderT(env),MaybeT,ContT(answer),WriterT(monoid). - Algebraic effects + handlers (
quivers.monadic.algebraic).Operation,EffectSignature,Handler,FreeMonad(signature)with a bounded-depth flat-FinSet carrier that preserves the recursive leaf-vs-operation structure.Handler.runis a post-order tree fold; a handler is a panproto theory morphism fromsig.to_theory()into the target monad's theory. - Bridges (
quivers.monadic.bridges).Kleisli(monad)wraps aMonadasArrow/ArrowApplywith concrete strength and app;ArrowMonad(arrow)wraps anArrowApplyas aMonad. The Joyal–Street–Verity iterative trace realizesloop_arr.
Chart parser¶
- Class-directed lifts (
quivers.stochastic.effect_lifts.class_directed_lifts). Given a baseSchemaDecland an effect, returns the tuple of lifted schemas keyed by the effect's typeclass interface:Applicative→pure_T/apply_T;Monad→ addsbind_T(Charlow's scope-extruding lift);Alternative→ addsalt_T;MonadPlusis their union. Dispatch is on the typeclass, never the effect's identity, so adding a new effect or a new typeclass extends the lifting machinery automatically. lift_rule_set(base, effects)applies the dispatch matrix pairwise across a rule-set and an effect-stack, returning the union of base + lifted schemas; consumed uniformly by the chart-fold runtime.chart_foldend-to-end. The compiler path drops the previouseffect_depth > 0guard, composes handlers as log-space transitions on the parser output, and emitsswap_TUschemas from registeredDistributiveLawinstances.
Tooling¶
qvr checkCLI (quivers.cli.{__init__,check}). Parse + constraint-solver + compile pipeline over a list of.qvrfiles; structured JSON output via--json; exit codes0/1/2for clean / error / usage. Registered as theqvrconsole script.- Constraint solver (
quivers.dsl.constraints.check_constraints). Walks the parsed AST and reportsresiduated_constraint,effect_constraint, andbundle_unknown_memberviolations without invoking the full compiler. Surfaced viaqvr check. - Pygments lexer (
quivers.dsl.pygments_lexer). Driven by the in-tree tree-sitter parser via the_dev_grammarshim, so the highlighter always reflects the authoritative grammar. - Highlight queries (
grammars/qvr/queries/highlights.scm) refreshed forschema_decl,type_slash,type_effect_apply,chart_fold,curry_right/curry_left,EnumSet/FreeResiduated/FreeMonoid,alias_decl,bundle_decl. - GitHub linguist (
.gitattributes)..qvrclassified as detectable; tree-sitter generated artefacts marked vendored / generated.
Examples + documentation¶
quantifier_scope.qvr: Charlow-style scope-taking grammar using theContinuationeffect, exercising the new surface (EnumSet,FreeResiduated,schema,Cont_S(X)) end-to-end.docs/guides/effects.md: typeclass + algebraic-effects framework: monad and arrow towers, stdlib effect instances, class-driven schema lifting, joint type-and-effect dispatch, bridges between the two towers.docs/semantics/effects.md: denotational layer: panproto- theory mirrors of each typeclass, effect-typed schema denotations as natural transformations, joint type-and-effect chart dispatch, lifting-adequacy theorem, conservativity over the bare-grammar fragment.docs/getting-started/architecture.md:quivers/arrows/section added;quivers/monadic/rewritten to cover the typeclass spine, transformers, algebraic effects, bridges, theories, laws.- mkdocstrings stubs for every new public module:
quivers.monadic.{typeclasses,instances,transformers,algebraic,bridges,theories,laws},quivers.arrows.{typeclasses,theories},quivers.stochastic.effect_lifts.
Internal¶
- Program theory (
quivers.dsl.program_theory). Vertex kinds extended withenum_set,free_residuated,schema_decl.write_set_objectbranches forEnumSet(name + element constraints) andFreeResiduated(depth + op constraints +generatorsedge to anenum_set). - Local-grammar override (
quivers.dsl._dev_grammar). Adopts panproto 0.47's first-classAstParserRegistry.override_grammarAPI, replacing the previous ctypes-pinned-buffer shim. Activated byQVR_USE_LOCAL_GRAMMAR=1untilpanproto-grammars-allvendors the new QVR grammar. quivers.core._factories: the concrete morphism alphabet (coproduct injection / case eliminator, product projection / pairing, parallel pair, distributivity, terminal, constant, coproduct functorial action) on which the typeclass realizations are built.- Legacy
MonadABC removed.quivers.monadic.monadsno longer ships a parallelMonad;FuzzyPowersetMonad,FreeMonoidMonad, andGiryMonadsubclass the typeclassMonaddirectly. The Eilenberg–Moore aliases (unit,multiply,kleisli_compose) are kept as convenience methods.FreeMonoidMonad.joinnow does the full word-concatenation tensor with truncation beyondmax_length.
Dependencies¶
panproto >= 0.47.0(was>= 0.45.0).panproto-grammars-all >= 0.47.0(was>= 0.45.0).didactic >= 0.7.0(was>= 0.6.0).
Tests¶
- 986 tests pass under
QVR_USE_LOCAL_GRAMMAR=1. New surface coverage: 32 cases intests/test_dsl_extensions.py(aliases, bundles, doc comments, FreeMonoid surface, constraint solver,qvr checkCLI, highlight-query parity, Pygments lexer round-trips, grammar-shape parity across every example). - Monad unit laws hold on
Identity,Maybe,Alternative_,State,Reader,Writer;FreeMonadleft / right unit laws hold up to truncation; trace yanking and identity verified onVRel.
Key citations¶
- Charlow, S. (2025). Static and dynamic exceptional scope. Journal of Semantics, 42(4), 353–398.
- Bumford, D. and Charlow, S. (forthcoming, 2026). Effect-Driven Interpretation: Functors for Natural Language Composition. Elements in Semantics, Cambridge University Press. Online ISBN 9781009285377; preprint arXiv:2504.00316.
- Hughes, J. (2000). Generalising monads to arrows. Science of Computer Programming 37(1–3):67–111.
- Bauer, A. and Pretnar, M. (2015). Programming with algebraic effects and handlers. Journal of Logical and Algebraic Methods in Programming 84(1):108–123.
- Plotkin, G. and Power, J. (2003). Algebraic operations and generic effects. Applied Categorical Structures 11(1):69–94.
- Joyal, A., Street, R., and Verity, D. (1996). Traced monoidal categories. Mathematical Proceedings of the Cambridge Philosophical Society 119(3):447–468.
- McBride, C. and Paterson, R. (2008). Applicative programming with effects. Journal of Functional Programming 18(1):1–13.
[0.2.0] - 2026-05-06¶
Changed¶
- Every record-shaped value type (AST nodes,
FinSet,ProductSet,CoproductSet,ContinuousSpacevariants,Categoryvariants,RuleSystem) is now adidactic.api.Model. Recursive sums aredx.TaggedUnionroots discriminated by akind: Literal[...]field. JSON round-trips viamodel_dump_json/model_validate_jsonare available on every value type. - Resolution from
TypeExpr/SpaceExprAST trees to runtimeSetObject/ContinuousSpacevalues is expressed as adx.Lensfamily inquivers.dsl.resolution. - Variadic constructors
ProductSet(A, B, C)andCoproductSet(A, B, C)are replaced by keyword formProductSet(components=(A, B, C))andCoproductSet(components=(A, B, C)). The flattening converter preserves the previous flattening behavior. - Continuous spaces (
Euclidean,Simplex,PositiveReals,ProductSpace) expose publicnameanddimfields (no longer private with property accessors). - Minimum supported Python is now 3.14.
Added¶
- A tree-sitter grammar for the QVR DSL at
grammars/qvr/, registered with panproto'spanproto-grammars-alldistribution. quivers.dsl.parserdelegates parsing to panproto via theqvrtree-sitter grammar.quivers.dsl.program_theorydefinesQVR_PROGRAM_PROTOCOLandextract_program_schema, lifting every compiled program to a panprotoSchemafor use withpanproto schema diff,panproto lens generate, and related tooling.- A
Denotational Semanticsdocumentation section giving a formal, compositional semantics for the DSL across the discrete, stochastic, and continuous strata, plus an adequacy theorem connecting the compiler implementation to the denotation. RuleSystemcarries cross-field axioms (__axioms__) ensuringbinary_weights/unary_weightslengths matchbinary_rules/unary_ruleswhen supplied..github/workflows/release.ymlbuilds an sdist + wheel on tag push and publishes to PyPI via the quivers-dev/quivers OIDC trusted publisher.- Pull request template under
.github/PULL_REQUEST_TEMPLATE.mdand issue templates under.github/ISSUE_TEMPLATE/.
Removed¶
quivers.dsl.lexerandquivers.dsl.tokens: the hand-written lexer is replaced by panproto's tree-sitter–integrated lexing.LexError: lexical errors now surface asParseError.
[0.1.0] - 2026-03-26¶
Added¶
Core Categorical Algebra¶
- Fundamental category types and morphisms
- Object declarations and morphism composition
- Support for latent and observed morphisms
- Basic categorical operations and abstractions
Stochastic Morphisms¶
- Stochastic morphism declarations and semantics
- Integration with probability theory
- Support for morphism composition in stochastic settings
Continuous Distributions (30+ Families)¶
- Normal distribution and variants (LogitNormal, TruncatedNormal)
- Beta, Dirichlet for probability simplices
- Exponential family: Exponential, Gamma, Chi2
- Heavy-tailed: Cauchy, StudentT, Pareto
- Bounded: Uniform, Kumaraswamy
- Half-variants: HalfCauchy, HalfNormal
- Transformed: LogNormal, Gumbel, Laplace, Weibull
- Multivariate: MultivariateNormal, LowRankMVN, Wishart
- Bernoulli variants: Bernoulli, ContinuousBernoulli, RelaxedBernoulli
- Advanced: RelaxedOneHotCategorical, FisherSnedecor
- Normalized flows: Flow
- Categorical and discrete approximations
Monadic Programs¶
- Draw statements for sampling from morphisms
- Observe statements for conditioning and likelihood
- Return statements with optional labeled outputs
- Variable binding and destructuring in patterns
- Program parameters and composition
QVR DSL¶
- Complete lexer with token recognition for all language constructs
- Recursive descent parser with full grammar support
- Abstract syntax tree (AST) node definitions
- Program block execution with proper scoping
- Let bindings for expression computation
- Built-in let functions: sigmoid, exp, log, abs, softplus
- Comment support (#)
- Type expressions: products (*), coproducts (+)
- Expression operators: composition (>>), tensor product (@), marginalization
- Indentation-aware program body parsing
- Specialized handling for draw/observe arguments
Variational Inference Layer¶
- Inference interface for probabilistic programs
- Support for approximate posterior computation
- Integration with continuous distribution families