Skip to content

Add mixed data task and MNLE mode to mini SBIBM - #2034

Merged
janfb merged 3 commits into
sbi-dev:mainfrom
SahilKumar75:feat/mini-sbibm-mnle-task
Oct 5, 2026
Merged

janfb merged 3 commits into
sbi-dev:mainfrom
SahilKumar75:feat/mini-sbibm-mnle-task

Conversation

@SahilKumar75

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds a mixed data task and an mnle mode to Mini SBIBM, as discussed in #1396. The task shares the simulator and prior used in the MNLE tests. Its reference posterior is available analytically as independent Gamma and Beta distributions.

Training uses individual trials. Evaluation observations contain 10 IID trials by default, configurable through --bm-num-iid-trials. Existing default benchmark collection remains at 24 cases; the new mode collects one mixed data case.

Does this close any issues?

Related to #1396. This implements the mixed data task only; the broader issue remains open.

Anything else we should know?

This follows the merged #2024 and is based on current main.

Validation performed:

  1. uv run --no-sync pytest tests/mini_sbibm/mixed_data_test.py -q: six cases pass after rebasing, including agreement between the analytic reference density and simulator likelihood for 1, 4, and 10 trials.
  2. Default collection remains 24 cases; MNLE collection yields one case.
  3. A benchmark execution with 20 training simulations and three IID trials passes. This is an execution smoke test, not an inference accuracy claim.
  4. One existing MNLE API case using MDN and rejection sampling passes after sharing the simulator and prior.
  5. Focused Ruff, formatting, pre commit checks, and uv run --no-sync pyright sbi pass after rebasing. Validation used the existing environment because downloads for the updated lockfile timed out.

Benchmark execution and reporting pass with a fresh results file (20 simulations, three trials, C2ST 0.851). A previous collection run had created a CSV containing only the test_id header; appending execution results to it caused the existing summary error. The old file was preserved. Reporting changes remain outside this task, as maintainers are already working on that area.

Implementation and tests were developed with AI assistance and checked locally.

Checklist

  • I have read the contributing guide.
  • uv run pytest -n auto -m "not slow and not gpu" passes.
  • uv run pre-commit run --all-files passes (ruff and formatting).
  • uv run pyright sbi passes.
  • I added or updated tests for the changed behavior.
  • I used Google-style docstrings for new or changed public functions.
  • New task tests complete quickly; the slowest measured test took 0.28 seconds.

@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

🧰 Additional context used
📚 Code guidelines (2)
docs/contributing.md — auto-discovered
AGENTS.md — auto-discovered

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0f76cf37-6423-4508-8394-b6e781868f74
📥 Commits

Reviewing files that changed from the base of the PR and between 21c2be9 and 4217c20.

📒 Files selected for processing (5)
  • docs/contributing.md
  • tests/bm_test.py
  • tests/conftest.py
  • tests/mini_sbibm/mixed_data.py
  • tests/mini_sbibm/mixed_data_test.py
💤 Files with no reviewable changes (1)
  • tests/conftest.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/contributing.md

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

The benchmark suite adds a mixed-data task for MNLE evaluation. A pytest option sets the number of independent trials. Contributor instructions document the default count and option.

Changes

MNLE mixed-data benchmark

Layer / File(s) Summary
Define and validate the mixed-data task
tests/mini_sbibm/mixed_data.py, tests/mini_sbibm/__init__.py, tests/mini_sbibm/mixed_data_test.py, tests/mnle_test.py
Adds and registers MixedData, which simulates reaction-time and binary-choice observations and provides reference posterior samples. Tests compare reference posterior density with the simulator likelihood and prior. MNLE tests use the shared simulator and prior.
Configure and run the MNLE benchmark
tests/conftest.py, tests/bm_test.py, docs/contributing.md
Adds the mnle benchmark mode and selects MixedData for it. The benchmark passes a constructed Task to amortized or sequential evaluation. A pytest option sets the trial count. Documentation describes the default and option.

Priority: ⬇️ Low

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

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Pytest
  participant TaskFixture
  participant BenchmarkTest
  participant EvaluationFunction
  Pytest->>TaskFixture: Provide task parameter
  TaskFixture->>BenchmarkTest: Supply constructed Task
  BenchmarkTest->>EvaluationFunction: Pass Task to evaluation
Loading

Suggested reviewers: janfb

Merge Risk: ⚪ Minimal · up to 4217c

The configurable-trial MNLE benchmark appears ready to merge after normal checks.

Security Architecture Review

Security architecture risk: ⚪ Minimal · up to 4217c

The benchmark contract changes, but the inspected execution paths remain test-local and use synthetic data and predefined task constructors. No material security risk introduced or worsened by this change was identified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The demonstrated exposure is the benchmark process: a test operator controls mode and trial count, affecting synthetic-data allocation and computation. The inspected transition does not grant tenant, service, credential, or data-store authority.

Trust Boundaries and Controls

  • observed — Task selection supplies predefined repository constructors through pytest parameterization. Passing the resulting object into evaluation does not replace an authentication or authorization control and does not introduce an arbitrary task loader.

Resilience and Maintainability Implications

  • inferred — With fixed configuration and uninterrupted sequential calls, reseeding by observation index preserves observation/reference pairing. The methods mutate the process-global RNG without restoring it, including on failure, so concurrent interleaving is not isolated. The actual evaluation loop is synchronous, subsequent tests reseed, and the same RNG convention predates this PR in GaussianLinear. No security isolation or rollback guarantee dependent on this RNG was established.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: adding a mixed-data task and MNLE mode to mini SBIBM.
Description check ✅ Passed The description covers the change, related issue, implementation details, validation results, and checklist. It also clearly notes that the full test suite and all-files pre-commit checks remain unche…
Docstring Coverage ✅ Passed Docstring coverage is 95.45% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 22 functions across 6 files. (1 skipped: 1 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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.

@codecov

codecov Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.42%. Comparing base (35724e1) to head (4217c20).
⚠️ Report is 3 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2034      +/-   ##
==========================================
- Coverage   89.65%   89.42%   -0.24%     
==========================================
  Files         142      142              
  Lines       14736    15164     +428     
==========================================
+ Hits        13212    13560     +348     
- Misses       1524     1604      +80     
Flag Coverage Δ
fast 85.03% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.
see 7 files with indirect coverage changes

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

@SahilKumar75 thanks for implementing this! I ran the new mode with the default 2000 simulations and it works well: C2ST is about 0.6 with 10 trials and the case takes about 40 seconds.

I have a few comments below, mostly small. The one that matters most is that runs with different trial counts end up with the same name in the results.

Comment thread tests/bm_test.py Outdated
"""
torch.manual_seed(SEED)
task = get_task(task_name)
task = _get_benchmark_task(task_name, benchmark_num_iid_trials)

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.

Runs with a different --bm-num-iid-trials get the same test id and the same row in the results (MNLE{} on mixed_data), and the results file does not store the trial count. So a run with 3 trials silently replaces a run with 10 trials, although they evaluate different posteriors. Please make the trial count part of the task name in the results, e.g. mixed_data-10trials, so these rows stay apart.

Comment thread tests/bm_test.py
"snpe": [NPE_C], # NPE_B not implemented, NPE_A need Gaussian prior
"snle": [NLE],
"snre": [NRE_A, NRE_B, NRE_C, BNRE],
"mnle": [MNLE],

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.

--bm-mode mnle --bm-estimators mnle currently fails during collection with "requires a benchmark mode whose methods use the same estimator argument", because the new mode has no entry in the estimator argument map. MNLE takes its estimator as density_estimator, so adding "mnle": "density_estimator" to that map should be enough.

Comment thread tests/bm_test.py Outdated
return "-".join(str(value) for value in parameters.values()) or "default"


def _get_benchmark_task(task_name: str, num_iid_trials: int) -> Task:

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.

The trial count is only used by the mixed data task, but it is now passed through the sequential path too, which never runs this task, and the runner checks for the task name as a string. I think it would be cleaner if the mnle mode carried the trial count to its task, so that the rest of the runner stays as it is.

Comment thread tests/mini_sbibm/mixed_data.py Outdated
choices = observation[:, 1:]

rate_posterior = Gamma(
torch.tensor([1.0 + self.stimulus_condition * self.num_trials]),

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.

The posterior update takes the number of trials from the task setting and repeats the prior parameters as numbers. Both are right today, but if someone passes an observation with a different number of trials, or changes the prior, the reference becomes wrong without any error. Please take the number of trials from the observation and the parameters from the prior.

Comment thread tests/mini_sbibm/mixed_data_test.py Outdated
from .mixed_data import MixedData


def test_mixed_data_task_shapes():

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.

The likelihood test below covers what matters, and it would fail if the reference were wrong. The shape test, the parameter test and the test for zero trials check things that either fail loudly anyway or are already covered by the likelihood test, so please remove them and keep only the likelihood test.

Comment thread tests/mini_sbibm/mixed_data_test.py Outdated
rate, choice = task._get_reference_posterior(observation)
reference_log_prob = rate.log_prob(theta[:, 0]) + choice.log_prob(theta[:, 1])

likelihood = InverseGamma(2.0, theta[:, :1]).log_prob(observation[:, 0])

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.

Small one: the likelihood here uses a fixed 2.0 for the reaction time concentration. Please take it from the task, so the test stays correct if the stimulus condition changes.

@SahilKumar75

Copy link
Copy Markdown
Contributor Author

Thanks @janfb, addressed all six points in 4217c20.

Trial counts now appear in both test IDs and result task names. MNLE estimator selection works, and the mode configures its task without passing trial counts through the sequential runner. The reference posterior now reads the observation length and prior parameters. I kept the likelihood test, added differing observation lengths and prior parameters to it, and removed the three redundant tests.

The six likelihood cases pass. Benchmark runs with 20 simulations and 3 then 10 trials both pass and remain separate in the same results CSV. These are execution checks, not accuracy claims. Default collection remains 24 cases, and Ruff, formatting, pre commit checks and Pyright pass.

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

@SahilKumar75 thanks, this looks good now, and the stronger likelihood test is neat. Merging.

@janfb
janfb merged commit 17ef00b into sbi-dev:main Oct 5, 2026
18 checks passed
janfb added a commit that referenced this pull request Oct 5, 2026
Bring in the mnle mode and the mixed data task from PR #2034. The mode
now gets run labels, seeds and the mean and std errors like the other
modes. Its docs move to the mini-sbibm developer page.
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