Qiskit / Qiskit/sbd-eigensolver-python

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

Offen
#27 1 Kommentar 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Vorherrschende Sprache
Python
Sterne
2
Forks
0
Ø Merge
8 Std. 45 Min.
Gemergte PRs (30 T.)
23

Beschreibung

On macOS, a process that loads sbd._core_cpu alongside another OpenMP-linked library 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)

The extension imports fine; it dies the moment it forks a parallel team:

[ 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...
[12] _core_cpu.cpython-314-darwin.so  pybind11::detail::func...

Both names are libomp.dylib — two copies of the same LLVM runtime, not the more familiar Intel libiomp5 vs LLVM libomp clash. No libgomp or libiomp5 appears anywhere in the failing log.

Where this came from

Qiskit/qiskit-addon-sqd#367 adds a guide that runs sbd-eigensolver under nbmake as part of the notebook suite. Linux passes on 3.10–3.14; macos-latest fails. The goal of this issue is to get that job green.

Failing job: https://github.com/Qiskit/qiskit-addon-sqd/actions/runs/34401508007/job/102634250262 (macos-latest, Python 3.14, sbd-eigensolver==1.6.1 from PyPI).

It is not a conflict between notebooks — under nbmake each notebook gets its own kernel, and the other notebook in the suite passes. Both libomp copies are inside the SBD kernel alone.

Mechanism

The downstream workflow bootstraps a macOS compiler by installing both Homebrew llvm and Homebrew libomp, then exporting flags for both prefixes:

brew install llvm libomp open-mpi openblas
LLVM_PREFIX="$(brew --prefix llvm)"
OMP_PREFIX="$(brew --prefix libomp)"

which reaches the build as:

CC:       /opt/homebrew/opt/llvm/bin/clang
CXX:      /opt/homebrew/opt/llvm/bin/clang++
LDFLAGS:  -L/opt/homebrew/opt/llvm/lib -L/opt/homebrew/opt/libomp/lib -Wl,-rpath,/opt/homebrew/opt/llvm/lib
CPPFLAGS: -I/opt/homebrew/opt/llvm/include -I/opt/homebrew/opt/libomp/include

Homebrew's llvm formula ships its own libomp.dylib under opt/llvm/lib, distinct from the standalone libomp formula's copy under opt/libomp/lib. Both formulae are keg-only, so each is reachable only by explicit path — and here both paths are on the same line, with only llvm/lib in the rpath. Two same-named copies, one process.

That step predates this guide (it was added so fulqrum could build) and #367 extends it, which is why the interaction only surfaced now.

Worth noting this is the exact hazard #23 added a diagnostic for: setup.py now prints the resolved compiler because "conda records a bare clang++, which PATH resolves, so a Homebrew LLVM silently wins over both Apple clang and a conda toolchain." That diagnostic is absent from the failing log, since 1.6.1 predates it. Re-running against current main would report which compiler and which libomp were actually chosen — the cheapest next step, and it would confirm or kill the reading above.

A third-party wheel bringing its own runtime is the alternative explanation. The notebook environment also has:

numpy==2.5.3   scipy==1.18.1   pyscf==2.14.0   qiskit-aer==0.17.2
jax==0.11.1    jaxlib==0.11.1  primme==3.2.3   fulqrum==0.4.4

I would not name a culprit without checking; otool -L on the installed extensions, or DYLD_PRINT_LIBRARIES=1 on the failing kernel, would settle it.

Relationship to #20

#20 (macOS support: classifiers + a macOS CI matrix, plus brew --prefix, keg-only -L, and an -Wl,-rpath spelling fix) is not expected to resolve this, and its green macOS cells do not cover it:

  • #20 fixes link- and load-time resolution — finding the right libomp and recording an rpath so it resolves at import.
  • This abort is runtime initialization — two OpenMP runtimes co-resident in one process.

Linking correctly against one libomp does not prevent a second arriving by another route. The two are independent properties, and #20 establishes only the first. Its macOS cells run SBD's own suite, which neither co-loads a scientific stack nor builds under Homebrew LLVM, so a fully green matrix there is consistent with this still failing.

Related but distinct is the intra-package reasoning already in setup.py (the comment above build_backend): the three backends were once kept apart on OpenMP-runtime grounds, now resolved by lazy loading. That covers SBD's own modules in one process, not a foreign runtime.

Possible paths forward

Roughly in order of cost, and not mutually exclusive:

  1. Re-run #367's macOS job against current main to capture #23's compiler/libomp diagnostic. Nearly free, and it tells us whether to pursue (2) or (3).

  2. Reproduce it in this repo's CI. A macOS cell built the way downstream builds — Homebrew LLVM with both llvm and libomp prefixes on the flags — that then runs a diagonalization. The import alone is not enough: the abort is at the first __kmpc_fork_call, so the test has to reach sbd::tpb::diag. This is the piece that would have caught it, and it makes everything else verifiable rather than speculative.

  3. Decide which libomp a macOS build should use, and use only that one. If the mechanism above holds, the fix is to not build against opt/llvm/lib and opt/libomp/lib simultaneously — either prefer the compiler's own runtime or the standalone formula's, but not both. #23's conda-first preference already leans this way, which may be why conda environments appear less affected; worth checking whether a conda-built SBD reproduces the abort at all. Some of this may be a fix downstream in the bootstrap step rather than here — that is worth deciding explicitly rather than by default.

  4. Document it. A troubleshooting entry for Error #15: the cause, that KMP_DUPLICATE_LIB_OK is not a sanctioned fix (below), and how to identify the second runtime with otool -L. Useful even after a code fix, since users will hit this with their own stacks.

  5. Consider whether the CPU extension should link OpenMP at all on macOS, or link it so a foreign copy is tolerated. Deepest option, probably needs upstream input; listed for completeness rather than proposed.

On KMP_DUPLICATE_LIB_OK

KMP_DUPLICATE_LIB_OK=TRUE suppresses the abort, and the runtime's own hint mentions 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 a bad deal, so I would rather not rely on it downstream. Flagging it so it is not mistaken for a fix.

Current downstream state

#367 does not skip the SBD notebook on macOS — tests (macos-latest, 3.14) is still in the matrix and still red. So this is currently blocking that PR rather than being worked around in it.


This issue was generated by Claude Opus 5 under my guidance.

Beitragsleitfaden

Für dieses Repository ist kein Beitragsleitfaden indexiert

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

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.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
macos, python
Bereich
build-system, ci-cd, operating-systems, testing-qa
Issue-Typ
Bug
Schwierigkeit
5/5
Geschätzter Aufwand
Über eine Woche
Aktivitätsstatus
Aktiv
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
35/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.