Skip to content

fixed numba attempting to parallelize in already parallelized processes#337

Merged
aaronleesander merged 8 commits intomainfrom
numba-fix
Feb 19, 2026
Merged

fixed numba attempting to parallelize in already parallelized processes#337
aaronleesander merged 8 commits intomainfrom
numba-fix

Conversation

@aaronleesander
Copy link
Member

Description

This pull request fixes numba from using multiple threads despite the larger simulation already being maximally parallelized such as in a trajectory simulation.

This led to crashes for certain problems on certain operating systems.
Additionally, this now allows numba to use multiple threads when the simulation is deterministic or serialized.

Finally, Linux now uses fork rather than spawn which seems to stabilize simulations (despite expectation of the opposite).
This should be monitored in the future.

Checklist:

  • The pull request only contains commits that are focused and relevant to this change.
  • I have added appropriate tests that cover the new/changed functionality.
  • I have updated the documentation to reflect these changes.
  • I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals.
  • I have added migration instructions to the upgrade guide (if needed).
  • The changes follow the project's style guidelines and introduce no new warnings.
  • The changes are fully tested and pass the CI checks.
  • I have reviewed my own code changes.

@aaronleesander aaronleesander added the bug Something isn't working label Feb 19, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 19, 2026

Warning

Rate limit exceeded

@aaronleesander has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 49 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

Adds OS-aware multiprocessing context selection and Numba thread-control to the simulator worker initialization; introduces a changelog entry for a new MPO.from_matrix() API and adds a test verifying multiprocessing start method and Numba thread capping.

Changes

Cohort / File(s) Summary
Changelog
CHANGELOG.md
Documents new public API MPO.from_matrix() and notes a fix for NumPy/Numba parallelization conflicts in Unreleased.
Simulator core
src/mqt/yaqs/simulator.py
Replaces _spawn_context with OS-aware _get_parallel_context; sets Numba thread limits in _call_backend and _worker_init (uses NUMBA_NUM_THREADS / set_num_threads() with ImportError/AttributeError guards); expands THREAD_ENV_VARS; reorganizes imports and adjusts parallel execution paths.
Tests
tests/test_simulator.py
Adds test_threading_config to assert selected multiprocessing start method (fork on Linux, spawn elsewhere) and that _worker_init(..., n_threads=1) enforces numba.get_num_threads() == 1 and sets NUMBA_NUM_THREADS.

Sequence Diagram(s)

sequenceDiagram
    participant Main as Main Process
    participant Context as Multiprocessing Context
    participant Worker as Worker Process
    participant Numba as Numba Runtime
    participant TPCTL as threadpoolctl

    rect rgba(135,206,235,0.5)
    Main->>Context: _get_parallel_context() (select fork/spawn)
    Context-->>Main: Context object
    Main->>Context: spawn Worker(s) with THREAD_ENV_VARS (incl. NUMBA_NUM_THREADS)
    end

    rect rgba(144,238,144,0.5)
    Context->>Worker: start process -> run _worker_init(env, n_threads)
    Worker->>Numba: set_num_threads(n_threads) (if available)
    Worker->>TPCTL: limit threadpools (threadpoolctl)
    TPCTL-->>Worker: threadpool limits applied
    Numba-->>Worker: numba threads capped
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through forks and spawns tonight,
Tucked threads to one, made parallels light,
NumPy and Numba now dance in tune,
Workers hum softly beneath the moon. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately captures the main fix: preventing numba from parallelizing within already parallelized processes, which aligns with the primary objective of the PR.
Description check ✅ Passed The description covers the main problem and solution, includes a completed checklist, and provides context about OS-specific behavior changes. All required sections are addressed.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch numba-fix

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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/mqt/yaqs/simulator.py (1)

402-419: ⚠️ Potential issue | 🟡 Minor

Stale section header contradicts the new behavior.

Lines 402–405 still read "spawn" CONTEXT and warn that "fork" can hang/crash, but the function now deliberately returns "fork" on POSIX. Update the header comment to match the new intent.

Suggested fix
-# 8) MULTIPROCESS "spawn" CONTEXT
-# On Linux, using "fork" with heavy numerical libs can hang/crash due to
-# non-fork-safe OpenMP/BLAS state. "spawn" is the robust cross-platform choice.
+# 8) MULTIPROCESS CONTEXT SELECTION
+# On Windows, "spawn" is the only safe option.
+# On Linux, "fork" avoids the OOM overhead of "spawn" and is safe provided
+# that threading libraries are capped to 1 thread before/after forking.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/mqt/yaqs/simulator.py` around lines 402 - 419, The top comment block is
outdated and says "spawn" CONTEXT and warns that "fork" can hang/crash while
_get_parallel_context now returns "fork" on POSIX; update the header and
docstring to reflect the current intent: state that we prefer "fork" on POSIX
for performance (with safeguards to cap OpenMP/BLAS threads) and use "spawn" on
Windows, and adjust any wording that warns against "fork" to instead note the
precautions taken (e.g., capping threading libraries before forking). Ensure
references to multiprocessing.get_context and os.name remain correct.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/mqt/yaqs/simulator.py`:
- Around line 407-419: The _get_parallel_context function currently returns
multiprocessing.get_context("fork") for all POSIX systems which is unsafe on
macOS; change the logic so that "fork" is only used when running on Linux (e.g.,
check sys.platform == "linux"), and default to
multiprocessing.get_context("spawn") for macOS and other non-Linux platforms;
also correct the docstring typo "caped" to "capped". Ensure references to the
function name _get_parallel_context and the multiprocessing.get_context calls
are updated accordingly.

In `@tests/test_simulator.py`:
- Line 40: The import line brings in _get_parallel_context and _worker_init but
has an unnecessary noqa comment suppressing PLC2701; remove the trailing " #
noqa: PLC2701" from the import statement so the import remains unchanged but the
no-op suppression is deleted (target the import of _get_parallel_context and
_worker_init in tests/test_simulator.py).
- Around line 86-100: The test leaves THREAD_ENV_VARS entries and
NUMBA_NUM_THREADS in the process environment because the finally block contains
a no-op; update the cleanup to either use pytest.monkeypatch to setenv/undo or
explicitly save and restore all affected variables: in tests/test_simulator.py
replace the no-op with code that restores original values for each name in
THREAD_ENV_VARS (pop new keys or reset to saved values) and remove any permanent
os.environ.setdefault usage in _limit_worker_threads by changing it to set only
for the test (use os.environ.setdefault only when the caller guarantees
restoration) or have _limit_worker_threads accept an env-mutation hook so tests
can isolate changes; ensure _worker_init/_limit_worker_threads and
NUMBA_NUM_THREADS are fully reverted in the finally block.

---

Outside diff comments:
In `@src/mqt/yaqs/simulator.py`:
- Around line 402-419: The top comment block is outdated and says "spawn"
CONTEXT and warns that "fork" can hang/crash while _get_parallel_context now
returns "fork" on POSIX; update the header and docstring to reflect the current
intent: state that we prefer "fork" on POSIX for performance (with safeguards to
cap OpenMP/BLAS threads) and use "spawn" on Windows, and adjust any wording that
warns against "fork" to instead note the precautions taken (e.g., capping
threading libraries before forking). Ensure references to
multiprocessing.get_context and os.name remain correct.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/mqt/yaqs/simulator.py (1)

403-421: ⚠️ Potential issue | 🟡 Minor

Stale section comment contradicts the implementation.

Lines 404–406 still say "fork" with heavy numerical libs can hang/crash and "spawn" is the robust cross-platform choice, but the function now deliberately uses "fork" on Linux. The docstring (lines 411–413) is correct; the section header should be updated to match.

Suggested fix
 # ---------------------------------------------------------------------------
-# 8) MULTIPROCESS "spawn" CONTEXT
-# On Linux, using "fork" with heavy numerical libs can hang/crash due to
-# non-fork-safe OpenMP/BLAS state. "spawn" is the robust cross-platform choice.
+# 8) MULTIPROCESS CONTEXT SELECTION
+# On Linux, "fork" is used for speed (safe when thread pools are capped).
+# On Windows and macOS, "spawn" is the standard/required choice.
 # ---------------------------------------------------------------------------
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/mqt/yaqs/simulator.py` around lines 403 - 421, The section header comment
above _get_parallel_context is stale and contradicts the implementation: update
the top block comment (lines describing "MULTIPROCESS 'spawn' CONTEXT" and
"spawn is the robust cross-platform choice") to reflect the current behavior
that uses "fork" on Linux and "spawn" on Windows/macOS; keep the existing
docstring in _get_parallel_context as-is, and make the header explain that we
prefer "fork" on Linux when safe and "spawn" otherwise to avoid confusion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/mqt/yaqs/simulator.py`:
- Around line 386-392: The inline comment before attempting to import numba is
truncated and the exception handling is inconsistent with _worker_init: complete
the comment sentence to clearly state that numba threading must be set before
threadpoolctl limits backend threads, and update _worker_init to catch both
ImportError and AttributeError (matching the try/except around
numba.set_num_threads) so both places handle the same edge-case Numba builds;
ensure you reference the numba.set_num_threads call and the _worker_init
function when making these changes.

In `@tests/test_simulator.py`:
- Around line 104-117: The finally block that restores environment variables
mutates os.environ while iterating it (for key in os.environ) which raises
RuntimeError; fix by iterating over a static list of keys (e.g., for key in
list(os.environ)) or, better, replace the entire manual cleanup with
pytest.MonkeyPatch to setenv/undo changes; update the code that references
env_snapshot and the deletion loop in the finally block (and keep
numba.set_num_threads(original_numba_threads) as-is) so you restore added keys
by iterating list(os.environ) or let MonkeyPatch handle env isolation
automatically.

---

Outside diff comments:
In `@src/mqt/yaqs/simulator.py`:
- Around line 403-421: The section header comment above _get_parallel_context is
stale and contradicts the implementation: update the top block comment (lines
describing "MULTIPROCESS 'spawn' CONTEXT" and "spawn is the robust
cross-platform choice") to reflect the current behavior that uses "fork" on
Linux and "spawn" on Windows/macOS; keep the existing docstring in
_get_parallel_context as-is, and make the header explain that we prefer "fork"
on Linux when safe and "spawn" otherwise to avoid confusion.

---

Duplicate comments:
In `@tests/test_simulator.py`:
- Line 40: The import line in tests/test_simulator.py includes an unnecessary
noqa directive ("# noqa: PLC2701") for the symbols _get_parallel_context and
_worker_init; inspect the project's Ruff configuration to see if PL (PLC2701)
rules are enabled and if so keep the directive, otherwise remove the "# noqa:
PLC2701" from the import statement; ensure this change targets the import that
references _get_parallel_context and _worker_init so linting reflects the actual
project config.

@codecov
Copy link

codecov bot commented Feb 19, 2026

Codecov Report

❌ Patch coverage is 78.94737% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/mqt/yaqs/simulator.py 78.9% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

@aaronleesander aaronleesander merged commit 46086bd into main Feb 19, 2026
14 of 15 checks passed
@aaronleesander aaronleesander deleted the numba-fix branch February 19, 2026 04:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments