From 6daad78fe0317bd95223c4af2b0fd981c26158cb Mon Sep 17 00:00:00 2001 From: James Lamb Date: Fri, 24 Jan 2025 08:49:26 -0600 Subject: [PATCH 1/6] update pip devcontainers to UCX 1.18, other small packaging changes (#65) Contributes to https://github.com/rapidsai/build-planning/issues/138 Updates to using UCX 1.18 in pip devcontainers here. Also updates to the latest version of `rapids-dependency-file-generator` in `pre-commit` config. Authors: - James Lamb (https://github.com/jameslamb) Approvers: - https://github.com/jakirkham - Gil Forsyth (https://github.com/gforsyth) URL: https://github.com/rapidsai/nx-cugraph/pull/65 --- .devcontainer/cuda11.8-pip/devcontainer.json | 2 +- .devcontainer/cuda12.5-pip/devcontainer.json | 2 +- .pre-commit-config.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.devcontainer/cuda11.8-pip/devcontainer.json b/.devcontainer/cuda11.8-pip/devcontainer.json index 05d14095e..7c4a308c8 100644 --- a/.devcontainer/cuda11.8-pip/devcontainer.json +++ b/.devcontainer/cuda11.8-pip/devcontainer.json @@ -5,7 +5,7 @@ "args": { "CUDA": "11.8", "PYTHON_PACKAGE_MANAGER": "pip", - "BASE": "rapidsai/devcontainers:25.02-cpp-cuda11.8-ucx1.17.0-openmpi-ubuntu22.04" + "BASE": "rapidsai/devcontainers:25.02-cpp-cuda11.8-ucx1.18.0-openmpi-ubuntu22.04" } }, "runArgs": [ diff --git a/.devcontainer/cuda12.5-pip/devcontainer.json b/.devcontainer/cuda12.5-pip/devcontainer.json index d909f8b6c..91ba8400c 100644 --- a/.devcontainer/cuda12.5-pip/devcontainer.json +++ b/.devcontainer/cuda12.5-pip/devcontainer.json @@ -5,7 +5,7 @@ "args": { "CUDA": "12.5", "PYTHON_PACKAGE_MANAGER": "pip", - "BASE": "rapidsai/devcontainers:25.02-cpp-cuda12.5-ucx1.17.0-openmpi-ubuntu22.04" + "BASE": "rapidsai/devcontainers:25.02-cpp-cuda12.5-ucx1.18.0-openmpi-ubuntu22.04" } }, "runArgs": [ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 07ea842fd..dce222dd4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -85,7 +85,7 @@ repos: # Don't have strict linting for miscellaneous code args: [--extend-exclude, "benchmarks/,ci/,docs/,notebooks/"] - repo: https://github.com/rapidsai/dependency-file-generator - rev: v1.16.0 + rev: v1.17.0 hooks: - id: rapids-dependency-file-generator args: ["--clean"] From 277b3531b40fb63fae304bee264aac508412fb33 Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Sun, 26 Jan 2025 22:08:48 -0600 Subject: [PATCH 2/6] Add experimental version of `leiden_communities` (#50) This function is not yet defined or implemented in NetworkX, and there is high probability the API may change once added to NetworkX. For now, this is "best effort" and simply mimics PLC leiden. Authors: - Erik Welch (https://github.com/eriknw) - Ralph Liu (https://github.com/nv-rliu) Approvers: - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/nx-cugraph/pull/50 --- _nx_cugraph/__init__.py | 6 ++- nx_cugraph/algorithms/community/__init__.py | 3 +- nx_cugraph/algorithms/community/leiden.py | 52 +++++++++++++++++++++ nx_cugraph/tests/test_leiden.py | 22 +++++++++ 4 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 nx_cugraph/algorithms/community/leiden.py create mode 100644 nx_cugraph/tests/test_leiden.py diff --git a/_nx_cugraph/__init__.py b/_nx_cugraph/__init__.py index a52583d4d..07bfde667 100644 --- a/_nx_cugraph/__init__.py +++ b/_nx_cugraph/__init__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2023-2024, NVIDIA CORPORATION. +# Copyright (c) 2023-2025, 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 @@ -115,6 +115,7 @@ "katz_centrality", "krackhardt_kite_graph", "ladder_graph", + "leiden_communities", "les_miserables_graph", "lollipop_graph", "louvain_communities", @@ -235,6 +236,9 @@ "katz_centrality": { "dtype : dtype or None, optional": "The data type (np.float32, np.float64, or None) to use for the edge weights in the algorithm. If None, then dtype is determined by the edge values.", }, + "leiden_communities": { + "dtype : dtype or None, optional": "The data type (np.float32, np.float64, or None) to use for the edge weights in the algorithm. If None, then dtype is determined by the edge values.", + }, "louvain_communities": { "dtype : dtype or None, optional": "The data type (np.float32, np.float64, or None) to use for the edge weights in the algorithm. If None, then dtype is determined by the edge values.", }, diff --git a/nx_cugraph/algorithms/community/__init__.py b/nx_cugraph/algorithms/community/__init__.py index 51a4f5c19..9515769a6 100644 --- a/nx_cugraph/algorithms/community/__init__.py +++ b/nx_cugraph/algorithms/community/__init__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2023, NVIDIA CORPORATION. +# Copyright (c) 2023-2025, 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 @@ -10,4 +10,5 @@ # 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 .leiden import * from .louvain import * diff --git a/nx_cugraph/algorithms/community/leiden.py b/nx_cugraph/algorithms/community/leiden.py new file mode 100644 index 000000000..4f143754f --- /dev/null +++ b/nx_cugraph/algorithms/community/leiden.py @@ -0,0 +1,52 @@ +# Copyright (c) 2024-2025, 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 numpy as np +import pylibcugraph as plc + +from nx_cugraph.convert import _to_undirected_graph +from nx_cugraph.utils import ( + _dtype_param, + _get_float_dtype, + _groupby, + _seed_to_int, + networkx_algorithm, + not_implemented_for, +) + +__all__ = ["leiden_communities"] + + +@not_implemented_for("directed") +@networkx_algorithm(extra_params=_dtype_param, version_added="25.02", _plc="leiden") +def leiden_communities( + G, weight="weight", resolution=1, max_level=None, seed=None, *, dtype=None +): + # Warning: this API is experimental and may change. It is not yet in NetworkX. + # See: https://github.com/networkx/networkx/pull/7743 + seed = _seed_to_int(seed) + G = _to_undirected_graph(G, weight, 1, np.float32) + dtype = _get_float_dtype(dtype, graph=G, weight=weight) + if max_level is None or max_level < 0: + max_level = 500 + node_ids, clusters, modularity = plc.leiden( + resource_handle=plc.ResourceHandle(), + random_state=seed, + graph=G._get_plc_graph(weight, 1, dtype), + max_level=max_level, + resolution=resolution, + theta=1, # TODO: expose theta as a backend-only parameter once it's used + do_expensive_check=False, + ) + groups = _groupby(clusters, node_ids, groups_are_canonical=True) + return [set(G._nodearray_to_list(ids)) for ids in groups.values()] diff --git a/nx_cugraph/tests/test_leiden.py b/nx_cugraph/tests/test_leiden.py new file mode 100644 index 000000000..beee13f16 --- /dev/null +++ b/nx_cugraph/tests/test_leiden.py @@ -0,0 +1,22 @@ +# Copyright (c) 2024-2025, 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 nx_cugraph as nxcg + + +def test_leiden_karate(): + # Basic smoke test; if something here changes, we want to know! + G = nxcg.karate_club_graph() + leiden = nxcg.community.leiden_communities(G, seed=123) + louvain = nxcg.community.louvain_communities(G, seed=123) + assert leiden == louvain From fab2d8646e5b17ce79f8ae9c202225e29f899152 Mon Sep 17 00:00:00 2001 From: Bradley Dice Date: Tue, 28 Jan 2025 16:32:27 -0600 Subject: [PATCH 3/6] Build and test with CUDA 12.8.0 (#66) This PR uses CUDA 12.8.0 to build and test. xref: https://github.com/rapidsai/build-planning/issues/139 Authors: - Bradley Dice (https://github.com/bdice) Approvers: - James Lamb (https://github.com/jameslamb) URL: https://github.com/rapidsai/nx-cugraph/pull/66 --- .../devcontainer.json | 8 ++++---- .../devcontainer.json | 12 ++++++------ .github/workflows/build.yaml | 8 ++++---- .github/workflows/pr.yaml | 18 +++++++++--------- .github/workflows/test.yaml | 4 ++-- ...6_64.yaml => all_cuda-128_arch-x86_64.yaml} | 4 ++-- dependencies.yaml | 6 +++++- 7 files changed, 32 insertions(+), 28 deletions(-) rename .devcontainer/{cuda12.5-conda => cuda12.8-conda}/devcontainer.json (91%) rename .devcontainer/{cuda12.5-pip => cuda12.8-pip}/devcontainer.json (88%) rename conda/environments/{all_cuda-125_arch-x86_64.yaml => all_cuda-128_arch-x86_64.yaml} (92%) diff --git a/.devcontainer/cuda12.5-conda/devcontainer.json b/.devcontainer/cuda12.8-conda/devcontainer.json similarity index 91% rename from .devcontainer/cuda12.5-conda/devcontainer.json rename to .devcontainer/cuda12.8-conda/devcontainer.json index 66276192d..973b76139 100644 --- a/.devcontainer/cuda12.5-conda/devcontainer.json +++ b/.devcontainer/cuda12.8-conda/devcontainer.json @@ -3,7 +3,7 @@ "context": "${localWorkspaceFolder}/.devcontainer", "dockerfile": "${localWorkspaceFolder}/.devcontainer/Dockerfile", "args": { - "CUDA": "12.5", + "CUDA": "12.8", "PYTHON_PACKAGE_MANAGER": "conda", "BASE": "rapidsai/devcontainers:25.02-cpp-mambaforge-ubuntu22.04" } @@ -11,7 +11,7 @@ "runArgs": [ "--rm", "--name", - "${localEnv:USER:anon}-rapids-${localWorkspaceFolderBasename}-25.02-cuda12.5-conda" + "${localEnv:USER:anon}-rapids-${localWorkspaceFolderBasename}-25.02-cuda12.8-conda" ], "hostRequirements": {"gpu": "optional"}, "features": { @@ -20,7 +20,7 @@ "overrideFeatureInstallOrder": [ "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils" ], - "initializeCommand": ["/bin/bash", "-c", "mkdir -m 0755 -p ${localWorkspaceFolder}/../.{aws,cache,config,conda/pkgs,conda/${localWorkspaceFolderBasename}-cuda12.5-envs}"], + "initializeCommand": ["/bin/bash", "-c", "mkdir -m 0755 -p ${localWorkspaceFolder}/../.{aws,cache,config,conda/pkgs,conda/${localWorkspaceFolderBasename}-cuda12.8-envs}"], "postAttachCommand": ["/bin/bash", "-c", "if [ ${CODESPACES:-false} = 'true' ]; then . devcontainer-utils-post-attach-command; . rapids-post-attach-command; fi"], "workspaceFolder": "/home/coder", "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/nx-cugraph,type=bind,consistency=consistent", @@ -29,7 +29,7 @@ "source=${localWorkspaceFolder}/../.cache,target=/home/coder/.cache,type=bind,consistency=consistent", "source=${localWorkspaceFolder}/../.config,target=/home/coder/.config,type=bind,consistency=consistent", "source=${localWorkspaceFolder}/../.conda/pkgs,target=/home/coder/.conda/pkgs,type=bind,consistency=consistent", - "source=${localWorkspaceFolder}/../.conda/${localWorkspaceFolderBasename}-cuda12.5-envs,target=/home/coder/.conda/envs,type=bind,consistency=consistent" + "source=${localWorkspaceFolder}/../.conda/${localWorkspaceFolderBasename}-cuda12.8-envs,target=/home/coder/.conda/envs,type=bind,consistency=consistent" ], "customizations": { "vscode": { diff --git a/.devcontainer/cuda12.5-pip/devcontainer.json b/.devcontainer/cuda12.8-pip/devcontainer.json similarity index 88% rename from .devcontainer/cuda12.5-pip/devcontainer.json rename to .devcontainer/cuda12.8-pip/devcontainer.json index 91ba8400c..99948d79f 100644 --- a/.devcontainer/cuda12.5-pip/devcontainer.json +++ b/.devcontainer/cuda12.8-pip/devcontainer.json @@ -3,20 +3,20 @@ "context": "${localWorkspaceFolder}/.devcontainer", "dockerfile": "${localWorkspaceFolder}/.devcontainer/Dockerfile", "args": { - "CUDA": "12.5", + "CUDA": "12.8", "PYTHON_PACKAGE_MANAGER": "pip", - "BASE": "rapidsai/devcontainers:25.02-cpp-cuda12.5-ucx1.18.0-openmpi-ubuntu22.04" + "BASE": "rapidsai/devcontainers:25.02-cpp-cuda12.8-ucx1.18.0-openmpi-ubuntu22.04" } }, "runArgs": [ "--rm", "--name", - "${localEnv:USER:anon}-rapids-${localWorkspaceFolderBasename}-25.02-cuda12.5-pip" + "${localEnv:USER:anon}-rapids-${localWorkspaceFolderBasename}-25.02-cuda12.8-pip" ], "hostRequirements": {"gpu": "optional"}, "features": { "ghcr.io/rapidsai/devcontainers/features/cuda:25.2": { - "version": "12.5", + "version": "12.8", "installcuBLAS": true, "installcuSOLVER": true, "installcuRAND": true, @@ -28,7 +28,7 @@ "ghcr.io/rapidsai/devcontainers/features/cuda", "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils" ], - "initializeCommand": ["/bin/bash", "-c", "mkdir -m 0755 -p ${localWorkspaceFolder}/../.{aws,cache,config/pip,local/share/${localWorkspaceFolderBasename}-cuda12.5-venvs}"], + "initializeCommand": ["/bin/bash", "-c", "mkdir -m 0755 -p ${localWorkspaceFolder}/../.{aws,cache,config/pip,local/share/${localWorkspaceFolderBasename}-cuda12.8-venvs}"], "postAttachCommand": ["/bin/bash", "-c", "if [ ${CODESPACES:-false} = 'true' ]; then . devcontainer-utils-post-attach-command; . rapids-post-attach-command; fi"], "workspaceFolder": "/home/coder", "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/nx-cugraph,type=bind,consistency=consistent", @@ -36,7 +36,7 @@ "source=${localWorkspaceFolder}/../.aws,target=/home/coder/.aws,type=bind,consistency=consistent", "source=${localWorkspaceFolder}/../.cache,target=/home/coder/.cache,type=bind,consistency=consistent", "source=${localWorkspaceFolder}/../.config,target=/home/coder/.config,type=bind,consistency=consistent", - "source=${localWorkspaceFolder}/../.local/share/${localWorkspaceFolderBasename}-cuda12.5-venvs,target=/home/coder/.local/share/venvs,type=bind,consistency=consistent" + "source=${localWorkspaceFolder}/../.local/share/${localWorkspaceFolderBasename}-cuda12.8-venvs,target=/home/coder/.local/share/venvs,type=bind,consistency=consistent" ], "customizations": { "vscode": { diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a7c9cfdcf..a131c45f6 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -28,7 +28,7 @@ concurrency: jobs: conda-python-build: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@branch-25.02 + uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@cuda-12.8.0 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -37,7 +37,7 @@ jobs: upload-conda: needs: conda-python-build secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-upload-packages.yaml@branch-25.02 + uses: rapidsai/shared-workflows/.github/workflows/conda-upload-packages.yaml@cuda-12.8.0 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -45,7 +45,7 @@ jobs: sha: ${{ inputs.sha }} wheel-build-nx-cugraph: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-25.02 + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@cuda-12.8.0 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -57,7 +57,7 @@ jobs: wheel-publish-nx-cugraph: needs: wheel-build-nx-cugraph secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@branch-25.02 + uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@cuda-12.8.0 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index d996fe554..6d4510cc3 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -20,13 +20,13 @@ jobs: - wheel-build-nx-cugraph - wheel-tests-nx-cugraph secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/pr-builder.yaml@branch-25.02 + uses: rapidsai/shared-workflows/.github/workflows/pr-builder.yaml@cuda-12.8.0 if: always() with: needs: ${{ toJSON(needs) }} changed-files: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/changed-files.yaml@branch-25.02 + uses: rapidsai/shared-workflows/.github/workflows/changed-files.yaml@cuda-12.8.0 with: files_yaml: | test_notebooks: @@ -42,29 +42,29 @@ jobs: - '!notebooks/**' devcontainer: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/build-in-devcontainer.yaml@branch-25.02 + uses: rapidsai/shared-workflows/.github/workflows/build-in-devcontainer.yaml@cuda-12.8.0 with: arch: '["amd64"]' - cuda: '["12.5"]' + cuda: '["12.8"]' build_command: | sccache -z; build-all --verbose -j$(nproc --ignore=1); sccache -s; checks: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/checks.yaml@branch-25.02 + uses: rapidsai/shared-workflows/.github/workflows/checks.yaml@cuda-12.8.0 with: enable_check_generated_files: false conda-python-build: needs: [checks] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@branch-25.02 + uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@cuda-12.8.0 with: build_type: pull-request conda-python-tests: needs: [conda-python-build, changed-files] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@branch-25.02 + uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@cuda-12.8.0 if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_python with: build_type: pull-request @@ -72,7 +72,7 @@ jobs: wheel-build-nx-cugraph: needs: [checks] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-25.02 + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@cuda-12.8.0 with: build_type: pull-request script: ci/build_wheel_nx-cugraph.sh @@ -81,7 +81,7 @@ jobs: wheel-tests-nx-cugraph: needs: [wheel-build-nx-cugraph, changed-files] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-25.02 + uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@cuda-12.8.0 if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_python with: build_type: pull-request diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index eda3f3abe..150098021 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -16,7 +16,7 @@ on: jobs: conda-python-tests: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@branch-25.02 + uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@cuda-12.8.0 with: build_type: nightly branch: ${{ inputs.branch }} @@ -27,7 +27,7 @@ jobs: run_codecov: false wheel-tests-nx-cugraph: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-25.02 + uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@cuda-12.8.0 with: build_type: nightly branch: ${{ inputs.branch }} diff --git a/conda/environments/all_cuda-125_arch-x86_64.yaml b/conda/environments/all_cuda-128_arch-x86_64.yaml similarity index 92% rename from conda/environments/all_cuda-125_arch-x86_64.yaml rename to conda/environments/all_cuda-128_arch-x86_64.yaml index c7fa6f217..5a69e3336 100644 --- a/conda/environments/all_cuda-125_arch-x86_64.yaml +++ b/conda/environments/all_cuda-128_arch-x86_64.yaml @@ -6,7 +6,7 @@ channels: - conda-forge - nvidia dependencies: -- cuda-version=12.5 +- cuda-version=12.8 - cudf==25.2.*,>=0.0.0a0 - cupy>=12.0.0 - graphviz @@ -33,4 +33,4 @@ dependencies: - sphinx-markdown-tables - sphinxcontrib-websupport - wheel -name: all_cuda-125_arch-x86_64 +name: all_cuda-128_arch-x86_64 diff --git a/dependencies.yaml b/dependencies.yaml index 690f26cfa..58697e475 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -3,7 +3,7 @@ files: all: output: [conda] matrix: - cuda: ["11.8", "12.5"] + cuda: ["11.8", "12.8"] arch: [x86_64] includes: - checks @@ -113,6 +113,10 @@ dependencies: cuda: "12.5" packages: - cuda-version=12.5 + - matrix: + cuda: "12.8" + packages: + - cuda-version=12.8 docs: common: - output_types: [conda] From 8427a4d347ebc40c5e42aa164c4b41c5a715c5ae Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Wed, 29 Jan 2025 01:47:40 -0600 Subject: [PATCH 4/6] Add benchmark for `leiden_communities` (#67) Here are results from a single round of benchmarks for louvain and leiden: ``` Name (time in ms) Min bench_leiden_communities[ds=karate-backend=cugraph-preconverted] 32.3493 bench_louvain_communities[ds=karate-backend=cugraph-preconverted] 15.2782 bench_leiden_communities[ds=netscience-backend=cugraph-preconverted] 83.5688 bench_louvain_communities[ds=netscience-backend=cugraph-preconverted] 34.1273 bench_leiden_communities[ds=email_Eu_core-backend=cugraph-preconverted] 81.3393 bench_louvain_communities[ds=email_Eu_core-backend=cugraph-preconverted] 50.0181 bench_leiden_communities[ds=amazon0302-backend=cugraph-preconverted] 755.0755 bench_louvain_communities[ds=amazon0302-backend=cugraph-preconverted] 939.3094 Name (time in s) Min bench_leiden_communities[ds=cit-patents-backend=cugraph-preconverted] 8.2224 bench_louvain_communities[ds=cit-patents-backend=cugraph-preconverted] 7.7888 bench_leiden_communities[ds=soc-livejournal1-backend=cugraph-preconverted] 16.1031 bench_louvain_communities[ds=soc-livejournal1-backend=cugraph-preconverted] 22.2967 bench_leiden_communities[ds=hollywood-backend=cugraph-preconverted] 22.7196 bench_louvain_communities[ds=hollywood-backend=cugraph-preconverted] 24.3643 bench_leiden_communities[ds=europe_osm-backend=cugraph-preconverted] 39.7309 bench_louvain_communities[ds=europe_osm-backend=cugraph-preconverted] 46.4452 ``` Authors: - Erik Welch (https://github.com/eriknw) Approvers: - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/nx-cugraph/pull/67 --- benchmarks/pytest-based/bench_algos.py | 21 +++++++++++++++++++ .../pytest-based/run-main-benchmarks.sh | 6 +++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/benchmarks/pytest-based/bench_algos.py b/benchmarks/pytest-based/bench_algos.py index 96b849884..877912288 100644 --- a/benchmarks/pytest-based/bench_algos.py +++ b/benchmarks/pytest-based/bench_algos.py @@ -319,6 +319,27 @@ def bench_louvain_communities(benchmark, graph_obj, backend_wrapper): assert type(result) is list +@pytest.mark.skipif("not hasattr(nx.community, 'leiden_communities')") +def bench_leiden_communities(benchmark, graph_obj, backend_wrapper): + G = get_graph_obj_for_benchmark(graph_obj, backend_wrapper) + # DiGraphs are not supported + if G.is_directed(): + G = G.to_undirected() + if G.__networkx_backend__ not in nx.community.leiden_communities.backends: + pytest.skip( + reason=f"leiden_communities not implemented by {G.__networkx_backend__!r}" + ) + return + result = benchmark.pedantic( + target=backend_wrapper(nx.community.leiden_communities), + args=(G,), + rounds=rounds, + iterations=iterations, + warmup_rounds=warmup_rounds, + ) + assert type(result) is list + + def bench_degree_centrality(benchmark, graph_obj, backend_wrapper): G = get_graph_obj_for_benchmark(graph_obj, backend_wrapper) result = benchmark.pedantic( diff --git a/benchmarks/pytest-based/run-main-benchmarks.sh b/benchmarks/pytest-based/run-main-benchmarks.sh index 3059e3d4b..0817bb010 100755 --- a/benchmarks/pytest-based/run-main-benchmarks.sh +++ b/benchmarks/pytest-based/run-main-benchmarks.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright (c) 2024, NVIDIA CORPORATION. +# Copyright (c) 2024-2025, 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 @@ -58,8 +58,8 @@ fi for algo in $algos; do for dataset in $datasets; do - # this script can be used to download benchmarking datasets by name via cugraph.datasets - python get_graph_bench_dataset.py $dataset + # this script can be used to download benchmarking datasets by name via cugraph.datasets + python get_graph_bench_dataset.py $dataset for backend in $backends; do name="${backend}__${algo}__${dataset}" echo "Running: $backend, $dataset, bench_$algo" From 8f2ee66f71d829fee3639e126e47a758ca53c5e5 Mon Sep 17 00:00:00 2001 From: Rick Ratzel <3039903+rlratzel@users.noreply.github.com> Date: Thu, 30 Jan 2025 12:59:55 -0600 Subject: [PATCH 5/6] Adds support for `jaccard_coefficient` (#62) Adds support for `jaccard_coefficient` to nx-cugraph. This includes a test, but relies largely on the existing test coverage provided by NetworkX. The test included here could (should) be submitted to NetworkX though in a separate PR, since it is not covering anything unique to nx-cugraph. A benchmark is also included, with results showing 2-4X speedup. I've seen much, much larger speedup on a different graph (large movie review bipartite graph, showing 966s for NX, 2s for nx-cugraph = ~500X), so I need to investigate further. This investigation need not prevent this PR from being merged now though. ![image](https://github.com/user-attachments/assets/3ceb7d62-50c4-437e-96d2-0ab452dd39d2) Authors: - Rick Ratzel (https://github.com/rlratzel) Approvers: - Ralph Liu (https://github.com/nv-rliu) - Erik Welch (https://github.com/eriknw) URL: https://github.com/rapidsai/nx-cugraph/pull/62 --- README.md | 2 + _nx_cugraph/__init__.py | 1 + benchmarks/pytest-based/bench_algos.py | 35 +++++++++++- nx_cugraph/algorithms/__init__.py | 4 +- nx_cugraph/algorithms/link_prediction.py | 68 ++++++++++++++++++++++++ nx_cugraph/classes/graph.py | 16 +++++- nx_cugraph/tests/test_link_prediction.py | 45 ++++++++++++++++ 7 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 nx_cugraph/algorithms/link_prediction.py create mode 100644 nx_cugraph/tests/test_link_prediction.py diff --git a/README.md b/README.md index d406bbe6c..2afd1e5b3 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,8 @@ Below is the list of algorithms that are currently supported in nx-cugraph. │ └─ hits └─ pagerank_alg └─ pagerank +link_prediction + └─ jaccard_coefficient operators └─ unary ├─ complement diff --git a/_nx_cugraph/__init__.py b/_nx_cugraph/__init__.py index 07bfde667..a34dd0b4b 100644 --- a/_nx_cugraph/__init__.py +++ b/_nx_cugraph/__init__.py @@ -110,6 +110,7 @@ "is_tree", "is_weakly_connected", "isolates", + "jaccard_coefficient", "k_truss", "karate_club_graph", "katz_centrality", diff --git a/benchmarks/pytest-based/bench_algos.py b/benchmarks/pytest-based/bench_algos.py index 877912288..8f5f82938 100644 --- a/benchmarks/pytest-based/bench_algos.py +++ b/benchmarks/pytest-based/bench_algos.py @@ -197,7 +197,11 @@ def get_graph_obj_for_benchmark(graph_obj, backend_wrapper): """ G = graph_obj if backend_wrapper.backend_name == "cugraph-preconverted": - G = nxcg.from_networkx(G, preserve_all_attrs=True) + G = nxcg.from_networkx( + G, + preserve_all_attrs=True, + use_compat_graph=True, + ) return G @@ -919,6 +923,35 @@ def bench_bipartite_BC_n1000_m3000_k100000(benchmark, backend_wrapper): assert type(result) is dict +def bench_jaccard(benchmark, graph_obj, backend_wrapper): + G = get_graph_obj_for_benchmark(graph_obj, backend_wrapper) + + # ebunch is a list of node pairs to limit the jaccard run. + nodes = list(G.nodes) + start = nodes[0] + ebunch = [(start, n) for n in nodes[1:]] + start = nodes[1] + ebunch += [(start, n) for n in nodes[2:]] + start = nodes[2] + ebunch += [(start, n) for n in nodes[3:]] + + # DiGraphs are not supported + if G.is_directed(): + G = G.to_undirected() + + result = benchmark.pedantic( + target=backend_wrapper(nx.jaccard_coefficient, force_unlazy_eval=True), + args=(G,), + kwargs=dict( + ebunch=ebunch, + ), + rounds=rounds, + iterations=iterations, + warmup_rounds=warmup_rounds, + ) + assert type(result) is list + + @pytest.mark.skip(reason="benchmark not implemented") def bench_complete_bipartite_graph(benchmark, graph_obj, backend_wrapper): pass diff --git a/nx_cugraph/algorithms/__init__.py b/nx_cugraph/algorithms/__init__.py index b4a10bcf0..859d3c681 100644 --- a/nx_cugraph/algorithms/__init__.py +++ b/nx_cugraph/algorithms/__init__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2023-2024, NVIDIA CORPORATION. +# Copyright (c) 2023-2025, 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 @@ -17,6 +17,7 @@ community, components, link_analysis, + link_prediction, operators, shortest_paths, traversal, @@ -30,6 +31,7 @@ from .dag import * from .isolate import * from .link_analysis import * +from .link_prediction import * from .operators import * from .reciprocity import * from .shortest_paths import * diff --git a/nx_cugraph/algorithms/link_prediction.py b/nx_cugraph/algorithms/link_prediction.py new file mode 100644 index 000000000..fc198eb65 --- /dev/null +++ b/nx_cugraph/algorithms/link_prediction.py @@ -0,0 +1,68 @@ +# Copyright (c) 2025, 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 cupy as cp +import networkx as nx +import pylibcugraph as plc + +from nx_cugraph.convert import _to_undirected_graph +from nx_cugraph.utils import index_dtype, networkx_algorithm, not_implemented_for + +__all__ = [ + "jaccard_coefficient", +] + + +@not_implemented_for("directed") +@not_implemented_for("multigraph") +@networkx_algorithm(version_added="25.02", _plc="jaccard_coefficients") +def jaccard_coefficient(G, ebunch=None): + G = _to_undirected_graph(G) + + # If ebunch is not specified, create pairs representing all non-edges. + # This can be an extremely large set and is not realistic for large graphs, + # but this is required for NX compatibility. + if ebunch is None: + A = cp.tri(G._N, G._N, dtype=bool) + A[G.src_indices, G.dst_indices] = True + u_indices, v_indices = cp.nonzero(~A) + if u_indices.size == 0: + return iter([]) + u_indices = u_indices.astype(index_dtype) + v_indices = v_indices.astype(index_dtype) + + else: + (u, v) = zip(*ebunch) + try: + # Convert the ebunch lists to cupy arrays for passing to PLC, possibly + # mapping to integers if the Graph was renumbered. + # Allow the Graph renumber lookup (if renumbering was done) to check + # for invalid node IDs in ebunch. + u_indices = G._list_to_nodearray(u) + v_indices = G._list_to_nodearray(v) + except (KeyError, ValueError) as n: + raise nx.NodeNotFound(f"Node {n} not in G.") + + (u, v, p) = plc.jaccard_coefficients( + resource_handle=plc.ResourceHandle(), + graph=G._get_plc_graph(), + first=u_indices, + second=v_indices, + use_weight=False, + do_expensive_check=False, + ) + + u = G._nodearray_to_list(u) + v = G._nodearray_to_list(v) + p = p.tolist() + + return zip(u, v, p) diff --git a/nx_cugraph/classes/graph.py b/nx_cugraph/classes/graph.py index 018636228..f6da5bbce 100644 --- a/nx_cugraph/classes/graph.py +++ b/nx_cugraph/classes/graph.py @@ -1,4 +1,4 @@ -# Copyright (c) 2023-2024, NVIDIA CORPORATION. +# Copyright (c) 2023-2025, 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 @@ -1009,6 +1009,11 @@ def _get_plc_graph( dst_indices = self.dst_indices if switch_indices: src_indices, dst_indices = dst_indices, src_indices + + # FIXME: the SGGraph constructor arg "symmetrize" will perform all + # symmetrization steps required by libcugraph. The edge_array check + # should be kept, but all other code in this `if` block should be + # removed if possible. if symmetrize is not None: if edge_array is not None: raise NotImplementedError( @@ -1147,6 +1152,15 @@ def _nodearray_to_list(self, node_ids: cp.ndarray[IndexValue]) -> list[NodeKey]: def _list_to_nodearray(self, nodes: list[NodeKey]) -> cp.ndarray[IndexValue]: if (key_to_id := self.key_to_id) is not None: nodes = [key_to_id[node] for node in nodes] + else: + N = self._N + for node in nodes: + try: + n = int(node) + except (TypeError, ValueError): + raise KeyError(node) from None + if n != node or n < 0 or n >= N: + raise KeyError(node) return cp.array(nodes, dtype=index_dtype) def _nodearray_to_set(self, node_ids: cp.ndarray[IndexValue]) -> set[NodeKey]: diff --git a/nx_cugraph/tests/test_link_prediction.py b/nx_cugraph/tests/test_link_prediction.py new file mode 100644 index 000000000..b2e0d9085 --- /dev/null +++ b/nx_cugraph/tests/test_link_prediction.py @@ -0,0 +1,45 @@ +# Copyright (c) 2025, 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 collections.abc import Iterable + +import networkx as nx +import pytest + +# The tests in this file cover use cases unique to nx-cugraph. If the coverage +# here is not unique to nx-cugraph, consider moving those tests to the NetworkX +# project. + + +def test_no_nonexistent_edges_no_ebunch(): + """Test no ebunch and G is fully connected + + Ensure function returns iter([]) or equivalent due to no nonexistent edges. + """ + G = nx.complete_graph(5) + result = nx.jaccard_coefficient(G) + assert isinstance(result, Iterable) + assert pytest.raises(StopIteration, next, result) + + +def test_node_not_found_in_ebunch(): + """Test that all nodes in ebunch are valid + + Ensure function raises NodeNotFound for invalid nodes in ebunch. + """ + G = nx.Graph([(0, 1), (1, 2)]) + with pytest.raises(nx.NodeNotFound, match="Node [']*A[']* not in G."): + nx.jaccard_coefficient(G, [("A", 1)]) + with pytest.raises(nx.NodeNotFound, match=r"Node \(1,\) not in G."): + nx.jaccard_coefficient(G, [(0, (1,))]) + with pytest.raises(nx.NodeNotFound, match="Node 9999 not in G."): + nx.jaccard_coefficient(G, [(0, 9999)]) From edd96f010ec93bf0b9999a5c09d60532f0a54eb7 Mon Sep 17 00:00:00 2001 From: Vyas Ramasubramani Date: Fri, 31 Jan 2025 03:58:51 -0800 Subject: [PATCH 6/6] Revert CUDA 12.8 shared workflow branch changes (#74) This PR points the shared workflow branches back to the default 25.02 branches. xref: https://github.com/rapidsai/build-planning/issues/139 Authors: - Vyas Ramasubramani (https://github.com/vyasr) Approvers: - Bradley Dice (https://github.com/bdice) URL: https://github.com/rapidsai/nx-cugraph/pull/74 --- .github/workflows/build.yaml | 8 ++++---- .github/workflows/pr.yaml | 16 ++++++++-------- .github/workflows/test.yaml | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a131c45f6..a7c9cfdcf 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -28,7 +28,7 @@ concurrency: jobs: conda-python-build: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@cuda-12.8.0 + uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@branch-25.02 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -37,7 +37,7 @@ jobs: upload-conda: needs: conda-python-build secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-upload-packages.yaml@cuda-12.8.0 + uses: rapidsai/shared-workflows/.github/workflows/conda-upload-packages.yaml@branch-25.02 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -45,7 +45,7 @@ jobs: sha: ${{ inputs.sha }} wheel-build-nx-cugraph: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@cuda-12.8.0 + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-25.02 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -57,7 +57,7 @@ jobs: wheel-publish-nx-cugraph: needs: wheel-build-nx-cugraph secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@cuda-12.8.0 + uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@branch-25.02 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 6d4510cc3..87438cf84 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -20,13 +20,13 @@ jobs: - wheel-build-nx-cugraph - wheel-tests-nx-cugraph secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/pr-builder.yaml@cuda-12.8.0 + uses: rapidsai/shared-workflows/.github/workflows/pr-builder.yaml@branch-25.02 if: always() with: needs: ${{ toJSON(needs) }} changed-files: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/changed-files.yaml@cuda-12.8.0 + uses: rapidsai/shared-workflows/.github/workflows/changed-files.yaml@branch-25.02 with: files_yaml: | test_notebooks: @@ -42,7 +42,7 @@ jobs: - '!notebooks/**' devcontainer: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/build-in-devcontainer.yaml@cuda-12.8.0 + uses: rapidsai/shared-workflows/.github/workflows/build-in-devcontainer.yaml@branch-25.02 with: arch: '["amd64"]' cuda: '["12.8"]' @@ -52,19 +52,19 @@ jobs: sccache -s; checks: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/checks.yaml@cuda-12.8.0 + uses: rapidsai/shared-workflows/.github/workflows/checks.yaml@branch-25.02 with: enable_check_generated_files: false conda-python-build: needs: [checks] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@cuda-12.8.0 + uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@branch-25.02 with: build_type: pull-request conda-python-tests: needs: [conda-python-build, changed-files] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@cuda-12.8.0 + uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@branch-25.02 if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_python with: build_type: pull-request @@ -72,7 +72,7 @@ jobs: wheel-build-nx-cugraph: needs: [checks] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@cuda-12.8.0 + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-25.02 with: build_type: pull-request script: ci/build_wheel_nx-cugraph.sh @@ -81,7 +81,7 @@ jobs: wheel-tests-nx-cugraph: needs: [wheel-build-nx-cugraph, changed-files] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@cuda-12.8.0 + uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-25.02 if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_python with: build_type: pull-request diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 150098021..eda3f3abe 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -16,7 +16,7 @@ on: jobs: conda-python-tests: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@cuda-12.8.0 + uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@branch-25.02 with: build_type: nightly branch: ${{ inputs.branch }} @@ -27,7 +27,7 @@ jobs: run_codecov: false wheel-tests-nx-cugraph: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@cuda-12.8.0 + uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-25.02 with: build_type: nightly branch: ${{ inputs.branch }}