Skip to content

feat: split rebuild_leaf from leaf_signature - #346

Closed
seabbs-bot wants to merge 3 commits into
mainfrom
feat/rebuild-leaf-split
Closed

feat: split rebuild_leaf from leaf_signature#346
seabbs-bot wants to merge 3 commits into
mainfrom
feat/rebuild-leaf-split

Conversation

@seabbs-bot

@seabbs-bot seabbs-bot commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Splits the two questions leaf_ctor currently fuses: rebuild_leaf(leaf, vals) is the reconstruction hook (a plain method, no egal-stable callable struct needed), and leaf_signature(leaf) is the tie-identity hook, which now carries the egal-stability contract explicitly. Both peel wrapper layers, so an inner-leaf override is honoured under truncated/censored/uncertain wrappers — review caught that the naive defaults silently bypassed overrides and could tie two incompatible wrapped leaves into one parameter; both defects are fixed and pinned by a test that fails without the peeling. The signature keeps parameter names from the outer leaf so wrapper-owned extras survive.

This strengthens the instance-level leaf protocol that the staged codec change (runtime dispatch replacing the leaf registries) leans on. Gates: test-fast 1616/1616, quality clean post-format.

This was opened by a bot. Please ping @seabbs for any questions.

Refs #332. Refs EpiAware/ModifiedDistributions.jl#142.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Try this Pull Request!

Option 1: Julia Package Manager

Open Julia and type:

import Pkg
Pkg.activate(temp=true)
Pkg.add(url="https://github.com/EpiAware/ComposedDistributions.jl", rev="feat/rebuild-leaf-split")
using ComposedDistributions

Option 2: Local Checkout

If you have the repo locally:

git checkout feat/rebuild-leaf-split
julia --project=. -e "using Pkg; Pkg.instantiate()"

@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Flag Coverage Δ
ad-enzyme-forward 0.00% <0.00%> (ø)
ad-enzyme-reverse 14.24% <45.45%> (+0.06%) ⬆️
ad-forwarddiff 12.62% <45.45%> (+0.03%) ⬆️
ad-mooncake-forward 0.00% <0.00%> (ø)
ad-mooncake-reverse 12.82% <45.45%> (+0.03%) ⬆️
ad-reversediff 12.62% <45.45%> (+0.03%) ⬆️
unit 86.90% <90.90%> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/composers/Shared.jl 63.75% <100.00%> (+1.25%) ⬆️
src/composers/introspection.jl 85.13% <100.00%> (+0.19%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

📖 Documentation preview is ready!

View the docs for this PR at: https://EpiAware.github.io/ComposedDistributions.jl/previews/PR346/

This preview will be updated automatically when you push new commits.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Benchmark comparison vs base

Minimum time per call. Buckets are PR time as a % of base, so lower is faster (🟢 faster, ⚪ within 5%, 🔴 slower). Counts of benchmarks per bucket:

Group 🟢 <50% 🟢 50–75% 🟢 75–95% ⚪ 95–105% 🔴 105–125% 🔴 125–150% 🔴 >150%
Evaluation · · 3 7 5 · 2
ForwardDiff · · 1 6 2 · ·
ReverseDiff (tape) · · 1 7 1 · ·
Mooncake reverse · 1 7 1 · · ·
Enzyme reverse · · · 8 1 · ·
Evaluation — 17 benchmarks (by time change)
Benchmark base PR time memory
Composition / Choose / construct 20.0 ns 30.0 ns 🔴 1.5×
Composition / Compete / construct 20.0 ns 30.0 ns 🔴 1.5×
Composition / Choose / logpdf 49.0 ns 60.0 ns 🔴 1.22×
Composition / Parallel / logpdf 100.0 ns 110.0 ns 🔴 1.1× ⚪ 1.0×
Composition / Compete / logccdf 320.0 ns 350.0 ns 🔴 1.09×
Composition / Sequential / rand 1.25 μs 1.15 μs 🟢 0.92× ⚪ 1.0×
Composition / Parallel / rand 1.25 μs 1.17 μs 🟢 0.94× ⚪ 1.0×
Composition / Compete / rand 510.0 ns 540.0 ns 🔴 1.06× ⚪ 1.0×
Composition / Resolve / rand 501.0 ns 530.0 ns 🔴 1.06× ⚪ 1.0×
Composition / Parallel / construct 1.42 μs 1.34 μs 🟢 0.94× ⚪ 1.0×
Composition / Nested / logpdf 1.66 μs 1.58 μs ⚪ 0.95× ⚪ 1.0×
Composition / Sequential / construct 1.42 μs 1.37 μs ⚪ 0.96× ⚪ 1.0×
Composition / Resolve / construct 29.0 ns 30.0 ns ⚪ 1.03×
Composition / Nested / rand 4.23 μs 4.35 μs ⚪ 1.03× ⚪ 1.0×
Composition / Nested / compose 2.68 μs 2.64 μs ⚪ 0.98× ⚪ 1.0×
Composition / Resolve / logpdf 140.0 ns 140.0 ns ⚪ 1.0×
Composition / Sequential / logpdf 100.0 ns 100.0 ns ⚪ 1.0× ⚪ 1.0×
AD gradients — 36 benchmarks (by time change)
Benchmark base PR time memory
AD gradients / Shared-tag unflatten/update codec / Mooncake reverse 114.8 μs 85.42 μs 🟢 0.74× ⚪ 1.01×
AD gradients / Sequential Gamma+LogNormal logpdf / Mooncake reverse 381.22 μs 298.38 μs 🟢 0.78× ⚪ 1.0×
AD gradients / Censored leaf marginal logpdf / Mooncake reverse 423.61 μs 333.33 μs 🟢 0.79× ⚪ 1.0×
AD gradients / Resolve stick-breaking branch-prob logpdf / Mooncake reverse 99.35 μs 88.25 μs 🟢 0.89× ⚪ 1.0×
AD gradients / Sequential Gamma+LogNormal logpdf / ForwardDiff 11.1 μs 12.17 μs 🔴 1.1× ⚪ 1.0×
AD gradients / Resolve mixture marginal logpdf / Mooncake reverse 96.04 μs 86.93 μs 🟢 0.91× ⚪ 1.0×
AD gradients / Sequential Gamma+LogNormal logpdf / ReverseDiff (tape) 27.17 μs 29.22 μs 🔴 1.08× ⚪ 1.0×
AD gradients / Truncated uncertain-leaf unflatten/update codec / ReverseDiff (tape) 12.79 μs 11.98 μs 🟢 0.94× ⚪ 1.0×
AD gradients / Choose selected-branch logpdf / Mooncake reverse 17.68 μs 16.59 μs 🟢 0.94× ⚪ 1.0×
AD gradients / Truncated uncertain-leaf unflatten/update codec / Mooncake reverse 145.98 μs 137.31 μs 🟢 0.94× ⚪ 1.0×
AD gradients / Shared-tag unflatten/update codec / Enzyme reverse 7.75 μs 8.21 μs 🔴 1.06× ⚪ 1.0×
AD gradients / Truncated uncertain-leaf unflatten/update codec / ForwardDiff 3.95 μs 4.18 μs 🔴 1.06× ⚪ 1.0×
AD gradients / Compete racing-hazard marginal logpdf / Mooncake reverse 55.39 μs 52.38 μs 🟢 0.95× ⚪ 1.0×
AD gradients / Censored leaf marginal logpdf / ForwardDiff 11.24 μs 10.68 μs 🟢 0.95× ⚪ 1.0×
AD gradients / Truncated uncertain-leaf unflatten/update codec / Enzyme reverse 19.28 μs 18.54 μs ⚪ 0.96× ⚪ 1.0×
AD gradients / Resolve stick-breaking branch-prob logpdf / ReverseDiff (tape) 54.62 μs 52.67 μs ⚪ 0.96× ⚪ 1.0×
AD gradients / Compete racing-hazard marginal logpdf / ReverseDiff (tape) 49.86 μs 48.23 μs ⚪ 0.97× ⚪ 1.0×
AD gradients / Choose selected-branch logpdf / Enzyme reverse 3.31 μs 3.2 μs ⚪ 0.97× ⚪ 1.0×
AD gradients / Compete racing-hazard marginal logpdf / Enzyme reverse 9.86 μs 10.15 μs ⚪ 1.03× ⚪ 1.0×
AD gradients / Pool non-centred reconstruction logpdf / ForwardDiff 1.01 μs 981.0 ns ⚪ 0.97× ⚪ 1.0×
AD gradients / Censored leaf marginal logpdf / Enzyme reverse 130.02 μs 126.29 μs ⚪ 0.97× ⚪ 1.0×
AD gradients / Pool non-centred reconstruction logpdf / Enzyme reverse 1.94 μs 1.89 μs ⚪ 0.97× ⚪ 1.0×
AD gradients / Resolve mixture marginal logpdf / ReverseDiff (tape) 62.99 μs 61.34 μs ⚪ 0.97× ⚪ 1.0×
AD gradients / Pool non-centred reconstruction logpdf / ReverseDiff (tape) 25.43 μs 24.9 μs ⚪ 0.98× ⚪ 1.0×
AD gradients / Shared-tag unflatten/update codec / ForwardDiff 1.77 μs 1.74 μs ⚪ 0.98× ⚪ 1.0×
AD gradients / Sequential Gamma+LogNormal logpdf / Enzyme reverse 103.88 μs 102.06 μs ⚪ 0.98× ⚪ 1.0×
AD gradients / Compete racing-hazard marginal logpdf / ForwardDiff 7.84 μs 7.71 μs ⚪ 0.98× ⚪ 1.0×
AD gradients / Choose selected-branch logpdf / ForwardDiff 671.0 ns 681.0 ns ⚪ 1.01× ⚪ 1.0×
AD gradients / Censored leaf marginal logpdf / ReverseDiff (tape) 27.29 μs 27.62 μs ⚪ 1.01× ⚪ 1.0×
AD gradients / Choose selected-branch logpdf / ReverseDiff (tape) 13.85 μs 13.69 μs ⚪ 0.99× ⚪ 1.0×
AD gradients / Resolve stick-breaking branch-prob logpdf / ForwardDiff 6.76 μs 6.71 μs ⚪ 0.99× ⚪ 1.0×
AD gradients / Resolve stick-breaking branch-prob logpdf / Enzyme reverse 21.13 μs 21.29 μs ⚪ 1.01× ⚪ 1.0×
AD gradients / Resolve mixture marginal logpdf / Enzyme reverse 20.97 μs 21.07 μs ⚪ 1.0× ⚪ 1.0×
AD gradients / Resolve mixture marginal logpdf / ForwardDiff 6.74 μs 6.71 μs ⚪ 1.0× ⚪ 1.0×
AD gradients / Pool non-centred reconstruction logpdf / Mooncake reverse 21.99 μs 21.89 μs ⚪ 1.0× ⚪ 1.0×
AD gradients / Shared-tag unflatten/update codec / ReverseDiff (tape) 11.52 μs 11.5 μs ⚪ 1.0× ⚪ 1.0×

`_update_leaf` now calls `rebuild_leaf(leaf, vals)`, an ordinary
method with no constructor-identity contract, instead of building
through `leaf_ctor` directly. `tie` groups leaves by the new
`leaf_signature(leaf)` hook, which keeps the egal-stability
requirement that only tie identity actually needs.

A leaf whose free parameters are not its native constructor
arguments can now override `rebuild_leaf` (an ordinary method) rather
than being forced into a callable-struct `leaf_ctor` just to satisfy
`tie`'s equality check.
Both hooks' defaults previously dispatched on the outer wrapped leaf
directly, so an override on an inner leaf (e.g. one wrapped in
truncated/uncertain) was never consulted: rebuild_leaf re-entered
leaf_ctor's own peeling instead of the override, and leaf_signature
could mis-group structurally distinct wrapped leaves under tie.

Peel one free_leaf layer at a time before dispatching, mirroring how
leaf_ctor already peels, so an inner-leaf override is honoured under
any wrapper stack. leaf_signature keeps the parameter-names half keyed
on the outer leaf so a wrapper's own extra_leaf_params names are not
dropped.

Add a testitem covering both hooks under truncated/uncertain wrappers,
including a tie-rejection case that fails without the fix.
@seabbs-bot

Copy link
Copy Markdown
Collaborator Author

Superseded by #381, which folds this and seven other PRs into one branch off main so the 0.2.0 contract reviews as a single diff. Its gates: test-fast 2379/2379, test-quality 255/255, full docs build clean.

This branch is preserved and the PR body is kept for its rationale — the detail here is the record of why, which #381 summarises rather than repeats. Closing so it does not compete for review.

This comment was posted by a bot. Please ping @seabbs for any questions.

@seabbs-bot seabbs-bot closed this Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants