Skip to content

fix: warn on disconnected RVs from centered/noncentered prior drift#966

Open
AlexanderFengler wants to merge 3 commits into
mainfrom
fix-idle-graph-nodes
Open

fix: warn on disconnected RVs from centered/noncentered prior drift#966
AlexanderFengler wants to merge 3 commits into
mainfrom
fix-idle-graph-nodes

Conversation

@AlexanderFengler
Copy link
Copy Markdown
Member

Summary

When a user supplies group-specific priors with nested hyperpriors (e.g. a Normal prior on 1|participant_id whose mu is itself a Prior), HSSM/bambi can silently produce a PyMC graph with idle, disconnected nodes. Under the default noncentered=True, bambi reparameterizes a group-specific Normal term as offset * sigma and never references mu — so a user-supplied mu hyperprior is created as a free RV in the graph but never wired into the likelihood. Until now this happened silently.

This PR adds two validation hooks and a tutorial.

  • Targeted check (src/hssm/param/parameterization_check.py::check_user_priors_against_parameterization): scoped to user-supplied prior keys, flags Normal group-specific priors with a mu hyperprior when the effective parameterization is non-centered, and emits an actionable warning ("either pass noncentered=False, or move the location prior onto the common Intercept…").
  • General safety net (find_disconnected_free_rvs): walks the PyMC graph after model.build() and warns about any free RV that is not an ancestor of an observed RV — catches future name-convention drift, not just the known case.
  • Wired into src/hssm/base.py around the existing bmb.Model(...) / self.model.build() block. noncentered remains a **kwargs passthrough.
  • New tutorial docs/tutorials/centered_vs_noncentered_basic_logic.ipynb walks through both parameterizations, the node-naming difference, the disconnected-node footgun, and two fixes.

Severity: warn (not raise) — model still builds. The default noncentered=True and HSSM behavior are otherwise unchanged.

Test plan

  • uv run pytest tests/test_prior.py -v — 8/8 pass (5 new cases covering targeted warning, centered no-warning, defaults-skip, hand-built orphan detection, unit-level check)
  • uv run pytest tests/ --ignore=tests/slow -x — 467/467 pass; no regressions from the new hook in HSSMBase.__init__
  • uv run ruff check clean on touched files (remaining ruff errors are pre-existing in tests/test_prior.py)
  • uv run mypy src/hssm/param/parameterization_check.py — clean
  • Manual: build the tutorial notebook end-to-end and confirm graphviz output for each model

🤖 Generated with Claude Code

Adds two validation hooks around the bambi model build:

* A targeted check that flags user-supplied Normal group-specific
  priors with a nested `mu` hyperprior under `noncentered=True`.
  Under non-centered, bambi reparameterizes the term as
  `offset * sigma` and never references `mu`, so the `mu` hyperprior
  is created in the PyMC graph but left disconnected from the
  likelihood. The check is scoped to user-supplied prior keys to
  avoid false alarms from HSSM defaults.

* A general post-build graph walk (`find_disconnected_free_rvs`)
  that surfaces any free RV not reachable from the observed RVs,
  acting as a safety net for future name-convention drift.

Both checks emit warnings via the existing `hssm` logger; model
construction is unaffected. `noncentered` remains a `**kwargs`
passthrough to `bmb.Model`.

Includes a new tutorial,
`docs/tutorials/centered_vs_noncentered_basic_logic.ipynb`, that
walks through the two parameterizations, the node-naming
difference, and the disconnected-`mu` footgun with two fixes
(switch to centered; or move the location prior onto the common
`Intercept`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@review-notebook-app
Copy link
Copy Markdown

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

AlexanderFengler and others added 2 commits May 19, 2026 11:48
On macOS, notebook kernels launched from non-terminal contexts (some
IDE integrations, GUI launchers) inherit only /etc/paths and miss
/opt/homebrew/bin, so pm.model_to_graphviz raises ExecutableNotFound
even when Graphviz is installed. Tutorial now prepends common
locations to PATH only if `dot` is missing — no-op otherwise.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…p mu

Adds a second user-prior check next to the existing disconnected-node
warning. It fires when a user-supplied group-specific Normal prior has a
non-trivial mu (hyperprior or non-zero scalar) and the same formula also
contains a common Intercept. The linear predictor sees only
Intercept + mu, so the two parameters are non-identifiable from the
data and the posterior has a ridge along their anti-diagonal. The
warning fires under both centered and non-centered parameterizations.

HSSM defaults already use mu=0 for group_intercept_with_common, so this
only triggers on user-written priors that explicitly carry a non-trivial
group-level location. Scoping reuses the _user_specified_prior_keys
snapshot.

Tutorial updates:

* New note after Fix 1 in centered_vs_noncentered_basic_logic.ipynb
  explaining that switching to noncentered=False trades the
  disconnected-node problem for a statistical-level redundancy and
  flagging the new warning.
* Rule-of-thumb section gains a bullet making mean-zero random effects
  explicit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@@ -0,0 +1,1332 @@
{
Copy link
Copy Markdown
Member

@krishnbera krishnbera May 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor comment: expand upon the description of location prior a bit?


Reply via ReviewNB

Copy link
Copy Markdown
Member

@krishnbera krishnbera left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!!

PriorMismatch(
parameter=param_name,
term=term_name,
reason=(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can explicitly suggest adding a prior on offset here?

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