Skip to content

Move examples out of the package, split install docs into INSTALL.md - #44

Merged
Sophia Wen (hfwen0502) merged 10 commits into
mainfrom
examples-reorg
Sep 30, 2026
Merged

Sophia Wen (hfwen0502) merged 10 commits into
mainfrom
examples-reorg

Conversation

@hfwen0502

Copy link
Copy Markdown
Member

Docs and layout only — no code changes, no behaviour changes.

What changed

  1. Examples move out of the package to a top-level examples/, grouped by basis: examples/tpb/. Runnable MPI scripts were shipping inside the installed wheel next to __init__.py and bindings.cpp. examples/tpb/ is the same depth as before, so the drivers' ../../vendor/... paths are untouched. A signpost stays at python/examples/README.md so old links don't 404.
  2. Install instructions move to INSTALL.md — they were 190 of the README's 424 lines. The README keeps a quickstart (conda env, pip install, available_backends() check) because it is also the PyPI long_description.
  3. No intermediate examples/README.md — each solver folder's README stands alone.

Corrections found while moving

Worth a look, since they go slightly beyond a move:

  • Removed a claim that GPU backends need a GPU-aware MPI. Not true for the OMP-offload path, and SBD_NON_CUDA_AWARE_MPI exists for Thrust. Backend Architecture already states it accurately.
  • OMP_NUM_THREADS=1 was advised for GPU runs. Helper construction is host-threaded in both solvers, so that starves it; now "divide the node's cores among the ranks".
  • MANIFEST.in gained *.json, so count_dict_h2o.json — which run_sqd_sbd.py defaults to — now reaches the sdist.
  • Troubleshooting audited against the code: the MPI_HOME entry was stale (the build now hard-errors with a specific message); added the macOS OMP: Error #15 symptom (macOS: OMP Error #15 (duplicate libomp) aborts at first parallel region #27) and multi-rank GPU GDB failing on the helper dimension.
  • The GDB config table said b_comm_size "must be 1" without saying whose limit that is. It's ours — upstream's in-memory gdb::diag expects a per-rank shard, and upstream's own run.sh uses --b_comm_size 2. t_comm_size == 1 then follows from upstream's algorithm.

Verification

  • run_sbd_diag.py runs from its new location, resolving its default vendored paths: -76.2359465468
  • sdist contains examples/, examples/tpb/ with all six files, INSTALL.md, and the signpost
  • every relative link in all five markdown files resolves; no python/examples reference survives except the MANIFEST line that ships the signpost
  • pytest test/ unchanged: 15 passed

A follow-up PR (gdb-sharding) builds on this and adds examples/gdb/. Reviewing this one first keeps the rename out of that diff.

🤖 Generated with Claude Code

Pure reorganization, no behaviour change, so that a later PR adding a second
solver's drivers is additive rather than a rename plus a feature.

Runnable MPI scripts were living inside the installed package: they shipped in the
wheel, sat beside __init__.py and bindings.cpp, and a path like
python/examples/run_sbd_diag.py reads as package internals. They move to a top-level
examples/, which is the common convention for runnable scripts, and are grouped by
basis type -- examples/tpb/ -- since the solvers take different subspaces and
decompose over MPI differently. (The sibling qiskit-addon-sqd has no examples/ only
because its tutorials are notebooks rendered into Sphinx docs; ours are CLI drivers.)

examples/tpb/ is the same depth as the old location, so every
../../vendor/sbd-upstream/... path inside the drivers keeps working untouched.

What is package-level rather than example-level -- backend selection, the --device
table, bundled test data, performance notes -- moves to examples/README.md, which the
solver README links to instead of restating. The GDB decomposition paragraph goes
there too so nothing is lost; a later PR relocates and expands it.

python/examples/README.md stays behind as a signpost, since git mv removes the
directory outright and links to python/examples/... would otherwise 404. It can be
deleted once those links age out.

The top README now names the folders and defers to their READMEs instead of listing
every example file, which is the part that rots whenever a driver is added.

Reference updates: MANIFEST.in, tox.ini's notebook env, python/__init__.py's citation
of the rank-to-device rule. Two small fixes ride along: MANIFEST.in gained *.json so
count_dict_h2o.json -- which run_sqd_sbd.py defaults to -- now reaches the sdist, and
the bundled-data paths in the shared README are stated relative to a solver folder,
matching how the drivers' own defaults are spelled.

Verified: run_sbd_diag.py runs from examples/tpb/ resolving its default vendored
paths (-76.2359465468); the sdist contains examples/, examples/tpb/ with all six
files, and the signpost; no reference to python/examples survives except the
MANIFEST line that ships the signpost.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Installation was 190 of the README's 424 lines -- 45% of the landing page spent on
prerequisites, every environment variable the build reads, host-MPI builds and
verification, before a reader learned what the package does. It moves to INSTALL.md,
taking the README to 264 lines.

The README keeps a quickstart rather than deferring everything, because it is also the
PyPI long_description: a conda env, pip install, and the available_backends() check
that tells you which backends the build produced, plus the GPU-aware MPI requirement,
which is the one prerequisite easy to miss and expensive to diagnose. Everything else
-- per-platform prerequisites, installing from a checkout with the submodule, the full
environment-variable matrix, narrowing which backends get built, building against an
existing host MPI -- is one link away.

Extracted subsections are promoted to top-level headings, the one intra-README
reference ("see Backend Architecture below") now names the README explicitly, and
INSTALL.md is added to MANIFEST.in so it reaches the sdist.

Verified: every relative link in both files resolves, the three referenced anchors
exist, and the sdist contains INSTALL.md alongside the READMEs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…constraint is

Two corrections.

The install quickstart asserted that GPU backends "need a GPU-aware MPI ... because
every one of them hands MPI device pointers". That is wrong twice over: it is not every
backend -- the OpenMP-offload path works with an MPI that cannot address device memory,
since ROCm device allocations are host-addressable -- and it is not a hard requirement
even for Thrust, which has two documented build-time escape hatches
(SBD_NON_CUDA_AWARE_MPI, SBD_THRUST_SAFE_MPI_ALLREDUCE). The Backend Architecture
section already states it accurately as an assumption of the Thrust build with those
hatches, and INSTALL.md carries the detail, so the quickstart line is removed rather
than reworded.

The GDB config table said b_comm_size "must be 1" and left t_comm_size unexplained,
which invites the reader to assume both are SBD limitations. They are not the same kind
of thing. b_comm_size == 1 is OUR calling convention: upstream's in-memory gdb::diag
expects each rank to pass its own shard -- that is what its file-based path feeds it
after distributing files over b_comm -- while this wrapper passes the whole list from
every rank, and upstream's own run.sh for the GDB app uses --b_comm_size 2. t_comm_size
== 1 is then UPSTREAM's algorithm: the matvec rotates the ket around b_comm as a ring,
so there are exactly b_comm_size stations and one task is one station, hence
t_comm_size cannot exceed b_comm_size. Also notes that the derived helper dimension
still absorbs the remaining ranks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…vented one

When splitting install out to INSTALL.md I wrote a shortened environment command from
scratch instead of reusing the one the project documents. It diverged four ways:
python=3.12 instead of the pinned 3.13.12, no -y, no pybind11/numpy/setuptools/wheel/
pip/pyscf, and -- the one that actually matters -- it dropped the "plus llvm-openmp on
macOS" note. setup.py's Darwin path looks for omp.h in the conda env, so a macOS reader
following the quickstart would have hit exactly the OpenMP problem issue #27 is about.

The build-tool packages are arguably redundant under pip's build isolation, since
pyproject's [build-system] requires supplies them, but that is not a judgement to make
silently in a quickstart -- and two different install commands in one repository is a
maintenance trap regardless. Now verbatim from INSTALL.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Checked all five against what the build and bindings actually do now. Three hold
unchanged, one was stale, and two symptoms we know about were missing.

Still accurate, verified: the conda CC/CXX entry (setup.py uses os.environ.setdefault,
so a caller-set compiler still suppresses nvc++/amdclang++); the GPU-not-building
entry; and the Bus error / SIGSEGV entry, including both of its misleading cases.

NOT resolved, contrary to how it might read: the "all ranks land on GPU 0" entry. The
omp_get_num_devices() fallback to counting the vendor visibility variable is still
there, and so is omp_set_default_device(mpi_rank % n_dev). Added the caveat that the
index is the GLOBAL rank, so the assignment is only even when the launcher places ranks
on nodes in contiguous blocks; round-robin placement leaves each node on a strided
subset of its GPUs.

Stale: "MPI errors: verify MPI_HOME, check MPI.Get_version()" predates the build
learning to diagnose this. setup.py now stops with "MPI_HOME=... is not the MPI that
mpi4py is linked against", so the entry now names that message, explains why it is fatal
rather than a warning, and gives the two resolutions.

Added, both real and previously undocumented here: the macOS OMP Error #15 duplicate
libomp abort, which kills the first diagonalization while letting the import succeed
(issue #27); and multi-rank GPU GDB failing with "GDB Thrust mult does not support
h_comm_size > 1", which is unavoidable in this release because b_comm_size is pinned to
1 and every added rank therefore lands in the helper dimension.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…one thread on GPU

Two things a reader could not act on.

"# NOT in the same process as 'cpu'" said don't without saying what happens. Both
backends link the same OpenMP runtime and _core_cpu is built without offload support, so
whichever loads first initializes it host-only; if that is the CPU backend, the
OMP-offload backend cannot acquire a device and silently runs its target regions on the
host -- right answers, exit 0, idle GPU, nothing to catch. Now stated, with
loaded_backends() as the way to check, and with the note that 'cpu' and 'gpu' (Thrust)
do coexist because Thrust does not route device work through OpenMP.

"GPU: One MPI rank per GPU, OMP_NUM_THREADS=1" is wrong advice. One rank per GPU is
about device ownership; it says nothing about threads, and a GPU build still does real
host work. Helper construction is host-threaded in both solvers (tpb/helper.h has 15 omp
regions, gdb/helper.h one), and for GDB the heatbath expansion and carryover selection
have no device implementation at all -- gdb/expansion.h and gdb/carryover.h contain no
thrust:: code and are included outside any SBD_THRUST guard. One thread per rank
single-threads all of that; the guidance is now to divide the node's physical cores among
the ranks exactly as for a CPU run, and to expect GPU utilisation below 100% because of
the host phases rather than read it as a fault.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An intermediate examples/README.md meant a reader looking for how to run something had
to visit two files. Removed, with its content moved to where it is used rather than
deleted:

- Backend Selection (the --device values, available_backends()/loaded_backends(), and
  why 'cpu' and 'gpu-omp' must not share a process) -> examples/tpb/README.md
- Available Test Data (the bundled h2o and n2 alpha lists) -> examples/tpb/README.md,
  whose drivers are what consume them
- Performance Tips (thread counts for CPU and GPU runs) -> examples/tpb/README.md

Its GDB paragraph is dropped rather than moved: the top README's Configuration section
now explains the b_comm_size and t_comm_size constraints more fully, including whose
limitation each one is, so the shorter version was redundant.

References repointed: the top README indexes the folder directly, INSTALL.md's See Also,
python/__init__.py's citation of the rank-to-device rule, and the python/examples
signpost. Verified every relative link in all four files resolves and the sdist still
ships the examples tree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PR #18 landed test/test_sqd_integration.py while this branch was open. Its
conftest resolves the curated h2o counts under python/examples/, which this
branch moves to examples/tpb/. Neither change conflicts textually, so the merge
was clean and the merged tree still broke -- every CI job failed on

    FileNotFoundError: .../python/examples/count_dict_h2o.json

which is exactly what the comment above COUNTS_PATH says should happen when a
move does not update it. Retarget the constant and drop the MANIFEST.in line
for the shared examples README this branch removed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…<root>/bin

Detection probed exactly one path, $NVHPC_HOME/bin/nvc++. NVHPC does not put the
compiler there: the version root holds compilers/ cuda/ math_libs/ comm_libs/ and
the binary is at <root>/compilers/bin/nvc++. NVIDIA's own modulefile does

    setenv NVHPC_ROOT $nvhome/$target/$version
    prepend-path PATH $nvcompdir/bin

so NVHPC_ROOT names the version root, one level above the compiler. Setting
NVHPC_HOME to that root -- the natural guess, and what copying $NVHPC_ROOT gives
you -- found nothing, and NVHPC_ROOT was never consulted.

Neither failure is loud. SBD_BUILD_BACKEND=auto falls back to a CPU-only build,
pip hides setup.py's output without -v, and the install exits 0; the user finds
out later from "Device 'gpu' requested but its backend is not usable". Verified on
a real NVHPC 26.3 tree with nvc++ off PATH: before, both NVHPC_HOME=<version root>
and NVHPC_ROOT=<version root> gave "will build CPU backend only"; after, both give
"will build CPU, Thrust GPU and OMP-offload GPU backends".

Read NVHPC_ROOT as well as NVHPC_HOME, and under each try bin/ and compilers/bin/,
so the compilers directory, the version root, and a bare `module load nvhpc` all
work. Also compare PATH entries rather than substrings when deciding whether the
directory is already there. The "Found NVIDIA HPC SDK at: <path>" prefix is kept
because the troubleshooting section quotes it; the variable name is appended.

test/test_build_detection.py is new -- setup.py had no tests. It extracts the
function rather than importing setup.py, which would run a build, and covers both
variables against both layouts, a stale NVHPC_HOME not masking a good NVHPC_ROOT,
and a negative control so the suite cannot pass against a function that always
claims success.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@garrison Jim Garrison (garrison) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you -- this looks reasonable to me.

The addition of test/test_build_detection.py is unrelated to the PR description I think, but it looks reasonable to me as well.

@garrison
Jim Garrison (garrison) added this pull request to stack #47 September 30, 2026 16:55
Flagged in review as unrelated to the PR description, which it is -- this branch
moves examples and splits the install docs. The setup.py NVHPC_ROOT fix stays,
since INSTALL.md documents the variable and the two belong together, but its test
does not need to ride along here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@hfwen0502
Sophia Wen (hfwen0502) merged commit d075325 into main Sep 30, 2026
11 checks passed
@hfwen0502
Sophia Wen (hfwen0502) deleted the examples-reorg branch September 30, 2026 18:03
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