Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# clang-format configuration for the CUDA/C++ extension (libs/ccc_cuda_ext).
# Microsoft base style already uses Allman braces and 4-space indentation,
# which matches the majority style of the existing sources, minimizing churn.
---
BasedOnStyle: Microsoft
IndentWidth: 4
ColumnLimit: 120
PointerAlignment: Right
SortIncludes: CaseSensitive
29 changes: 29 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# clang-tidy configuration for the CUDA/C++ extension (libs/ccc_cuda_ext).
#
# Curated check list targeting the bug classes actually found in review
# (integer narrowing, unchecked returns). Run against the exported
# compile_commands.json, host-only pass:
#
# mamba run -n ccc-gpu-dev clang-tidy \
# -p build \
# --extra-arg=--cuda-host-only \
# libs/ccc_cuda_ext/*.cu
#
# CI runs this report-only at first; promote to blocking once the baseline
# is clean.
Checks: >
-*,
clang-analyzer-*,
bugprone-*,
performance-*,
modernize-*,
readability-braces-around-statements,
readability-else-after-return,
cppcoreguidelines-init-variables,
misc-unused-parameters,
-modernize-use-trailing-return-type,
-modernize-avoid-c-arrays,
-bugprone-easily-swappable-parameters
WarningsAsErrors: ''
HeaderFilterRegex: 'libs/ccc_cuda_ext/.*\.cuh$'
FormatStyle: file
11 changes: 11 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Revisions listed here are skipped by `git blame` so that large mechanical
# formatting commits do not obscure authorship of the surrounding code.
#
# Enable locally (one-time):
# git config blame.ignoreRevsFile .git-blame-ignore-revs
#
# GitHub honors this file automatically.
#
# Append the SHA of the isolated "apply ruff-format + clang-format" commit
# from the add-lint-tooling change below, e.g.:
# <full-40-char-sha> # style: apply ruff-format + clang-format (add-lint-tooling)
151 changes: 151 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
name: CI

# Runs on every push to the default branch and on every pull request.
#
# NOTE: GPU tests (tests/gpu/**) are intentionally NOT run here — hosted GitHub
# runners have no CUDA device. They are a documented LOCAL command:
# mamba run -n ccc-gpu-dev python -m pytest tests/gpu -q -o addopts=""
# CI covers: (a) lint, (b) a no-device wheel/extension build smoke, and
# (c) the CPU-only test subset.
on:
push:
branches: [main, master]
pull_request:

# Keep tool versions in sync with .pre-commit-config.yaml and the dev env.
env:
RUFF_VERSION: "0.15.1"
CLANG_FORMAT_VERSION: "22.1.8"
CUDA_IMAGE: "nvidia/cuda:12.5.1-devel-ubuntu22.04"

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
# ---------------------------------------------------------------------- #
# (a) Lint: Python (ruff) + CUDA/C++ (clang-format). Pure CPU, no CUDA.
# ---------------------------------------------------------------------- #
lint:
name: Lint (ruff + clang-format)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install linters
run: |
python -m pip install --upgrade pip
python -m pip install "ruff==${RUFF_VERSION}" "clang-format==${CLANG_FORMAT_VERSION}"

- name: ruff check
run: ruff check libs/ccc tests

- name: ruff format --check
run: ruff format --check libs/ccc tests

- name: clang-format --dry-run --Werror (CUDA/C++ extension)
run: |
clang-format --dry-run --Werror \
libs/ccc_cuda_ext/*.cu libs/ccc_cuda_ext/*.cuh

# ---------------------------------------------------------------------- #
# (b) Build smoke: compile the CUDA extension in a CUDA devel container.
# nvcc compiles for CMAKE_CUDA_ARCHITECTURES=75 without any GPU device
# present, so this catches compilation breakage on hosted runners.
# ---------------------------------------------------------------------- #
build:
name: Build smoke (no GPU device)
runs-on: ubuntu-latest
container:
image: nvidia/cuda:12.5.1-devel-ubuntu22.04
steps:
- uses: actions/checkout@v4

- name: Install base tools
run: |
apt-get update
apt-get install -y --no-install-recommends git curl ca-certificates build-essential

- name: Install Miniforge
run: |
curl -fsSL -o /tmp/miniforge.sh \
https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-x86_64.sh
bash /tmp/miniforge.sh -b -p /opt/conda
echo "/opt/conda/bin" >> "$GITHUB_PATH"

- name: Build & install the package
env:
CUDACXX: /usr/local/cuda/bin/nvcc
CUDA_HOME: /usr/local/cuda
PATH: /opt/conda/bin:/usr/local/cuda/bin:/usr/bin:/bin
run: |
python -m pip install --upgrade pip
# Build isolation pulls the build backend (scikit-build-core, pybind11,
# cmake>=4, ninja) declared in pyproject [build-system].
python -m pip install . -v

- name: Import CPU path (smoke)
run: |
python - <<'PY'
import ccc
from ccc.coef import ccc as ccc_fn # triggers numba CPU compile at import
import numpy as np
print("CPU coefficient:", ccc_fn(np.random.rand(20), np.random.rand(20)))
print("import OK:", ccc.__name__)
PY

# ---------------------------------------------------------------------- #
# (c) CPU-only tests: the CPU subset of the suite. GPU, network, and
# undeclared-dep tests are ignored (see BASELINE). The two known
# np.unicode_ test-only failures are deselected until restructure-tests
# (PR D) fixes the NumPy-2 alias.
# TODO(PR D): once pytest markers land, replace the --ignore/--deselect
# list with `-m "not gpu and not slow and not network"`.
# ---------------------------------------------------------------------- #
test-cpu:
name: CPU tests
runs-on: ubuntu-latest
container:
image: nvidia/cuda:12.5.1-devel-ubuntu22.04
steps:
- uses: actions/checkout@v4

- name: Install base tools
run: |
apt-get update
apt-get install -y --no-install-recommends git curl ca-certificates build-essential

- name: Install Miniforge
run: |
curl -fsSL -o /tmp/miniforge.sh \
https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-x86_64.sh
bash /tmp/miniforge.sh -b -p /opt/conda
echo "/opt/conda/bin" >> "$GITHUB_PATH"

- name: Build & install the package (+ test deps)
env:
CUDACXX: /usr/local/cuda/bin/nvcc
CUDA_HOME: /usr/local/cuda
PATH: /opt/conda/bin:/usr/local/cuda/bin:/usr/bin:/bin
run: |
python -m pip install --upgrade pip
python -m pip install ".[test]" -v

- name: Run CPU test subset
env:
PATH: /opt/conda/bin:/usr/local/cuda/bin:/usr/bin:/bin
run: |
python -m pytest tests/ \
--ignore=tests/gpu \
--ignore=tests/test_giant.py \
--ignore=tests/test_methods.py \
--ignore=tests/test_plots.py \
--ignore=tests/test_corr.py \
--deselect "tests/test_coef_pval.py::test_cm_numerical_and_categorical_features_perfect_relationship_pvalue" \
--deselect "tests/test_coef_pval.py::test_cm_numerical_and_categorical_features_weakly_relationship_pvalue" \
-q -ra -p no:cacheprovider -o addopts=""
16 changes: 8 additions & 8 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ repos:
- id: check-toml
- id: check-added-large-files
- id: mixed-line-ending
# Python: ruff is the single source of truth for lint + format (black dropped).
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.5 # Ruff version.
rev: v0.15.1 # Ruff version (keep in sync with the dev env / CI).
hooks:
- id: ruff # Run the linter.
types_or: [python, pyi]
args: [--fix]
- id: ruff-format # Run the formatter.
types_or: [python, pyi]
- repo: https://github.com/psf/black
rev: 24.2.0 # Replace with desired version
# C++/CUDA: clang-format, scoped to the extension sources via file filter.
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v22.1.8 # clang-format version (matches the dev env / CI).
hooks:
- id: black
types_or: [python, pyi]
- id: black-jupyter
types_or: [python, pyi, jupyter]
additional_dependencies: [".[jupyter]"]
- id: clang-format
types_or: [c++, c, cuda]
files: ^libs/ccc_cuda_ext/.*\.(cu|cuh|cpp|cc|h)$
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
cmake_minimum_required(VERSION 3.15...3.26)
project(${SKBUILD_PROJECT_NAME} LANGUAGES CUDA CXX)

# Export compile_commands.json so clang-tidy (and editors/LSP) can analyze the
# CUDA/C++ sources. Run clang-tidy host-only, e.g.:
# clang-tidy -p build --extra-arg=--cuda-host-only libs/ccc_cuda_ext/*.cu
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Add this near the top of your file, after project()
# Define the include directories for the whole project
set(PROJECT_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/libs)
Expand Down
3 changes: 2 additions & 1 deletion libs/ccc/coef/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import numpy as np

from ccc.coef.impl import * # noqa: F403, F401

# Run CCC to initialize/compile its functions with numba
from ccc.coef.impl import ccc
import numpy as np

ccc(np.random.rand(10), np.random.rand(10))
9 changes: 4 additions & 5 deletions libs/ccc/coef/impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,16 @@
import os
from collections.abc import Iterable
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor, as_completed
from typing import Union

import numpy as np
from numba import njit
from numba.typed import List
from numpy.typing import NDArray

import numpy as np
from ccc.pytorch.core import unravel_index_2d
from ccc.scipy.stats import rank
from ccc.sklearn.metrics import adjusted_rand_index as ari
from ccc.utils import DummyExecutor, chunker
from numpy.typing import NDArray


@njit(cache=True, nogil=True)
Expand Down Expand Up @@ -319,7 +318,7 @@ def get_coords_from_index(n_obj: int, idx: int) -> tuple[int]:


def get_chunks(
iterable: Union[int, Iterable], n_threads: int, ratio: float = 1
iterable: int | Iterable, n_threads: int, ratio: float = 1
) -> Iterable[Iterable[int]]:
"""
It splits elements in an iterable in chunks according to the number of
Expand Down Expand Up @@ -587,7 +586,7 @@ def get_n_workers(n_jobs: int | None) -> int:
def ccc(
x: NDArray,
y: NDArray = None,
internal_n_clusters: Union[int, Iterable[int]] = None,
internal_n_clusters: int | Iterable[int] = None,
return_parts: bool = False,
n_chunks_threads_ratio: int = 1,
n_jobs: int = 1,
Expand Down
11 changes: 5 additions & 6 deletions libs/ccc/coef/impl_gpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@
import os
from collections.abc import Iterable
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
from typing import Union

import ccc_cuda_ext
import numpy as np
from numba import njit
from numba.typed import List
from numpy.typing import NDArray

import numpy as np
from ccc.scipy.stats import rank
from ccc.utils import chunker
from numpy.typing import NDArray


@njit(cache=True, nogil=True)
Expand Down Expand Up @@ -244,7 +243,7 @@ def get_feature_parts(params):


def get_chunks(
iterable: Union[int, Iterable], n_threads: int, ratio: float = 1
iterable: int | Iterable, n_threads: int, ratio: float = 1
) -> Iterable[Iterable[int]]:
"""
It splits elements in an iterable in chunks according to the number of
Expand Down Expand Up @@ -341,7 +340,7 @@ def get_n_workers(n_jobs: int | None) -> int:
def ccc(
x: NDArray,
y: NDArray = None,
internal_n_clusters: Union[int, Iterable[int]] = None,
internal_n_clusters: int | Iterable[int] = None,
return_parts: bool = False,
n_chunks_threads_ratio: int = 1,
n_jobs: int = 1,
Expand Down Expand Up @@ -617,4 +616,4 @@ def ccc(
return cm_values, max_parts, parts
if pvalue_n_perms is not None and pvalue_n_perms > 0:
return cm_values, cm_pvalues
return cm_values
return cm_values
11 changes: 6 additions & 5 deletions libs/ccc/corr.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@

from __future__ import annotations

import pandas as pd

import numpy as np
import pandas as pd
from sklearn.metrics import pairwise_distances


Expand Down Expand Up @@ -60,9 +59,9 @@ def mic(data: pd.DataFrame, estimator="mic_approx", n_jobs=None) -> pd.DataFrame
Compute the Maximal Correlation Coefficient (MIC).
"""
from minepy import pstats
from scipy.spatial.distance import squareform

from ccc.methods import mic as mic_single
from scipy.spatial.distance import squareform

if n_jobs is None:
corr_mat = pstats(
Expand All @@ -87,9 +86,10 @@ def ccc(data: pd.DataFrame, internal_n_clusters=None, n_jobs=1) -> pd.DataFrame:
"""
Compute the Clustermatch Correlation Coefficient (CCC).
"""
from ccc.coef import ccc
from scipy.spatial.distance import squareform

from ccc.coef import ccc

corr_mat = ccc(
data.to_numpy(),
internal_n_clusters=internal_n_clusters,
Expand All @@ -110,9 +110,10 @@ def ccc_gpu(data: pd.DataFrame, internal_n_clusters=10, n_jobs=24) -> pd.DataFra
"""
Compute the Clustermatch Correlation Coefficient (CCC).
"""
from ccc.coef.impl_gpu import ccc as ccc_gpu
from scipy.spatial.distance import squareform

from ccc.coef.impl_gpu import ccc as ccc_gpu

corr_mat = ccc_gpu(
data.to_numpy(),
internal_n_clusters=internal_n_clusters,
Expand Down
7 changes: 4 additions & 3 deletions libs/ccc/giant.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
Contains functions to interact with the REST API of HumanBase (GIANT networks):
https://hb.flatironinstitute.org/
"""
from pathlib import Path
import tempfile

import json
import tempfile
from pathlib import Path

import requests
import pandas as pd
import requests

URL_GENE_INFO = "https://hb.flatironinstitute.org/api/genes/"
URL_TISSUE_PREDICTION = "https://hb.flatironinstitute.org/api/integrations/relevant/"
Expand Down
Loading
Loading