Skip to content

feat: a small public node interface, and a node that no longer silently estimates nothing (#374) - #379

Closed
seabbs-bot wants to merge 1 commit into
feat/227-single-table-slice-1from
feat/374-node-interface
Closed

feat: a small public node interface, and a node that no longer silently estimates nothing (#374)#379
seabbs-bot wants to merge 1 commit into
feat/227-single-table-slice-1from
feat/374-node-interface

Conversation

@seabbs-bot

Copy link
Copy Markdown
Collaborator

Implements your steer: writing methods to add a node is fine, but they must be few, clean, public but not exported, and backed by a stated interface and developer docs. It also fixes the correctness bug underneath.

Stacked on #343 (feat/227-single-table-slice-1, which already carries #362).

The bug it closes

A fresh AbstractComposedDistribution subtype implementing all four documented methods, with an uncertain child, got flat_dimension == 0 where the answer is 1. A fit on that tree estimated nothing and reported no error, and the shipped test_node_interface passed it 8/8.

The cause was structural: the codec's if T <: Sequential / elseif T <: Choose / else leaf chains live inside @generated bodies, so there was no method a downstream node could add — it fell into the leaf branch.

Now composed_to_table, params, update, has_uncertain, has_varying and compose(...) nesting are generic over any AbstractComposedDistribution subtype rather than closed to the five built-ins, and a node missing a required method fails with an error naming the gap instead of being silently treated as a leaf.

The interface

A downstream node implements node_children, node_rebuild and component_names — promoted from the private _node_children/_rebuild — and that alone is enough to compose, table and update.

child_nleaves, child_logpdf and child_rand! now default generically off node_children for a plain concatenating node. A node with novel combination semantics still overrides them, as Choose does.

One honest limitation, documented rather than hidden

Full codec support additionally requires the node to carry its own child names and children's types as its first two type parameters, matching the shape Sequential/Parallel/Choose/Compete already have. That is a deliberate constraint, not an oversight: a @generated function cannot call a method a downstream package defines — a measured Julia world-age hazard, the same one #189 documented.

It is stated in the interface docs rather than left for someone to discover through a silent zero.

The conformance check that would have caught it

TestUtils.test_estimation_dimension asserts flat_dimension(d) matches composed_to_table(d)'s estimated-row count, wired into both test_node_interface and test_interface. A node that silently drops an uncertain leaf from the codec now fails the harness instead of shipping green. A dedicated negative-fixture testitem pins that an incomplete node fails loudly.

Docs

docs/src/developer/interface-contracts.md and test/interfaces.jl are rewritten around a Both node that genuinely composes, tables, flattens and fits. The previous worked example could not participate in composition at all — it was not an AbstractComposedDistribution subtype, so the composability gate rejected it.

Note for #365: that PR deletes these developer pages into extending.md. Expect a delete/modify conflict, resolved in #365's favour by carrying this wording across.

Assertions

None removed or weakened — verified by diff: zero removed @test lines, 25 assertions added.

Closes #374. Refs #332, #365.

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

…ent-zero

The composer-node extension contract is public (not exported) and minimal
now: a downstream node implements node_children/node_rebuild/component_names
(promoted from the private _node_children/_rebuild), and that alone is
enough to compose, table (composed_to_table), and update. child_nleaves/
child_logpdf/child_rand! default generically off node_children for a plain
"concatenating" node; a node with novel combination semantics still
overrides them, as Choose does. Codec (flatten/unflatten/reconstruct/fit)
support additionally needs the node's own child names and children's types
as its first two type parameters, matching Sequential/Parallel/Choose/
Compete's existing shape -- documented as a deliberate choice, since a
@generated function cannot call a method a downstream package defines (a
measured Julia world-age hazard).

composed_to_table, params, update, has_uncertain, has_varying and
compose(...) nesting are now generic over any AbstractComposedDistribution
subtype rather than closed to the five built-in node kinds; a node missing
a required method now fails with a clear error naming the gap, rather than
silently being treated as a leaf with zero estimated parameters (#374).

TestUtils.test_estimation_dimension is a new conformance check (wired into
both test_node_interface and test_interface) asserting flat_dimension(d)
matches composed_to_table(d)'s estimated-row count, so a node that silently
drops an uncertain leaf from the codec fails the harness instead of
shipping green.

docs/src/developer/interface-contracts.md and test/interfaces.jl are
rewritten around a genuinely working Both node (subtyping
AbstractComposedDistribution) that composes, tables, flattens and fits,
replacing the old worked example that could not actually participate in
composition. A dedicated negative-fixture testitem asserts an incomplete
node fails loudly rather than silently.
@github-actions

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/374-node-interface")
using ComposedDistributions

Option 2: Local Checkout

If you have the repo locally:

git checkout feat/374-node-interface
julia --project=. -e "using Pkg; Pkg.instantiate()"

@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.

1 participant