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.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 saturation-free 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. Seenotes/algebra-guided-training-tooling.mdfor the broader roadmap.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 saturation-free init 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_specmaterialises 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:let phi = softmax(B) >>> expectation let g = f.change_base(phi)
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:from quivers.stochastic.stdlib import Datalog view = Datalog(edge_axioms) reaches = view.enumerate(("reach", source, Wildcard("Y"))) -
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 (advance article).
- 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). Generalizing 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 FACTSlab/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