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
66 changes: 66 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: release

# Publishes to PyPI when a GitHub Release is published.
#
# Authentication is PyPI Trusted Publishing (OIDC): GitHub mints a short-lived
# token for this workflow and PyPI verifies it. No API token is stored in the
# repository, so there is nothing to leak or rotate.
on:
release:
types: [published]
workflow_dispatch:

jobs:
sdist:
name: Build sdist
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Build
run: |
python -m pip install --upgrade build twine
python -m build --sdist

- name: Check metadata renders on PyPI
run: python -m twine check --strict dist/*

- name: Verify the sdist actually carries the sources
# cuLOF ships source-only: pip compiles the CUDA extension on the
# target machine. That makes a complete sdist load-bearing, and the
# 0.1.0 release on PyPI shipped without a single .cu file, so it could
# never build. Fail the release rather than publish that again.
run: |
set -euo pipefail
for f in CMakeLists.txt src/cuda/knn.cu src/cuda/lof.cu \
src/cuda/pipeline.cu src/cuda/preprocess.cu \
src/cuda/binding.cpp include/culof.h \
include/culof_internal.cuh src/culof/__init__.py; do
tar tzf dist/*.tar.gz | grep -q "/$f$" \
|| { echo "::error::sdist is missing $f"; exit 1; }
done
echo "sdist contains every file needed to build"

- uses: actions/upload-artifact@v4
with:
name: sdist
path: dist/*.tar.gz

publish:
name: Publish to PyPI
needs: sdist
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write # required for trusted publishing
steps:
- uses: actions/download-artifact@v4
with:
name: sdist
path: dist

- uses: pypa/gh-action-pypi-publish@release/v1
20 changes: 20 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,24 @@
cmake_minimum_required(VERSION 3.18)

# ---------------------------------------------------------------------------
# Find nvcc before the CUDA language is enabled.
#
# CMake searches only PATH for nvcc. The usual Linux install puts the toolkit
# under /usr/local/cuda and only the driver on PATH, which would otherwise be
# indistinguishable from having no CUDA at all -- this is why `pip install`
# failed on machines that had a perfectly good toolkit.
# ---------------------------------------------------------------------------
if(NOT DEFINED CMAKE_CUDA_COMPILER AND NOT DEFINED ENV{CUDACXX})
file(GLOB _cuda_roots /usr/local/cuda-*)
find_program(_nvcc nvcc
HINTS ENV CUDA_HOME ENV CUDA_PATH ENV CUDA_ROOT
/usr/local/cuda ${_cuda_roots}
PATH_SUFFIXES bin)
if(_nvcc)
set(CMAKE_CUDA_COMPILER "${_nvcc}")
endif()
endif()

project(culof LANGUAGES CXX CUDA)

set(CMAKE_CXX_STANDARD 17)
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ answers. Changing the import is the whole migration.

## Performance

![Speedup over scikit-learn](img/benchmark_speed.png)
![Speedup over scikit-learn](https://raw.githubusercontent.com/Aminsed/cuLOF/main/img/benchmark_speed.png)

| samples | scikit-learn | cuLOF | speedup |
|--------:|-------------:|------:|--------:|
Expand Down Expand Up @@ -74,7 +74,7 @@ float32 distance matrix would be 149 GiB.

## Accuracy

![Agreement with scikit-learn](img/accuracy.png)
![Agreement with scikit-learn](https://raw.githubusercontent.com/Aminsed/cuLOF/main/img/accuracy.png)

cuLOF computes distances in float32 and scikit-learn in float64, so agreement is
measured rather than assumed. At n = 20,000, d = 8, k = 20: **median relative
Expand All @@ -89,7 +89,7 @@ which points were flagged.
[`test_near_ties_are_the_only_disagreement`](tests/python/test_parity.py) asserts
exactly this.

![Detection on three datasets](img/detection.png)
![Detection on three datasets](https://raw.githubusercontent.com/Aminsed/cuLOF/main/img/detection.png)

## Install

Expand Down
3 changes: 2 additions & 1 deletion src/culof/_version.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Single source of truth for the package version.

pyproject.toml reads it via `tool.setuptools.dynamic`, and `culof.__version__`
pyproject.toml reads it back through the scikit-build-core regex
metadata provider, and `culof.__version__`
re-exports it, so there is exactly one place to bump.
"""

Expand Down