align LDA to Sklearn #914
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries | |
| # https://github.com/pypa/cibuildwheel/blob/main/examples/github-deploy.yml | |
| name: Build and upload to PyPI | |
| on: | |
| workflow_dispatch: | |
| pull_request: | |
| push: | |
| branches: [main] | |
| release: | |
| types: [published] | |
| jobs: | |
| build_wheels: | |
| name: Build wheels for ${{ matrix.os }}-cu${{ matrix.cuda_major }} | |
| runs-on: ${{ matrix.runs-on }} | |
| strategy: | |
| matrix: | |
| include: | |
| # CUDA 12 wheels (compiled with 12.2 for runtime 12.2+ compatibility) | |
| - os: linux-intel | |
| cuda_major: "12" | |
| cuda_minor: "2" | |
| runs-on: ubuntu-latest | |
| cibw_image: "quay.io/manylinux_cuda/manylinux_2_28_x86_64_cuda12_2:latest" | |
| cuda_archs: "75-real;80-real;86-real;89-real;90" | |
| - os: linux-arm | |
| cuda_major: "12" | |
| cuda_minor: "2" | |
| runs-on: ubuntu-24.04-arm | |
| cibw_image: "quay.io/manylinux_cuda/manylinux_2_28_aarch64_cuda12_2:latest" | |
| cuda_archs: "75-real;80-real;86-real;89-real;90" | |
| # CUDA 13 wheels (native Blackwell support) | |
| - os: linux-intel | |
| cuda_major: "13" | |
| cuda_minor: "0" | |
| runs-on: ubuntu-latest | |
| cibw_image: "quay.io/manylinux_cuda/manylinux_2_28_x86_64_cuda13_0:latest" | |
| cuda_archs: "75-real;80-real;86-real;89-real;90-real;100-real;120" | |
| - os: linux-arm | |
| cuda_major: "13" | |
| cuda_minor: "0" | |
| runs-on: ubuntu-24.04-arm | |
| cibw_image: "quay.io/manylinux_cuda/manylinux_2_28_aarch64_cuda13_0:latest" | |
| cuda_archs: "75-real;80-real;86-real;89-real;90-real;100-real;120" | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| - name: Get version from hatch-vcs | |
| id: version | |
| run: | | |
| pip install hatchling hatch-vcs | |
| VERSION=$(python -m hatchling version) | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "Detected version: $VERSION" | |
| - name: Set package name, extras, and CUDA archs for CUDA ${{ matrix.cuda_major }} | |
| run: | | |
| python3 - ${{ matrix.cuda_major }} "${{ matrix.cuda_archs }}" "${{ steps.version.outputs.version }}" <<'SCRIPT' | |
| import re, sys, pathlib | |
| cuda = sys.argv[1] | |
| cuda_archs = sys.argv[2] | |
| version = sys.argv[3] | |
| other = "13" if cuda == "12" else "12" | |
| path = pathlib.Path("pyproject.toml") | |
| text = path.read_text() | |
| def remove_toml_array(text, key): | |
| lines = text.splitlines(keepends=True) | |
| out = [] | |
| i = 0 | |
| while i < len(lines): | |
| if lines[i].startswith(f"{key} = ["): | |
| depth = lines[i].count("[") - lines[i].count("]") | |
| i += 1 | |
| while i < len(lines) and depth > 0: | |
| depth += lines[i].count("[") - lines[i].count("]") | |
| i += 1 | |
| continue | |
| out.append(lines[i]) | |
| i += 1 | |
| return "".join(out) | |
| # Rename package | |
| text = text.replace( | |
| 'name = "rapids-singlecell"', | |
| f'name = "rapids-singlecell-cu{cuda}"', | |
| ) | |
| text = text.replace( | |
| 'dynamic = [ "version" ]', | |
| f'version = "{version}"', | |
| ) | |
| # Rewrite self-referential extras (e.g. "rapids_singlecell[test-minimal]") | |
| # to the renamed distribution, so `pip install rapids-singlecell-cuXX[test]` | |
| # composes extras within this wheel instead of pulling the plain | |
| # rapids-singlecell package from PyPI alongside it. | |
| text, n_self = re.subn( | |
| r'"rapids[-_]singlecell\[', | |
| f'"rapids-singlecell-cu{cuda}[', | |
| text, | |
| ) | |
| print(f"rewrote {n_self} self-referential extra(s)") | |
| if n_self == 0: | |
| raise SystemExit("expected at least one self-referential extra to rewrite") | |
| # Rename matching extra to "rapids", remove the other | |
| text = text.replace(f'rapids-cu{cuda} = [', 'rapids = [') | |
| text = remove_toml_array(text, f"rapids-cu{other}") | |
| # Set CUDA architectures (replace "native" with CI target archs) | |
| text = text.replace( | |
| 'CMAKE_CUDA_ARCHITECTURES = "native"', | |
| f'CMAKE_CUDA_ARCHITECTURES = "{cuda_archs}"', | |
| ) | |
| path.write_text(text) | |
| # Verify | |
| for line in path.read_text().splitlines(): | |
| if "name" in line or "rapids" in line.lower() or "CUDA_ARCH" in line: | |
| print(line) | |
| SCRIPT | |
| - name: Sanity check pyproject.toml | |
| run: | | |
| python3 -c "import tomllib; tomllib.load(open('pyproject.toml', 'rb'))" | |
| grep -E "name|rapids|CUDA_ARCH" pyproject.toml | |
| - name: Build wheels | |
| uses: pypa/cibuildwheel@v3.4.1 | |
| env: | |
| CIBW_BUILD: 'cp312-*' | |
| CIBW_SKIP: '*-musllinux*' | |
| CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.os == 'linux-intel' && matrix.cibw_image || '' }} | |
| CIBW_MANYLINUX_AARCH64_IMAGE: ${{ matrix.os == 'linux-arm' && matrix.cibw_image || '' }} | |
| CIBW_ENVIRONMENT: > | |
| CUDA_PATH=/usr/local/cuda | |
| LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH | |
| PATH=/usr/local/cuda/bin:$PATH | |
| CIBW_BEFORE_ALL: > | |
| dnf install -y --setopt=obsoletes=0 | |
| libcusolver-devel-${{ matrix.cuda_major }}-${{ matrix.cuda_minor }} | |
| libcusparse-devel-${{ matrix.cuda_major }}-${{ matrix.cuda_minor }} | |
| libnvjitlink-${{ matrix.cuda_major }}-${{ matrix.cuda_minor }} | |
| CIBW_BEFORE_BUILD: > | |
| python -m pip install -U pip | |
| scikit-build-core cmake ninja nanobind | |
| CIBW_TEST_SKIP: "*" | |
| CIBW_TEST_COMMAND: "" | |
| # Exclude CUDA runtime libs provided by dependency wheels. | |
| # Use SONAME globs so CUDA 12/13 suffix changes do not bundle them. | |
| CIBW_REPAIR_WHEEL_COMMAND: "auditwheel repair --exclude 'libcublas.so.*' --exclude 'libcublasLt.so.*' --exclude 'libcudart.so.*' --exclude 'libcusolver.so.*' --exclude 'libcusparse.so.*' --exclude 'libnvJitLink.so.*' -w {dest_dir} {wheel}" | |
| CIBW_BUILD_VERBOSITY: "1" | |
| - name: Check wheels link no RAPIDS C++ libs | |
| run: | | |
| python3 - <<'SCRIPT' | |
| import pathlib | |
| import shutil | |
| import subprocess | |
| import sys | |
| import tempfile | |
| import zipfile | |
| readelf = shutil.which("readelf") | |
| if readelf is None: | |
| raise SystemExit("readelf is required for the RAPIDS linkage check") | |
| # Kernels use a CuPy-backed scratch allocator and the CUDA toolkit's | |
| # bundled CCCL, so no extension may pull in librmm/librapids_logger. | |
| banned = ("librmm", "librapids_logger") | |
| failures = [] | |
| for wheel in sorted(pathlib.Path("wheelhouse").glob("*.whl")): | |
| with tempfile.TemporaryDirectory() as tmp: | |
| tmp_path = pathlib.Path(tmp) | |
| with zipfile.ZipFile(wheel) as zf: | |
| for name in zf.namelist(): | |
| if name.startswith("rapids_singlecell/_cuda/") and name.endswith(".so"): | |
| zf.extract(name, tmp_path) | |
| for ext in sorted((tmp_path / "rapids_singlecell" / "_cuda").glob("*.so")): | |
| result = subprocess.run( | |
| [readelf, "-d", str(ext)], | |
| check=True, | |
| capture_output=True, | |
| text=True, | |
| ) | |
| for line in result.stdout.splitlines(): | |
| if "(NEEDED)" in line and any(b in line for b in banned): | |
| failures.append(f"{wheel.name}:{ext.name}: {line.strip()}") | |
| if failures: | |
| print( | |
| "Wheel links against RAPIDS C++ libs, which are no longer a " | |
| "build or runtime dependency.", | |
| file=sys.stderr, | |
| ) | |
| print("\n".join(failures), file=sys.stderr) | |
| raise SystemExit(1) | |
| SCRIPT | |
| - uses: actions/upload-artifact@v7 | |
| with: | |
| name: cibw-wheels-${{ matrix.os }}-cu${{ matrix.cuda_major }}-${{ strategy.job-index }} | |
| path: ./wheelhouse/*.whl | |
| build_sdist: | |
| name: Build source distribution | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| # Full history + tags so hatch-vcs resolves the real version. | |
| # Without this, non-tag runs produce a bogus 0.1.dev1 sdist. | |
| fetch-depth: 0 | |
| - name: Build sdist | |
| run: pipx run build --sdist | |
| - uses: actions/upload-artifact@v7 | |
| with: | |
| name: cibw-sdist | |
| path: dist/*.tar.gz | |
| upload_wheels: | |
| needs: [build_wheels] | |
| runs-on: ubuntu-latest | |
| environment: publish-cu${{ matrix.cuda_major }} | |
| permissions: | |
| id-token: write | |
| if: github.event_name == 'release' && github.event.action == 'published' | |
| strategy: | |
| matrix: | |
| cuda_major: ["12", "13"] | |
| steps: | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| pattern: cibw-wheels-*-cu${{ matrix.cuda_major }}-* | |
| path: dist | |
| merge-multiple: true | |
| - uses: pypa/gh-action-pypi-publish@release/v1 | |
| upload_sdist: | |
| needs: [build_sdist] | |
| runs-on: ubuntu-latest | |
| environment: publish | |
| permissions: | |
| id-token: write | |
| if: github.event_name == 'release' && github.event.action == 'published' | |
| steps: | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| name: cibw-sdist | |
| path: dist | |
| - uses: pypa/gh-action-pypi-publish@release/v1 |