Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1a79e69
docs: add July 2026 audit findings document (FG-01..FG-64)
alexnodeland Jul 11, 2026
24b2760
audit(f-dist): fix distribution log-density guards, boundaries, cachi…
alexnodeland Jul 11, 2026
fdd1997
audit(f-vi): fix VI scale optimization, CRN gradients, support-aware …
alexnodeland Jul 11, 2026
3fd1f27
audit(f-runtime): stack-safe interpreter, structure-varying scoring, …
alexnodeland Jul 11, 2026
08a0ac4
audit(f-smc): fix SMC/ABC importance weights, add tempered SMC + evid…
alexnodeland Jul 11, 2026
9fc7404
merge wp/f-dist
alexnodeland Jul 11, 2026
2d0ebfe
merge wp/f-runtime
alexnodeland Jul 11, 2026
68bf537
merge wp/f-smc
alexnodeland Jul 11, 2026
17aaaaf
merge wp/f-vi
alexnodeland Jul 11, 2026
223c003
audit(f-mcmc): correct MH proposal corrections, normalized/multi-chai…
alexnodeland Jul 11, 2026
3ebfbc4
audit(f-perf): Address Arc<str> + cut dead memory subsystem + real e2…
alexnodeland Jul 11, 2026
7b3a2db
audit(f-hmc): add HMC gradient inference + 7 distributions, wire i64 …
alexnodeland Jul 11, 2026
965b7e1
audit(f-docs): honest positioning, SMC/ABC/VI examples, error taxonom…
alexnodeland Jul 11, 2026
c7c866b
audit(f-tests): sampler GOF coverage, exercise analytical-posterior h…
alexnodeland Jul 11, 2026
ebe9a6b
merge wp/f-tests
alexnodeland Jul 11, 2026
dac7859
merge wp/f-docs
alexnodeland Jul 11, 2026
a4e3629
audit(gate): fix Address field access in complex-models example
alexnodeland Jul 11, 2026
769612f
audit(gate): fix duplicate group-address sampling in hierarchical exa…
alexnodeland Jul 11, 2026
d6bebba
audit(f-validate): implement Validate for all exported distributions …
alexnodeland Jul 11, 2026
66c3112
audit(fixup): address re-verification findings
alexnodeland Jul 11, 2026
7924f90
audit(fixup): DiscreteUniform full-range, site-cache set invalidation…
alexnodeland Jul 11, 2026
5821d26
docs: fill all 64 audit resolutions + re-verification verdicts (remed…
alexnodeland Jul 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Complete recording of random choices and log-weights.
- Type-safe value access with `get_f64()`, `get_bool()`, `get_u64()`, `get_usize()`.
- Three-component log-weight decomposition (prior, likelihood, factors).
- **Memory optimization**:
- Copy-on-write traces (`CowTrace`) for efficient MCMC proposals.
- Object pooling (`TracePool`) for zero-allocation inference.
- Efficient trace construction (`TraceBuilder`).
- **Production features**:
- Comprehensive error handling with `FugueError` and error codes.
- Numerically stable algorithms with overflow protection.
Expand Down
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,45 @@ jobs:
# - name: mdBook tests (docs/)
# if: hashFiles('docs/**/*.md') != ''
# run: mdbook test docs

msrv:
name: MSRV (1.87.0)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4

# Pins the exact toolchain `rust-version = "1.87"` in Cargo.toml claims
# (finding FG-51). If this drifts above what the crate actually needs,
# bump both together.
- name: Setup Rust 1.87.0 (MSRV)
uses: dtolnay/rust-toolchain@1.87.0

- name: Cache cargo
uses: Swatinem/rust-cache@v2
with:
workspaces: . -> target
key: msrv-1.87.0

- name: Scope the manifest to the published library
run: |
# MSRV is a claim about the *published library* (`[dependencies]`),
# not about maintainer tooling in `[dev-dependencies]` (mdbook + its
# plugins, criterion, proptest, ...): those crates have their own,
# higher MSRV floors and are never pulled in by a downstream
# `cargo add fugue-ppl`. This is an ephemeral CI checkout, so
# truncating the manifest at `[dev-dependencies]` here commits
# nothing back and only scopes *this* check to what actually ships.
sed -i '/^\[dev-dependencies\]/,$d' Cargo.toml
# The committed Cargo.lock may be a newer lock-file format than an
# older cargo/rustc pair can parse; regenerating against the
# trimmed manifest above is required (and sufficient -- verified
# locally: `[dependencies]` resolves and builds cleanly on
# rustc 1.87.0 with no other changes, and `cargo clippy
# --all-targets --all-features` with `rust-version = "1.87"` finds
# zero `incompatible_msrv` diagnostics anywhere in the crate).
rm -f Cargo.lock

- name: cargo check --lib (rustc 1.87.0)
run: cargo check --lib
1,023 changes: 1,023 additions & 0 deletions AUDIT-2026-07.md

Large diffs are not rendered by default.

96 changes: 96 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
(pre-1.0: see the API-stability note in `README.md`'s Roadmap section).

For the initial 0.1.0 release notes, see `.github/CHANGELOG.md`.

## [Unreleased]

The entries below summarize a full-crate audit remediation (170 findings,
tracked as `FG-01` .. `FG-64` in the project's audit record) organized by
area. Each bullet range names the finding IDs addressed in that area; a later
pass appends the individual per-finding change lines under each heading.

### Correctness — MCMC / Metropolis-Hastings (FG-01, FG-02, FG-10 – FG-12, FG-35 – FG-42, FG-57)

Proposal-distribution corrections, normalized and multi-chain effective
sample size, split-R-hat, autocorrelation/Geweke diagnostics, and removal of
redundant recomputation in the adaptive MH sampler.

### Correctness — Sequential Monte Carlo (FG-03, FG-13, FG-43, FG-58, FG-59)

Prior-cancelled (not prior-squared) importance weights, weight-preserving
rejuvenation, no terminal resample, an unbiased log-evidence estimate, and a
move-not-clone particle construction path.

### Correctness — Approximate Bayesian Computation (FG-09, FG-34)

Importance-weighted ABC-SMC (replacing a biased prior-replacement heuristic)
with bounded, typed-error attempt budgets instead of unbounded loops or
panics on an empty population.

### Correctness — Variational Inference (FG-04, FG-16, FG-17, FG-18, FG-44, FG-46, FG-60)

Support-matched guide families (Normal/LogNormal/Beta) instead of a
one-size-fits-all Normal, both location *and* scale optimized via
common-random-numbers finite-difference gradients, an ELBO-plateau
convergence test, a corrected (non-double-counted) prior-baseline ELBO, and
exact (not moment-matched) Beta sampling.

### New — Hamiltonian Monte Carlo (FG-31) and expanded distribution coverage

A new gradient-based (finite-difference force, exact Metropolis correction)
HMC kernel, plus seven new distributions (StudentT, Cauchy, Laplace, Weibull,
ChiSquared, InverseGamma, DiscreteUniform) bringing the total to 17.

### Runtime / handler correctness (FG-47 and related)

Duplicate-address and structure-mismatch detection in the replay/scoring
interpreters now returns a typed `FugueError` (`AddressConflict`,
`UnexpectedModelStructure`) instead of panicking.

### Performance (FG-05, FG-22, FG-24, FG-62 – FG-64)

`Arc<str>` addressing, removal of a dead memory-pooling subsystem, and
realistic end-to-end benchmarks in place of micro-benchmarks that didn't
reflect actual usage.

### Documentation, examples, and API surface hygiene (FG-23, FG-25, FG-33, FG-50, FG-51)

- **FG-23**: Replaced the "production-ready" tagline (README, mdBook home
page, and a stale duplicate landing page) with accurate positioning:
type-safe, monadic, pre-1.0, actively developed. Added an explicit
pre-1.0 SemVer policy note.
- **FG-25**: Added `examples/smc_inference.rs`, `examples/abc_inference.rs`,
and `examples/vi_inference.rs` — the first examples anywhere in the crate
(README, `examples/`, or mdBook) to exercise `adaptive_smc`,
`abc_smc_weighted`, and `optimize_meanfield_vi_with_config`, each checked
against a closed-form posterior. Wired into a new mdBook "Advanced
Inference" tutorial section. Added `hmc_chain` to the README's example
index.
- **FG-33**: Removed 11 of 22 `ErrorCode` variants (and the `FugueError`
variants/constructors/macro that existed only to hold them) that no code
path in the crate ever constructed: `NumericalOverflow`,
`NumericalUnderflow`, `NumericalInstability`, `InvalidLogDensity`,
`ModelExecutionFailed`, `InferenceConvergenceFailed`,
`InsufficientSamples`, `InvalidInferenceConfig`, `TraceCorrupted`,
`TraceReplayFailed`, `UnsupportedType`. The 11 surviving codes are each
verified live (grepped construction sites) in `src/error.rs`'s module
docs. ABC and VI's own failure modes (`ABCError`, `GuideError`) keep their
dedicated, more precise error types rather than being folded into this
general enum.
- **FG-50**: README/mdBook now state the exact distribution count (17,
enumerated) instead of the ambiguous "10+".
- **FG-51**: The README's unverified "1.70+" claim was wrong: real
`rustc 1.70.0` fails to build the crate (an `E0659` ambiguous-name error on
`pub mod core` vs. the `core` extern-prelude crate, and
`usize::is_multiple_of`, stable only since 1.87.0). Pinned the verified
floor, `rust-version = "1.87"`, in `Cargo.toml`, corrected the README/mdBook
badges accordingly, and added a dedicated MSRV job to
`.github/workflows/ci.yml` that actually builds against `rustc 1.87.0`.

[Unreleased]: https://github.com/alexnodeland/fugue/compare/v0.1.0...HEAD
21 changes: 18 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,23 @@
name = "fugue-ppl"
version = "0.1.0"
edition = "2021"
# Finding FG-51: the README previously claimed "1.70+" with nothing pinning or
# verifying it. Verified empirically against real toolchains (not just
# clippy's `incompatible_msrv` lint, which agrees): rustc 1.70.0 actually
# fails to compile this crate for two independent reasons -- an `E0659`
# ambiguous-name error on `pub mod core` vs. the `core` extern-prelude crate
# (resolved by later rustc versions), and `usize::is_multiple_of` in
# `src/inference/abc.rs`, stable only since 1.87.0. rustc 1.87.0 compiles
# `[dependencies]` (the graph a downstream consumer actually pulls in)
# cleanly with zero `incompatible_msrv` findings anywhere in the crate.
# dev-dependencies (mdbook + plugins, criterion, proptest, ...) are NOT held
# to this floor -- they're maintainer tooling, not part of the published
# library -- so the MSRV CI job (.github/workflows/ci.yml) checks `--lib`
# only, against a temporarily dev-dependency-free manifest. See that job's
# comments for why.
rust-version = "1.87"
license = "MIT"
description = "Production-ready monadic PPL with numerically stable inference, comprehensive diagnostics, and memory optimization."
description = "Monadic PPL with numerically stable inference and comprehensive diagnostics."
readme = "README.md"
keywords = ["probability-monad", "ppl", "monad", "effects", "traces"]
categories = ["science", "mathematics"]
Expand Down Expand Up @@ -44,9 +59,9 @@ mdbook-linkcheck = "0.7.7"
mdbook-toc = "0.14.2"

[[bench]]
name = "memory_benchmarks"
name = "mcmc_benchmarks"
harness = false

[[bench]]
name = "mcmc_benchmarks"
name = "f_perf"
harness = false
35 changes: 25 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

<img src="assets/fugue-logo.svg" alt="Fugue Logo" width="200" height="200">

**A production-ready, monadic probabilistic programming library for Rust**
**A type-safe, monadic probabilistic programming library for Rust — pre-1.0 and actively developed**

*Write elegant probabilistic programs by composing `Model` values in direct style; execute them with pluggable interpreters and state-of-the-art inference algorithms.*

[![Rust](https://img.shields.io/badge/rust-1.70%2B-blue.svg)](https://www.rust-lang.org)
[![Rust](https://img.shields.io/badge/rust-1.87%2B-blue.svg)](https://www.rust-lang.org)
[![Crates.io](https://img.shields.io/crates/v/fugue-ppl.svg)](https://crates.io/crates/fugue-ppl)
[![Dev Docs](https://docs.rs/fugue-ppl/badge.svg)](https://docs.rs/fugue-ppl)
[![User Docs](https://img.shields.io/badge/guides-fugue.run-blue)](https://fugue.run)
Expand All @@ -20,26 +20,34 @@
[![Discord](https://img.shields.io/discord/1412802057437712426?logo=discord&label=discord)](https://discord.gg/QAcF7Nwr)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/alexnodeland/fugue)

**Supported Rust:** 1.70+ • **Platforms:** Linux / macOS / Windows • **Crate:** [`fugue-ppl` on crates.io](https://crates.io/crates/fugue-ppl)
**Supported Rust:** 1.87+ • **Platforms:** Linux / macOS / Windows • **Crate:** [`fugue-ppl` on crates.io](https://crates.io/crates/fugue-ppl)

</div>

## ✨ Features

- **Monadic PPL**: Compose probabilistic programs using pure functional abstractions
- **Type-Safe Distributions**: 10+ built-in probability distributions with natural return types
- **Multiple Inference Methods**: MCMC, SMC, Variational Inference, ABC
- **Type-Safe Distributions**: 17 built-in probability distributions with natural return types
- **Multiple Inference Methods**: MCMC, HMC, SMC, Variational Inference, ABC
- **Comprehensive Diagnostics**: R-hat convergence, effective sample size, validation
- **Production Ready**: Numerically stable algorithms with memory optimization
- **Numerically Stable**: Log-space computations throughout for robust probability arithmetic
- **Ergonomic Macros**: Do-notation (`prob!`), vectorization (`plate!`), addressing (`addr!`)

## 🤔 Why Fugue?

- 🔒 **Type-safe distributions**: natural return types (Bernoulli → `bool`, Poisson/Binomial → `u64`, Categorical → `usize`)
- 🧩 **Direct-style, monadic design**: compose `Model<T>` values with `bind/map` for explicit, readable control flow
- 🔌 **Pluggable interpreters**: prior sampling, replay, scoring, and safe variants for production robustness
- 📊 **Production diagnostics**: R-hat, ESS, validation utilities, and robust error handling
- ⚡ **Performance-minded**: memory pooling, copy-on-write traces, and numerically stable computations
- 🔌 **Pluggable interpreters**: prior sampling, replay, scoring, and safe variants
- 📊 **Diagnostics**: R-hat, ESS, validation utilities, and a structured error taxonomy (see [`error`](https://docs.rs/fugue-ppl/latest/fugue/error/))
- ⚡ **Performance-minded**: O(1), allocation-free address clones (`Arc<str>` with a cached hash) and numerically stable log-space computations

## 📦 Distributions

Bernoulli, Beta, Binomial, Categorical, Cauchy, ChiSquared, DiscreteUniform, Exponential, Gamma, InverseGamma, Laplace, LogNormal, Normal, Poisson, StudentT, Uniform, Weibull — 17 in total, each with natural return types and validated parameters.

## 🧪 Where Fugue stands today

Fugue is 0.1.x: pre-1.0, actively developed, with no SemVer stability guarantee yet and a single primary maintainer (see Roadmap, below). It's extensively tested — hundreds of unit, integration, and property-based tests, including statistical regression tests against closed-form posteriors — but that's not the same claim as "production-ready." Treat it as a serious, honestly-scoped research-grade PPL: pin an exact version, read the [CHANGELOG](CHANGELOG.md) before upgrading, and expect breaking API changes between 0.1.x releases as the design settles.

## 📦 Installation

Expand Down Expand Up @@ -80,7 +88,12 @@ let mu_values: Vec<f64> = samples.iter()

- **[User Guide](https://fugue.run/)** - Comprehensive tutorials and examples
- **[API Reference](https://docs.rs/fugue-ppl/latest/fugue/)** - Complete API documentation
- **Examples** - See `examples/` directory
- **Examples** - See the `examples/` directory, including one runnable example per inference method:
- `adaptive_mcmc_chain` - most foundation/statistical-modeling examples (e.g. `bayesian_coin_flip.rs`)
- `hmc_chain` (HMC) - see the [`hmc` module rustdoc](https://docs.rs/fugue-ppl/latest/fugue/inference/hmc/) for a runnable doctest
- `adaptive_smc` (SMC) - `examples/smc_inference.rs`
- `abc_smc_weighted` (ABC) - `examples/abc_inference.rs`
- `optimize_meanfield_vi_with_config` (VI) - `examples/vi_inference.rs`
- **[References](https://www.zotero.org/groups/6138134/fugue/library)** - Zotero library for Fugue

## 🤝 Community
Expand All @@ -100,6 +113,8 @@ Planned focus areas:
- API refinements and stability guarantees
- Improved documentation, diagnostics, and examples

**API stability / SemVer policy:** Fugue follows [Cargo's pre-1.0 SemVer convention](https://doc.rust-lang.org/cargo/reference/semver.html#change-categories): any `0.x.y -> 0.(x+1).0` bump may contain breaking changes, and `0.x.y -> 0.x.(y+1)` is additive/non-breaking. There is no 1.0 stability commitment yet; always pin an exact version and read the [CHANGELOG](CHANGELOG.md) before upgrading the minor version.

## 🤝 Contributing

Contributions welcome! See our [contributing guidelines](.github/CONTRIBUTING.md).
Expand Down
33 changes: 19 additions & 14 deletions benches/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,28 +144,33 @@ criterion_group!(mcmc_benches, benchmark_mcmc_throughput);
criterion_main!(mcmc_benches);
```

### Memory Benchmark Patterns
### End-to-End Benchmark Patterns

Benchmark the *shipped* inference entry points on a representative model (see
`benches/f_perf.rs`), not isolated bookkeeping utilities. The former memory
subsystem (`TracePool`/`PooledPriorHandler`/`CowTrace`/`TraceBuilder`) was
removed after `f_perf`'s `pooling_evidence` group showed <4% end-to-end benefit.

```rust
//! # Memory Usage Benchmarks
//!
//! Measures memory allocation patterns and validates memory optimization
//! strategies for high-throughput scenarios.
//! # End-to-End Inference Benchmarks
//!
//! Measures the real per-step and per-N cost of the functions users call.

use criterion::{black_box, criterion_group, criterion_main, Criterion};
use fugue::runtime::memory::{TracePool, PooledPriorHandler};
use fugue::runtime::interpreters::PriorHandler;
use fugue::*;

fn benchmark_trace_pooling(c: &mut Criterion) {
c.bench_function("trace_pooling_vs_allocation", |b| {
fn benchmark_prior_execution(c: &mut Criterion) {
c.bench_function("prior_handler_execution", |b| {
b.iter(|| {
let mut rng = StdRng::seed_from_u64(42);
let mut pool = TracePool::new(100);

// Benchmark pooled allocation pattern

// Benchmark the shipped PriorHandler over a representative model
for _ in 0..1000 {
let handler = PooledPriorHandler::new(&mut rng, &mut pool);
let model = simple_test_model();
let result = runtime::handler::run(handler, model);
let (result, _trace) = runtime::handler::run(
PriorHandler { rng: &mut rng, trace: Trace::default() },
simple_test_model(),
);
black_box(result);
}
});
Expand Down
Loading
Loading