Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

macOS: OMP Error #15 (duplicate libomp) aborts at first parallel region

Aperta
#27 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
5/5
Tempo stimato
Più di una settimana
Idoneità per principianti
35/100
Tipo di issue
Bug
Chiarezza
Abbastanza chiara
Stato di attività
Attiva
Stack tecnologico
macos, python

Direzione di ricerca

Start by rerunning the downstream macOS job against current main and inspect the compiler/libomp diagnostic added around setup.py. Then use otool -L or DYLD_PRINT_LIBRARIES=1 and exercise sbd::tpb::diag, not just the import, to identify both runtimes. Done means the cause is confirmed and either a reproducible macOS CI test, a selected build-path fix, or documented guidance is agreed.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

On macOS, a process that loads sbd._core_cpu can abort at SBD's first parallel region:

OMP: Error #15: Initializing libomp.dylib, but found libomp.dylib already initialized.
Signal: Abort trap: 6 (6)
[ 8] libomp.dylib                     __kmpc_fork_call + 52
[ 9] _core_cpu.cpython-314-darwin.so  sbd::GenerateExcitation...
[10] _core_cpu.cpython-314-darwin.so  sbd::MakeHelpers...
[11] _core_cpu.cpython-314-darwin.so  sbd::tpb::diag...

Both names are libomp.dylib — two copies of the same LLVM runtime, not the more familiar Intel libiomp5 vs LLVM libomp clash.

Root cause

Two Homebrew formulae each insist on their own OpenMP, and they arrive by different routes:

_core_cpu.so ──(-fopenmp, under Homebrew LLVM clang)──> Cellar/llvm/<ver>/lib/libomp.dylib
             └─(-lopenblas)──> libopenblas.dylib ─────> Cellar/libomp/<ver>/lib/libomp.dylib

Homebrew's openblas depends on libomp (deps: ['gcc', 'libomp']) and loads that copy at run time. Homebrew's llvm builds the openmp runtime, so its clang carries a libomp.dylib of its own and -fopenmp links that one. Link both and the process holds two.

Only one of those is on SBD's link line; the other is transitive through OpenBLAS, which is why otool -L on _core_cpu.so does not show it.

Measured by enumerating loaded images through dyld after each import stage:

  [baseline] 0 OpenMP image(s)
  [after pyscf] 0 OpenMP image(s)
  [after qiskit_addon_sqd.fermion (pulls jax)] 0 OpenMP image(s)
  [after qiskit + preset passmanager] 0 OpenMP image(s)
  [after importing sbd] 0 OpenMP image(s)
  compiled backends: ['cpu']
  [after sbd.get_backend() -- loads _core_cpu] 2 OpenMP image(s)
      /opt/homebrew/Cellar/libomp/23.1.0/lib/libomp.dylib
      /opt/homebrew/Cellar/llvm/23.1.0/lib/libomp.dylib

Nothing is mapped until get_backend(), because since #23 the backends load lazily, one per process, on first use. That is why the abort lands at the first parallel region rather than at import — and why a probe that only imports sbd sees nothing.

When this bites

Only when the build uses a compiler that ships its own OpenMP. Under Apple clang there is no second copy: Apple clang has no OpenMP of its own, so SBD takes libomp from the standalone Homebrew formula — the same file OpenBLAS loads. One runtime, no conflict. That is why this repo's own macOS CI has always been green.

It reproduces when CC/CXX is pinned to Homebrew LLVM, which is what Qiskit/qiskit-addon-sqd#367 did, in order to build another extension (fulqrum passes a bare -fopenmp that Apple clang rejects).

What this is not

Several plausible explanations were checked and ruled out:

  • pyscf links no OpenMP at all. It was the leading suspect and is not involved.
  • qiskit-aer ships a bundled libomp.dylib — the only bundled OpenMP in the environment — but it is never imported by the notebook in question (sys.modules has qiskit_aer: False) and its copy is not among the mapped images.
  • ffsim uses Rust/Rayon, not OpenMP.
  • Not a cross-notebook effect: under nbmake each notebook gets its own kernel.
  • Not fixable from the consumer's LDFLAGS/CPPFLAGS alone: setup.py chose its libomp from hardcoded paths and ignored those variables. (That part is addressed on the darwin-single-libomp branch; see below.)

Ways out

  1. Build with Apple clang and let the standalone libomp serve both SBD and OpenBLAS. Simplest, and what this repo's CI does. Downstream this means not pinning a compiler job-wide — #367 now builds each compiled dependency with the compiler it needs, and its macOS job is green with the released 1.6.1, so no new release is required for that.
  2. Use a conda (or pixi) environment, where one llvm-openmp serves the compiler and the BLAS alike. This is the only option that also covers users rather than just CI, and #23's conda-first preference already points at it. Worth stating outright in the README for macOS.
  3. Use a BLAS not tied to a second OpenMP — Accelerate, or a conda-provided OpenBLAS. This is what would make a Homebrew-LLVM build viable at all.

Note that 1 and 2 leave a sharp edge: if two extensions in one process are built by different compilers, they can still end up with different libomps. Measured downstream — SBD under Apple clang plus fulqrum under Homebrew LLVM gives 2 images again, even though each works alone. The durable fix for that pairing is for fulqrum to accept Apple clang (-Xpreprocessor -fopenmp), after which one runtime serves everything.

Not the fix

KMP_DUPLICATE_LIB_OK=TRUE suppresses the abort, and the runtime's own hint offers it — as "an unsafe, unsupported, undocumented workaround" that "may cause crashes or silently produce incorrect results." For an eigensolver, trading a clean abort for possibly-wrong energies is the wrong trade.

Status

  • Linux is unaffected throughout.
  • The darwin-single-libomp branch makes setup.py prefer a libomp shipped beside the compiler, which removes SBD's direct second copy. It does not fix this issue — OpenBLAS still supplies one — so #34 was closed rather than merged; see that PR for the measurements. The branch is a reasonable starting point once option 3 exists.
  • Two incidental findings worth recording, since each cost a debugging round:
    • omp.h lives in the clang resource directory (lib/clang/<ver>/include), not <prefix>/include. Ask clang++ -print-resource-dir.
    • tox discards the build backend's output at default verbosity, stdout and stderr alike, so a setup.py that picks the wrong OpenMP leaves no trace in CI. tox -vv shows it.

This issue body was updated by Claude Opus 5 under my guidance.
Lingua principale
Python
Stelle
2
Fork
1
Merge medio
1g 10h
PR unite (30g)
23

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di Qiskit/sbd-eigensolver-python

Tutte le issue di Qiskit/sbd-eigensolver-python

Issue simili

Altre issue su Python

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.