Skip to content

Fix HSGP prior handling: hsgp() terms are usable through HSSM - #1121

Merged
AlexanderFengler merged 8 commits into
mainfrom
624-fix-hsgp-priors
Aug 9, 2026
Merged

Fix HSGP prior handling: hsgp() terms are usable through HSSM#1121
AlexanderFengler merged 8 commits into
mainfrom
624-fix-hsgp-priors

Conversation

@AlexanderFengler

@AlexanderFengler AlexanderFengler commented Aug 2, 2026

Copy link
Copy Markdown
Member

Closes #624.

Severity was higher than reported: not only did user-specified HSGP priors fail — hsgp() terms could not be used through HSSM at all, because the safe-prior machinery injected a scalar default that bambi rejects even when no prior was given.

What changed

  • make_safe_priors no longer injects scalar defaults onto HSGP terms (detected with bambi's own is_hsgp_term); bambi's automatic HSGP priors apply when none are given
  • _make_prior_dict passes an hsgp(...) key's dict through as the covariance-prior dictionary bambi requires, instead of collapsing it into a single bmb.Prior; inner {"name": ...} specs still convert, and a top-level "name": "HSGP" (the shape from HSSM does not properly send priors to Bambi for an HSGP #624's report) is accepted and dropped
  • All production changes in src/hssm/param/regression_param.py (+54 lines); docstring for the prior shapes in hssm.py
  • New tutorial docs/tutorials/hsgp_regression.ipynb (nav-wired, outputs committed, execution skipped in docs build and notebook CI like the other sampling tutorials): recovers a smooth drift effect v = A*sin(stim) end to end

Tests

  • 12 new tests: prior shapes (incl. the HSSM does not properly send priors to Bambi for an HSGP #624 repro), by= variant, two HSGP terms, mixed formulas keeping safe defaults on linear terms, both prior_settings modes, non-centering inertness, and a slow end-to-end fit asserting the full HSGP posterior (sigma, ell, weights, GP contribution)

Verification

Notes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added an HSGP regression tutorial demonstrating smooth, stimulus-dependent drift effects, model fitting, and posterior visualization.
    • Added HSGP regression support with configurable covariance-function priors and Bambi-compatible defaults.
    • Added the tutorial to the documentation navigation.
  • Documentation

    • Expanded regression prior documentation, including HSGP-specific configuration guidance.
  • Bug Fixes

    • Improved handling of HSGP terms so their structured priors are preserved correctly.

AlexanderFengler and others added 5 commits August 1, 2026 19:31
make_safe_priors blanketed every common design-matrix term with a scalar
default bmb.Prior — hsgp() terms included, which bambi rejects at build
time since an HSGP prior must be None or a dict of covariance-function
priors. This made HSGP terms unusable through HSSM even with no user
prior at all.

Skip the injection for terms bambi itself identifies as HSGP
(bambi.utils.is_hsgp_term); bambi's automatic HSGP priors take over.
The term stays in self.terms for parameterization bookkeeping, and an
HSGP term can never be an intercept, so nothing else in the loop is
affected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
_make_prior_dict collapsed every dict-valued prior into a single
bmb.Prior(**spec). For hsgp() terms that either shipped bambi an object
its HSGPTerm prior setter rejects, or crashed earlier on the bambi-native
covariance dict for lack of a "name" key.

Keys addressing hsgp() formula terms now pass their dict through:
inner HSSM-style specs (with a "name") still convert to bmb.Prior, a
top-level "name": "HSGP" marker is dropped as convention residue, and
everything else reaches bambi untouched. Detection is by key prefix
because this runs before design matrices exist; "hsgp" is the reserved
stateful-transform name in bambi's formula namespace, so the prefix is
the semantics.

Tests cover the gh-624 reproduction, the bambi-documented dict shape,
no-prior (automatic priors), JSON-able inner specs, the by= variant,
two HSGP terms in one formula, mixed formulas keeping safe defaults on
linear terms, and both prior_settings modes — the None path skips
make_safe_priors entirely, so the pass-through carries the fix alone
there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- tests/slow: end-to-end fit of an hsgp() drift regression (25 draws),
  asserting the full HSGP posterior is present — covariance parameters,
  basis weights, and the per-observation GP contribution.
- tests/test_hsgp.py: a dict-valued HSGP prior must fall through the
  parameterization checks' isinstance guards — the group term still gets
  its *_offset RV and no spurious warning references the HSGP entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simulates a DDM whose drift varies smoothly with a stimulus variable
(v = A*sin(stim), trial-wise theta), specifies the hsgp() covariance
prior dict, fits, and recovers the curve against ground truth. Wired
into the docs nav; outputs committed and execution skipped in both the
docs build (mkdocs execute_ignore) and notebook CI (SKIP_NOTEBOOKS),
matching the other sampling tutorials — notebook execution is headed
out of GitHub CI into a dedicated job. End-to-end regression coverage
for gh-624 lives in tests/slow/test_hsgp_mcmc.py instead.

Co-Authored-By: Claude Fable 5 <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

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1bd3b4ab-77f7-431d-9751-dacecfce2cbc

📥 Commits

Reviewing files that changed from the base of the PR and between eddc896 and 47b9d8b.

📒 Files selected for processing (2)
  • .github/workflows/check_notebooks.yml
  • mkdocs.yml
💤 Files with no reviewable changes (1)
  • .github/workflows/check_notebooks.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • mkdocs.yml

📝 Walkthrough

Walkthrough

HSSM now preserves HSGP covariance-prior dictionaries for Bambi, skips scalar defaults for HSGP terms, adds regression tests, and documents the workflow in a new tutorial.

Changes

HSGP regression support

Layer / File(s) Summary
HSGP prior handling
src/hssm/param/regression_param.py, src/hssm/hssm.py
HSGP terms bypass scalar default priors. Nested HSGP prior dictionaries remain dictionaries and convert nested HSSM specifications recursively.
HSGP model validation
tests/unit/param/test_regression_param.py, tests/test_hsgp.py, tests/slow/test_hsgp_mcmc.py
Tests cover prior processing, grouped and multiple HSGP terms, mixed formulas, model construction, sampling, and posterior variables.
HSGP tutorial integration
docs/tutorials/hsgp_regression.ipynb, mkdocs.yml, .github/workflows/check_notebooks.yml
The tutorial simulates sinusoidal drift, fits an HSGP regression, plots posterior effects, and lists posterior variables. MkDocs navigation and notebook execution settings include the tutorial.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant HSSM
  participant Bambi
  participant Sampler
  participant Notebook
  User->>Notebook: define HSGP regression and priors
  Notebook->>HSSM: construct model
  HSSM->>Bambi: pass normalized HSGP prior dictionary
  Bambi->>Sampler: build and sample regression model
  Sampler-->>Notebook: return HSGP posterior variables
  Notebook->>Notebook: extract and plot posterior smooth effect
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary change: fixing HSGP prior handling so hsgp() terms work through HSSM.
Linked Issues check ✅ Passed The changes pass HSGP prior dictionaries to Bambi, preserve nested covariance priors, and add tests for issue #624 requirements.
Out of Scope Changes check ✅ Passed The workflow, documentation, tutorial, implementation, and tests directly support HSGP regression support and issue #624.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 624-fix-hsgp-priors

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/hssm/param/regression_param.py (1)

413-444: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Fix the malformed nested type hint in _make_prior_dict.

The parameter annotation at Line 414 declares dict[dict, Any] for the nested-dict branch of the union. The key type should be str, matching the actual key type of a covariance-parameter prior dict (e.g. "sigma", "ell"). dict is not a valid semantic key type here, and using it obscures the true shape of the accepted input.

🔧 Proposed fix
 def _make_prior_dict(
-    prior: dict[str, float | dict[dict, Any] | bmb.Prior],
+    prior: dict[str, float | dict[str, Any] | bmb.Prior],
 ) -> dict[str, float | bmb.Prior | dict[str, Any]]:

The dispatch logic below (HSGP dict pass-through vs. recursive conversion vs. pass-through scalar) is otherwise correct and matches the new tests.

As per coding guidelines, "Use pyrefly as the primary type checker and maintain mypy compatibility."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hssm/param/regression_param.py` around lines 413 - 444, Update the
parameter annotation of _make_prior_dict so its nested dictionary branch uses
string keys, changing dict[dict, Any] to the appropriate dict[str, Any] shape
while preserving the existing HSGP dispatch and recursive conversion logic.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/hssm/param/regression_param.py`:
- Around line 413-444: Update the parameter annotation of _make_prior_dict so
its nested dictionary branch uses string keys, changing dict[dict, Any] to the
appropriate dict[str, Any] shape while preserving the existing HSGP dispatch and
recursive conversion logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ad9731c3-8280-4fa8-8d4e-57cff8abd7e3

📥 Commits

Reviewing files that changed from the base of the PR and between ee7ca12 and 71f2645.

📒 Files selected for processing (8)
  • .github/workflows/check_notebooks.yml
  • docs/tutorials/hsgp_regression.ipynb
  • mkdocs.yml
  • src/hssm/hssm.py
  • src/hssm/param/regression_param.py
  • tests/slow/test_hsgp_mcmc.py
  • tests/test_hsgp.py
  • tests/unit/param/test_regression_param.py

Pre-existing code, unrelated to the HSGP change: pyrefly 1.2.0 (with
pandas 3.0.5 stubs) types `.loc` column-list selection as
DataFrame | Series and rejects the declared DataFrame return in
_stack_and_rename. CI resolves dependencies fresh on every run because
uv.lock is gitignored, so the floating toolchain surfaced this on the
first PR after the pyrefly release. A two-column selection is always a
DataFrame at runtime; cast accordingly.

Verified against the CI-equivalent environment (no lockfile, fresh
resolve, Python 3.13, pyrefly 1.2.0): 0 errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@AlexanderFengler

Copy link
Copy Markdown
Member Author

CI note: the initial lint failure was not from this PR's changes. run_tests (3.13) / pyrefly flagged plotting/utils.py:59 — code untouched here. Root cause: uv.lock is gitignored (.gitignore:98), so CI resolves dependencies fresh on every run; pyrefly 1.2.0 (released since the last green run, with pandas 3.0.5 stubs) types .loc column-list selection as DataFrame | Series and rejects the declared return. Any PR opened right now would hit this.

Fixed in eddc896 with a one-line cast, verified against a lockfile-free fresh-resolve environment (Python 3.13, pyrefly 1.2.0 → 0 errors).

Worth a separate discussion: re-tracking uv.lock (the repo docs still call it "the source of truth") or pinning pyrefly, so the type gate doesn't float with upstream releases.

🤖 Generated with Claude Code

@AlexanderFengler
AlexanderFengler merged commit b08c17b into main Aug 9, 2026
5 checks passed
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.

HSSM does not properly send priors to Bambi for an HSGP

1 participant