Skip to content

Commit

Permalink
start adding libraft wheels
Browse files Browse the repository at this point in the history
  • Loading branch information
jameslamb committed Dec 16, 2024
1 parent 3720d8e commit 24a4db8
Show file tree
Hide file tree
Showing 10 changed files with 315 additions and 45 deletions.
4 changes: 1 addition & 3 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -349,9 +349,7 @@ fi

# Append `-DFIND_RAFT_CPP=ON` to EXTRA_CMAKE_ARGS unless a user specified the option.
SKBUILD_EXTRA_CMAKE_ARGS="${EXTRA_CMAKE_ARGS}"
if [[ "${EXTRA_CMAKE_ARGS}" != *"DFIND_RAFT_CPP"* ]]; then
SKBUILD_EXTRA_CMAKE_ARGS="${SKBUILD_EXTRA_CMAKE_ARGS} -DFIND_RAFT_CPP=ON"
fi

# Replace spaces with semicolons in SKBUILD_EXTRA_CMAKE_ARGS
SKBUILD_EXTRA_CMAKE_ARGS=$(echo ${SKBUILD_EXTRA_CMAKE_ARGS} | sed 's/ /;/g')

Expand Down
63 changes: 63 additions & 0 deletions python/libraft/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# =============================================================================
# Copyright (c) 2024, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
# or implied. See the License for the specific language governing permissions and limitations under
# the License.
# =============================================================================

cmake_minimum_required(VERSION 3.26.4 FATAL_ERROR)

include(../../rapids_config.cmake)

include(rapids-cuda)
rapids_cuda_init_architectures(libraft-python)

project(
libraft-python
VERSION "${RAPIDS_VERSION}"
LANGUAGES CXX CUDA
)

option(USE_CUDA_MATH_WHEELS "Use the CUDA math wheels instead of the system libraries" OFF)

# Check if raft is already available. If so, it is the user's responsibility to ensure that the
# CMake package is also available at build time of the Python raft package.
find_package(raft "${RAPIDS_VERSION}")

if(raft_FOUND)
return()
endif()

unset(raft_FOUND)

set(BUILD_TESTS OFF)
set(BUILD_PRIMS_BENCH OFF)
set(RAFT_COMPILE_LIBRARY ON)
set(CUDA_STATIC_RUNTIME ON)
set(CUDA_STATIC_MATH_LIBRARIES ON)
if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 12.0)
set(CUDA_STATIC_MATH_LIBRARIES OFF)
elseif(USE_CUDA_MATH_WHEELS)
message(FATAL_ERROR "Cannot use CUDA math wheels with CUDA < 12.0")
endif()

add_subdirectory(../../cpp raft-cpp)

if(NOT CUDA_STATIC_MATH_LIBRARIES AND USE_CUDA_MATH_WHEELS)
set_property(
TARGET raft_lib
PROPERTY INSTALL_RPATH
"$ORIGIN/../nvidia/cublas/lib"
"$ORIGIN/../nvidia/curand/lib"
"$ORIGIN/../nvidia/cusolver/lib"
"$ORIGIN/../nvidia/cusparse/lib"
"$ORIGIN/../nvidia/nvjitlink/lib"
)
endif()
1 change: 1 addition & 0 deletions python/libraft/LICENSE
1 change: 1 addition & 0 deletions python/libraft/README.md
1 change: 1 addition & 0 deletions python/libraft/libraft/VERSION
16 changes: 16 additions & 0 deletions python/libraft/libraft/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright (c) 2024, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from libraft._version import __git_commit__, __version__
from libraft.load import load_library
30 changes: 30 additions & 0 deletions python/libraft/libraft/_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright (c) 2023-2024, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import importlib.resources

__version__ = (
importlib.resources.files(__package__).joinpath("VERSION").read_text().strip()
)
try:
__git_commit__ = (
importlib.resources.files(__package__)
.joinpath("GIT_COMMIT")
.read_text()
.strip()
)
except FileNotFoundError:
__git_commit__ = ""

__all__ = ["__git_commit__", "__version__"]
77 changes: 77 additions & 0 deletions python/libraft/libraft/load.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Copyright (c) 2024, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import ctypes
import os

# Loading with RTLD_LOCAL adds the library itself to the loader's
# loaded library cache without loading any symbols into the global
# namespace. This allows libraries that express a dependency on
# this library to be loaded later and successfully satisfy this dependency
# without polluting the global symbol table with symbols from
# libraft that could conflict with symbols from other DSOs.
PREFERRED_LOAD_FLAG = ctypes.RTLD_LOCAL


def _load_system_installation(soname: str):
"""Try to dlopen() the library indicated by ``soname``
Raises ``OSError`` if library cannot be loaded.
"""
return ctypes.CDLL(soname, PREFERRED_LOAD_FLAG)


def _load_wheel_installation(soname: str):
"""Try to dlopen() the library indicated by ``soname``
Returns ``None`` if the library cannot be loaded.
"""
if os.path.isfile(lib := os.path.join(os.path.dirname(__file__), "lib64", soname)):
return ctypes.CDLL(lib, PREFERRED_LOAD_FLAG)
return None


def load_library():
"""Dynamically load libcugraph.so and its dependencies"""
prefer_system_installation = (
os.getenv("RAPIDS_LIBRAFT_PREFER_SYSTEM_LIBRARY", "false").lower() != "false"
)

soname = "libraft.so"
libraft_lib = None
if prefer_system_installation:
# Prefer a system library if one is present to
# avoid clobbering symbols that other packages might expect, but if no
# other library is present use the one in the wheel.
try:
libraft_lib = _load_system_installation(soname)
except OSError:
libraft_lib = _load_wheel_installation(soname)
else:
# Prefer the libraries bundled in this package. If they aren't found
# (which might be the case in builds where the library was prebuilt before
# packaging the wheel), look for a system installation.
try:
libraft_lib = _load_wheel_installation(soname)
if libraft_lib is None:
libraft_lib = _load_system_installation(soname)
except OSError:
# If none of the searches above succeed, just silently return None
# and rely on other mechanisms (like RPATHs on other DSOs) to
# help the loader find the library.
pass

# The caller almost never needs to do anything with this library, but no
# harm in offering the option since this object at least provides a handle
# to inspect where libcugraph was loaded from.
return libraft_lib
125 changes: 125 additions & 0 deletions python/libraft/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Copyright (c) 2022, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

[build-system]

requires = [
"rapids-build-backend>=0.3.0,<0.4.0.dev0",
"scikit-build-core[pyproject]>=0.10.0",
] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`.
build-backend = "rapids_build_backend.build"

[project]
name = "libraft"
dynamic = ["version"]
description = "RAFT: Reusable Algorithms Functions and other Tools (C++)"
readme = { file = "README.md", content-type = "text/markdown" }
authors = [
{ name = "NVIDIA Corporation" },
]
license = { text = "Apache 2.0" }
requires-python = ">=3.10"
dependencies = [
"cuda-python",
"numpy>=1.23,<3.0a0",
"nvidia-cublas",
"nvidia-curand",
"nvidia-cusolver",
"nvidia-cusparse",
"rmm==25.2.*,>=0.0.0a0",
] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`.
classifiers = [
"Intended Audience :: Developers",
]

[project.optional-dependencies]
test = [
"cupy-cuda11x>=12.0.0",
"pytest-cov",
"pytest==7.*",
"scikit-learn",
"scipy",
] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`.

[project.urls]
Homepage = "https://github.com/rapidsai/raft"
Documentation = "https://docs.rapids.ai/api/raft/stable/"

[tool.isort]
line_length = 79
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
combine_as_imports = true
order_by_type = true
known_first_party = [
"libraft",
]
default_section = "THIRDPARTY"
sections = [
"FUTURE",
"STDLIB",
"THIRDPARTY",
"DASK",
"RAPIDS",
"FIRSTPARTY",
"LOCALFOLDER",
]
skip = [
"thirdparty",
".eggs",
".git",
".hg",
".mypy_cache",
".tox",
".venv",
"_build",
"buck-out",
"build",
"dist",
"__init__.py",
]

[tool.scikit-build]
build-dir = "build/{wheel_tag}"
cmake.build-type = "Release"
cmake.version = "CMakeLists.txt"
minimum-version = "build-system.requires"
ninja.make-fallback = true
sdist.exclude = ["*tests*"]
sdist.reproducible = true
wheel.packages = ["libraft"]

[tool.scikit-build.metadata.version]
provider = "scikit_build_core.metadata.regex"
input = "libraft/VERSION"
regex = "(?P<value>.*)"

[tool.rapids-build-backend]
build-backend = "scikit_build_core.build"
requires = [
"cmake>=3.26.4,!=3.30.0",
"cuda-python",
"cython>=3.0.0,<3.1.0a0",
"ninja",
"rmm==25.2.*,>=0.0.0a0",
] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`.
dependencies-file = "../../dependencies.yaml"
matrix-entry = "cuda_suffixed=true;use_cuda_wheels=true"

[tool.pydistcheck]
select = [
# NOTE: size threshold is managed via CLI args in CI scripts
"distro-too-large-compressed",
]
42 changes: 0 additions & 42 deletions python/pylibraft/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ project(
LANGUAGES CXX CUDA
)

option(FIND_RAFT_CPP "Search for existing RAFT C++ installations before defaulting to local files"
ON
)
option(USE_CUDA_MATH_WHEELS "Use the CUDA math wheels instead of the system libraries" OFF)

# If the user requested it we attempt to find RAFT.
Expand All @@ -48,47 +45,8 @@ endif()

include(rapids-cython-core)

if(NOT raft_FOUND)
find_package(CUDAToolkit REQUIRED)

set(BUILD_TESTS OFF)
set(BUILD_PRIMS_BENCH OFF)
set(RAFT_COMPILE_LIBRARY ON)
set(CUDA_STATIC_RUNTIME ON)
set(CUDA_STATIC_MATH_LIBRARIES ON)
if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 12.0)
set(CUDA_STATIC_MATH_LIBRARIES OFF)
elseif(USE_CUDA_MATH_WHEELS)
message(FATAL_ERROR "Cannot use CUDA math wheels with CUDA < 12.0")
endif()

add_subdirectory(../../cpp raft-cpp EXCLUDE_FROM_ALL)

if(NOT CUDA_STATIC_MATH_LIBRARIES AND USE_CUDA_MATH_WHEELS)
set_property(
TARGET raft_lib
PROPERTY INSTALL_RPATH
"$ORIGIN/../nvidia/cublas/lib"
"$ORIGIN/../nvidia/curand/lib"
"$ORIGIN/../nvidia/cusolver/lib"
"$ORIGIN/../nvidia/cusparse/lib"
"$ORIGIN/../nvidia/nvjitlink/lib"
)
endif()

# When building the C++ libraries from source we must copy libraft.so alongside the
# pairwise_distance and random Cython libraries TODO: when we have a single 'compiled' raft
# library, we shouldn't need this
set(cython_lib_dir pylibraft)
install(TARGETS raft_lib DESTINATION ${cython_lib_dir})
endif()

rapids_cython_init()

add_subdirectory(pylibraft/common)
add_subdirectory(pylibraft/random)
add_subdirectory(pylibraft/sparse)

if(DEFINED cython_lib_dir)
rapids_cython_add_rpath_entries(TARGET raft PATHS "${cython_lib_dir}")
endif()

0 comments on commit 24a4db8

Please sign in to comment.