Skip to content

Refactoring initiative: code quality, docs, testing, benchmarking, deps (proposals 1–7)#39

Open
haoyu-zc wants to merge 15 commits into
mainfrom
refacotring-07-16-2026
Open

Refactoring initiative: code quality, docs, testing, benchmarking, deps (proposals 1–7)#39
haoyu-zc wants to merge 15 commits into
mainfrom
refacotring-07-16-2026

Conversation

@haoyu-zc

Copy link
Copy Markdown
Collaborator

Integration branch for the July 2026 repo-wide refactoring initiative. Bundles proposals 1–7 of the deep-review plan (each landed and reviewed as its own PR into this branch: #33, #34, #35, #36, #37). Proposal 8 (optimization backlog) is a separate PR (#38) into this branch, for review — intentionally not folded in here.

14 commits · 193 files · +8,183 / −9,745.

What's included

  • fix-cuda-correctness — corrects real CUDA p-value bugs: 64-bit indexing, long long sums, removal of the silent MAX_CLUSTERS=16 zero-cliff, consistent invalid-partition semantics for the permutation null, throwing error macros instead of exit(), input-shape validation, and memory-bounded permutation batching. New GPU regression tests.
  • cleanup-dead-code — removes ~4k lines of upstream-fork/dead code (dead compute stack, cub.cu, demo bindings, orphan CMake, broken test stubs, 29 committed logs), replaces the broken Dockerfile, and slims the published wheel.
  • add-lint-tooling — root-level ruff (lint+format) + clang-format/clang-tidy, black dropped, GitHub Actions CI, and an isolated mechanical-format commit tracked in .git-blame-ignore-revs.
  • restructure-tests + extract-benchmarksgpu/slow/network markers so pytest tests/ is green on a CPU-only box, benchmarks pulled out of the test suite, CUDA gtests wired into ctest (gated so wheel builds stay offline), and the new ccc-gpu-bench CLI.
  • improve-docs + upgrade-dependencies — the previously-undocumented p-value argument fully documented (method, estimator, cost warning), an autodoc API page, version single-sourced to 0.2.4, a multi-arch wheel (75;80;86;89;90), modern dependency pins, and a regenerated conda-lock for the Python 3.12 / NumPy 2.x stack.

Testing

Built and verified in a dedicated conda env (Python 3.12, NumPy 2.4, CUDA 12.5, RTX 4090): 139 CPU + 79 GPU + 7 slow tests pass, ctest 8 pass / 2 skip, ccc-gpu-bench works, docs build clean under sphinx-build -W. Every constituent PR was independently rebuilt, re-tested, and code-reviewed before merge into this branch.

Two decisions to confirm before merging to main

  1. License — the LICENSE file is BSD-2-Clause Plus Patent, but CITATION.cff previously said MIT. This branch aligns CITATION/README/pyproject to the LICENSE file. If MIT was intended, that should be flipped instead.
  2. Version bump — the wheel-slimming is a breaking packaging change, so a 0.3.0 bump is warranted before the next release. Not tagged here.

Related

Haoyu Zhang added 15 commits July 16, 2026 14:28
Deep-review-driven improvement plan across code quality, docs, testing,
benchmarking, dependencies, and optimization. Each change carries a
proposal, design, delta specs, and task breakdown.
…ness)

OpenSpec change: fix-cuda-correctness. Fixes correctness bugs found in the
2026-07-16 review, concentrated in the CUDA p-value permutation path.

Error handling
- utils.cuh: gpu_assert/CUDA_CHECK* now throw std::runtime_error (pybind11 ->
  Python RuntimeError) instead of exit(); new CUDA_CHECK_KERNEL checks launch +
  sync after every kernel. No exit() remains in library code.
- metrics.cu: process_input_array validates dtype/ndim/shape against
  (n_features, n_parts, n_objs) and raises ValueError; underflow guard reordered.
- coef.cu: compute_coef validates n_partitions in (0, 255].

P-value path correctness
- 64-bit indexing (math.cuh get_coords_from_index overload; computePValues) so
  n_feature_comp * n_perms no longer wraps at 2^32.
- long long pair-confusion sums in computePermutedARI (no int overflow at
  ~46k+ objects/cluster).
- Removed the MAX_CLUSTERS=16 silent-zero cliff: per-thread global scratch sized
  by k supports arbitrary cluster counts.
- Null distribution now uses the same invalid-partition semantics as the observed
  statistic (shared classify_partition_pair helper: categorical -1 -> 0.0,
  singleton -2 -> NaN, clamp >= 0); permute-target matches the CPU reference.
- Memory-bounded batching of d_perm_ccc_values (chunked) so large inputs don't OOM.
- findMaxAriKernel negative-key path no longer writes garbage into max_parts.

Python
- Fixed the inverted -1/-2 singleton/categorical legend in ccc() docstrings
  (impl_gpu.py, impl.py) to match get_parts and the code.

Tests
- New tests/gpu/test_cuda_correctness.py (9 tests): shape/partition validation,
  interpreter survival, >16-cluster p-values vs CPU, categorical & singleton
  p-value consistency vs CPU.

Behavior changes: previously-silent failures now raise; p-values change for
partitions with >16 clusters or categorical/singleton features (old values were
biased/wrong; new values track the CPU reference).
The per-permutation contingency scratch scaled as n_perms * k^2 and was
allocated in full before the memory-chunking loop, so the newly-supported
high-cluster p-value path could still OOM (e.g. k=10000 -> ~120 GB). Process
permutations in sub-batches sized to a memory budget (<=1 GiB); scratch is now
indexed by the local sub-batch id, so memory is bounded regardless of k and
large cluster counts simply take more passes. Results are unchanged (each
permutation is independent).
OpenSpec change: cleanup-dead-code. Deletes upstream-fork leftovers and the
GPU cut-over's dead code; slims the published wheel.

Dead Python
- impl_gpu.py: removed the unreachable CPU compute stack (cdist_parts_basic/
  parallel, compute_ccc, compute_ccc_perms, compute_coef, get_coords_from_index),
  dead imports (unravel_index_2d, ari, DummyExecutor, as_completed), the dead
  X_has_cat_features local, and the commented debug-export block. Live ccc() GPU
  path (-> ccc_cuda_ext.compute_coef) untouched.
- Deleted sklearn/metrics_gpu.py (abandoned cupy prototype), empty numpy/
  subpackage, and the stray scaffold libs/ccc/pyproject.toml.

Dead CUDA/C++
- Deleted cub.cu (demo), example_return_optional_vectors (+ decl + binding + its
  test), ari_reduced, the dead double s_mem_size add, orphan
  libs/ccc_cuda_ext/CMakeLists.txt, and the broken test stubs.
- Documented return_parts as Python-side-only in the binder.

Repo hygiene
- Removed 29 committed benchmark .log files, the nested tests/gpu/tests/ dir,
  print-only pseudo-tests, the dead tests/gpu/excluded/ dir, and unused
  ccc-example-*.pkl fixtures; fixed tests/data/README.md naming.
- .gitignore: ignore *.log, tests/**/logs/, build dirs, compile_commands.json.

Docker / environment
- Replaced the broken upstream Dockerfile (copied a nonexistent env file, no CUDA
  base) with a minimal nvidia/cuda:12.5.1-runtime + conda-lock image referencing
  only files that exist; deleted entrypoint.sh and upstream environment leftovers.

Wheel scoping (BREAKING for wheel imports)
- Excluded research-only modules (plots.py, methods.py, giant.py, corr.py) from
  the published wheel; they remain in-repo for analysis/. log.py/log_config.yaml
  kept (used by ccc.utils.curl and tests); added pyyaml to declared deps so every
  shipped module imports cleanly. See CHANGELOG.md.

Tests: removed 4 tests that exercised the deleted demo binding; focused gate
214 passed, 2 deselected (pre-existing np.unicode_).
…ling)

OpenSpec change: add-lint-tooling. Single-source lint/format config + CI.

- pyproject.toml: recreate the ruff config (deleted with the scaffold in the
  cleanup PR) at the root as the single source; rule set B,I,ARG,C4,EM,PL,PT,
  PTH,RET,SIM,UP,NPY,PD (+E/F/W); documented ignore list splits formatter-owned
  codes from logic-adjacent codes deferred to follow-up; per-file-ignores for
  tests; analysis/ and docs/ excluded.
- .pre-commit-config.yaml: drop black/black-jupyter (ruff-format replaces them),
  bump ruff to v0.15.1, add mirrors-clang-format hook scoped to the extension.
- .clang-format (Microsoft/4-space/120/PointerAlignment Right/sorted includes)
  and .clang-tidy (curated bugprone/performance/modernize checks).
- CMakeLists.txt: export compile_commands.json for clang-tidy.
- .github/workflows/ci.yml: lint (ruff + clang-format check), build, and
  CPU-test jobs (build/test use a CUDA devel container; GPU tests remain local).
- .git-blame-ignore-revs for the mechanical format commit.
Mechanical, tool-generated formatting of the package, tests, and CUDA
extension (scope: libs/ccc, tests, libs/ccc_cuda_ext; analysis/ untouched).
No behavior change intended.

- Python: `ruff format` reflow + `ruff check --fix` safe fixes only
  (import sorting, unused-import removal in tests, Union->|, .format->f-string,
  redundant open mode, NPY201 np.unicode_->np.str_).
- CUDA/C++: `clang-format -i` (Microsoft base, 4-space, 120 cols, sorted
  includes within each group; local headers unmoved).

The NPY201 fix (np.unicode_->np.str_) in tests/test_coef_pval.py incidentally
resolves the 2 pre-existing NumPy-2 test failures — they now pass.
…ture-tests)

OpenSpec change: restructure-tests.

- Markers gpu/slow/network registered with --strict-markers; tests/gpu/conftest.py
  auto-marks the GPU dir and skips cleanly when cupy/ccc_cuda_ext are absent, so
  `pytest tests/ -m "not gpu"` passes on a CPU-only box. tests/conftest.py holds
  shared fixtures; clean_gpu_memory is now an autouse fixture.
- De-benchmarked the parity tests: removed the duplicated timing/logging/log-file
  machinery from test_ccc_gpu.py and test_ccc_gpu_return_parts.py (kept the
  assert_frame_equal parity at 1e-6), removed wall-clock speedup assertions from
  the n_jobs tests (now determinism checks), deleted the pure-timing benchmark
  tests (their intent moves to ccc-gpu-bench).
- CUDA gtests wired into ctest behind option(CCC_BUILD_TESTS OFF); googletest
  FetchContent gated inside the option so default/wheel builds do not fetch it;
  registered via gtest_discover_tests. Two oversized reference cases GTEST_SKIP.
- Closed GPU coverage gaps (all pass after the fix-cuda-correctness fixes):
  categorical return_parts parity, constant-feature matrix, too-few-objects error
  parity, mixed numerical+categorical DataFrame (tests/gpu/test_coverage_gaps.py).
- Updated scripts/run_tests.sh, tests/README.md, and ci.yml to the marker-based
  commands. (pyproject.toml also carries the bench console-script for the
  companion extract-benchmarks commit.)
OpenSpec change: extract-benchmarks. Decouples performance measurement from the
test suite into a standalone CLI (`ccc-gpu-bench` / `python -m ccc.bench`).

- Modes: coef (GPU-vs-CPU end-to-end, --pvalue-n-perms/--return-parts/
  --gpu-only/--cpu-only + grids), ari (kernel-level), scaling (CPU n_jobs).
- Structured output: JSONL (default) or CSV, incremental writes, one record per
  case with full config, seed, env metadata (GPU/driver/CUDA/CPU/pkg version),
  and warmup-excluded repeated timings + speedup.
- Presets smoke (seconds) and paper (poster/README grid). --profile gives a CPU
  cProfile category breakdown and prints an nsys suggestion for GPU runs.
- Stdlib-only (argparse); graceful degradation without a GPU. CPU-only smoke test
  for the CLI plumbing (tests/test_bench_cli.py).
- All human-readable status/progress/nsys-suggestion output now goes to stderr
  (cli.py, runners.py), so the default JSONL/CSV written to stdout stays
  machine-parseable when --output is omitted or '-'.
- gpu_available() now probes cupy for an accessible CUDA device count instead of
  only checking that cupy/ccc_cuda_ext are importable, so a device-less host
  (CUDA env on a CPU runner) fails fast with a clear message.
…encies)

OpenSpec change: upgrade-dependencies. Aligns declared pins/config with the
tested modern stack (numpy 2.x / numba / pandas 3 / pybind11 3 / py3.12 /
CUDA 12.x) and broadens GPU support.

- build-system: drop setuptools/wheel; add scikit-build-core>=0.10; pin
  pybind11>=2.13,<4 (verified building on 3.0.3).
- CUDA architectures 75 -> 75;80;86;89;90 (native SASS for Turing..Hopper;
  verified via cuobjdump). Reconciled a single CMake floor (3.26...4.0) across
  pyproject and CMakeLists.txt.
- Extras [project.optional-dependencies]: plots, research, test — so the core
  install stays lean and research modules install on demand.
- plots.py: replaced the private seaborn._freedman_diaconis_bins import with a
  public reimplementation (numerically identical).
- environment: environment-gpu.yml is now the real single lock source (py3.12/
  numpy2/numba/CUDA12.x); folded benchmark/toolchain env files into extras;
  new environment/README.md documents the conda-lock regen command.
- cibuildwheel: toolkit 12.5->12.8 (fixed the stale "12.9" comment), arch list,
  Python range 3.10-3.14.
OpenSpec change: improve-docs.

- P-VALUE DOCS (the reported gap): rewrote the pvalue_n_perms docstring in
  impl.py and impl_gpu.py — one-sided permutation test, estimator
  (count+1)/(n_perms+1), interpretation, min resolvable p ~ 1/(n_perms+1),
  returned tuple shapes, and a cost warning for 2d inputs (a p-value per pair x
  n_perms extra evaluations). Fixed typos (cm_vlaues, pvalue_n_permutations,
  returns_parts, coordiates, maximimized). Added a "Computing p-values" section
  to usage.rst and expanded the README.
- Fixed ccc() return-type annotations to the real polymorphic return.
- Version single-sourcing: ccc.__version__ via importlib.metadata; conf.py reads
  package metadata; CITATION.cff -> 0.2.4. One version everywhere (0.2.4).
- LICENSE: aligned CITATION.cff (was MIT), README, and pyproject classifier to
  the authoritative LICENSE file (BSD-2-Clause-Patent). SEE PR NOTE — confirm.
- Sphinx: added an autodoc API page (api.rst) rendering ccc(); switched theme to
  sphinx_rtd_theme; filled the bindings.rst stub; de-duplicated PUBLISHING.md;
  added Marc Subirana-Granes to authors; corrected the stale citation in
  introduction.rst (Bioinformatics 2026, btag068).
- Added a benchmarking.rst page for the ccc-gpu-bench CLI + README pointer.
- Reconciled README/docs claims (real PyPI install, CUDA/CC floor, Python range).
…codex)

Addresses the two codex findings on PR E:

- [P1] conda-lock.yml still resolved Python 3.10 / NumPy 1.26 and referenced the
  removed environment-gpu-new.yml, so the documented `conda-lock install` built
  the OLD stack. Slimmed environment-gpu.yml to the reproducible build/test/docs
  env (Python 3.12, NumPy 2.x, numba, CUDA 12.5 toolkit, pybind11 3.x, sphinx),
  removing the research/analysis-only deps (minepy/seaborn/etc. — minepy has no
  modern-Python build and blocked the solve), and regenerated conda-lock.yml from
  scratch. The lock now matches the declared source.

- [P2] Removed the `plots` and `research` optional-dependency extras: the modules
  they target (ccc.plots/methods/giant) are intentionally excluded from the wheel
  (cleanup change), so `pip install cccgpu[plots]` could never provide them.
  Their deps are documented for source-checkout/analysis use instead. Kept `test`.
…dex)

--repeats 0 or negative previously crashed in _timed() with a confusing
ValueError from min(times); negative --warmup was silently treated as 0. Add
argparse type validators so invalid values fail cleanly before any work runs.
@haoyu-zc

Copy link
Copy Markdown
Collaborator Author

Codex review of the cumulative diff found one [P2]: the ccc-gpu-bench CLI accepted --repeats 0/negative and then crashed with a confusing ValueError from min(times); negative --warmup was silently treated as 0. Fixed — added argparse validators (--repeats >= 1, --warmup >= 0) so invalid configs fail cleanly. No other blocking issues were identified. Branch remains green (bench CLI test 5 passed).

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.

1 participant