Repository navigation
Description
Summary
AlphaImpute2 0.0.4 deterministically segfaults (SIGSEGV, rc -11) ~4 min
into population imputation on a real-scale aquaculture (shrimp) dataset when
run with the default --threads 8. The same inputs complete cleanly with
--threads 1 (full-data run: 2,228 s ≈ 37 min). The fault lands inside the
numba-compiled ParticlePhasing.phase method (a @jitclass), called from
ThreadPoolExecutor workers in ParticleImputation.multi_threaded_apply.
We reproduced the crash in 5 of 5 runs at --threads 8 (three standalone
panel variants and two runs through our wrapper); --threads 1 completed
2 of 2 runs cleanly. We tested only these two thread counts — not the
intermediate range (2–7). The crash is independent of data content:
dropping an all-NA individual and 26 monomorphic markers, and a second,
cleaned and relabeled panel, all crash at the identical point.
We suspect a thread-safety problem in the numba jitclass/nogil code path
shared between concurrent workers. We have not tested older numba or
AlphaImpute2 releases.
Environment
- AlphaImpute2 0.0.4 (pip; console script
/opt/miniconda/bin/AlphaImpute2) - Python 3.10.11 (miniconda base)
- numpy 1.26.4, numba 0.65.1
- Linux 6.8.0-106-generic, x86_64
- CPU: Intel Xeon Gold 6338 @ 2.00 GHz (128 hardware threads available; the
fault occurs with only 8 workers)
Reproduction
Standalone invocation (outside any wrapper):
AlphaImpute2 -g genotypes.txt -p pedigree.txt -s spec_file --threads 8 --out ...
Data scale: merged two-array SNP panel with 56,214 markers and 2,014
individuals (205 high-density reference + 1,809 low-density targets);
pedigree of ~2,000 rows. The pedigree contains single-parent rows, for which
AlphaImpute2 synthesizes FatherOf*/MotherOf* dummy parents. No variant of
our inputs removed those rows, so we cannot fully exclude them as a
contributing factor; the successful single-threaded run used the same
pedigree.
What we ran (all 8-thread crashes occur at the same point, ~4 min in):
| # | setup | threads | result |
|---|---|---|---|
| 1 | full data, standalone | 8 | SIGSEGV — ai2_dbg.log |
| 2 | trim: drop the all-NA individual + 26 monomorphic markers, standalone | 8 | SIGSEGV — ai2_trim_dbg.log |
| 3 | clean panel (Mendel/pedigree-consistent, relabeled), standalone | 8 | SIGSEGV |
| 4 | full data, through our wrapper | 8 | SIGSEGV, rc -11 |
| 5 | full data, wrapper rerun | 8 | SIGSEGV, rc -11 |
| 6 | full data (same inputs as #1), standalone | 1 | completes cleanly — 2,228 s |
| 7 | full data, end-to-end through our wrapper | 1 | rc 0 |
Net: --threads 8 crashed 5 of 5 runs; --threads 1 completed 2 of 2 runs.
We did not test intermediate thread counts (2–7) — happy to run a sweep if
that would help isolate the threshold.
Crash signature
In both standalone crash logs, the last progress line printed before the
fault is Imputing chip 1 of 1. The single-threaded run prints the same
line and continues to completion. faulthandler excerpt (verbatim from
ai2_dbg.log; ai2_trim_dbg.log shows the identical pair of stacks):
Fatal Python error: Segmentation fault
Thread 0x... (most recent call first):
File ".../numba/experimental/jitclass/boxing.py", line 61 in wrapper
File ".../alphaimpute2/Imputation/ParticlePhasing.py", line 146 in phase
File ".../alphaimpute2/Imputation/ParticleImputation.py", line 82 in backward_impute_individual
File ".../concurrent/futures/thread.py", line 58 in run
...
Current thread 0x... (most recent call first):
File ".../alphaimpute2/Imputation/ParticlePhasing.py", line 147 in phase
File ".../alphaimpute2/Imputation/ParticleImputation.py", line 82 in backward_impute_individual
File ".../concurrent/futures/thread.py", line 58 in run
...
i.e. two ThreadPoolExecutor workers are simultaneously inside
ParticlePhasing.phase (lines 146-147) when the process dies — the shared,
concurrently executed code path under ParticleImputation.multi_threaded_apply
(line 68).
Hypothesis / suggested checks
ParticlePhasingis a numba@jitclass; concurrent invocation of its
compiled methods (or of a@jit(nogil=True)helper used byphase) from
several threads looks unsafe under 0.0.4 + numba 0.65.1. Serializing the
phasing calls, or running single-threaded until audited, avoids the fault.- Repro suggestion: run the bundled example or a synthetic pedigree with
--threads 8. We could not trigger it on tiny synthetic data, so a
real-scale pedigree (≥ ~1,000 individuals with single-parent rows and a
low-density array) seems required. Happy to help debug further: our raw
input files are commercially sensitive, but we can share the full
faulthandler logs, the input dimensions (56,214 × 2,014, ~2,000-row
pedigree), and run any test panel you propose.
Impact note
We drive AlphaImpute2 through a pipeline wrapper; until this is fixed, the
wrapper detects signal-crash exits and automatically re-runs with
--threads 1, logging the full engine trace. The single-threaded fallback
completes correctly — measured at 2,228 s (≈ 37 min) for this full panel.
Logs
Full faulthandler logs exist for all three runs (full-data crash,
trimmed-panel crash, and the successful single-threaded run) and can be
pasted in full on request — the essential excerpts are inline above.
Activity
XingerTang commentedon Sep 5, 2026
Hi @luansheng, thank you so much for your detailed report. We will investigate it.
It may be worth noting that in our
develbranch, which is not released, we have fixed an error that may cause a segmentation fault (relevant link: #66 (comment)). That error could also lead to this issue.gregorgorjanc commentedon Sep 5, 2026
@luansheng can you test the devel version on your data and report back?
luansheng commentedon Sep 5, 2026
Hi @XingerTang @gregorgorjanc — we installed
devel(HEAD32acec6e) in a fresh venv matching our crashing environment (Python 3.10.11 / numba 0.65.1 / numpy 1.26.4) and reran our exact reproduction twice on the full dataset with--maxthreads 8.Both runs passed the point where 0.0.4 died (~4 min in, on
Imputing chip 1 of 1) and finished cleanly: 614 s and 625 s. The two outputs are byte-identical to each other and to our known-good single-threaded 0.0.4 result.0.0.4 crashed 5 for 5 on our data; devel is 2 for 2 — so this resolves the issue for us. We'll switch our pipeline to devel while waiting for the release on PyPI.
XingerTang commentedon Sep 5, 2026
@luansheng, thank you for checking this out and sharing the details. Good to hear that the issue is resolved. I will close this issue as completed.