From 5cfea53777ed2232ad274af5b74bf014bdec9ff8 Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Wed, 6 Mar 2024 18:33:06 -0600 Subject: [PATCH] Fix `louvain_communities`, `PropertyGraph`, cudf `column.full`, dgl 2.1.0 CI failures (#4215) * Updates cudf usage in hypergraph to use `cudf.core.column.as_column` instead of the deprecated `cudf.core.column.full` API * Adds code to call louvain using a signature that's compatible with both pre and post NX 3.3 * Adds an upper bound to the DGL version pin to ensure a compatible version is used * Updates a PropertyGraph test to handle minor ordering changes in the result DataFrame * Test matrices starting 24.04 have changed, resulting in an "empty matrix" error when attempting to use the supported combination used for PyG (PyG wheel tests need pytorch with CUDA 12.2 on arm64), so the matrix was simplified to remove the CUDA 11.8 requirement. cc @tingyu66 Authors: - Erik Welch (https://github.com/eriknw) - Naim (https://github.com/naimnv) Approvers: - Rick Ratzel (https://github.com/rlratzel) - Ray Douglass (https://github.com/raydouglass) URL: https://github.com/rapidsai/cugraph/pull/4215 --- .github/workflows/pr.yaml | 2 +- .github/workflows/test.yaml | 2 +- ci/test_python.sh | 2 +- ci/test_wheel_cugraph-dgl.sh | 2 +- .../cugraph/cugraph/structure/hypergraph.py | 4 +- .../tests/data_store/test_property_graph.py | 6 +- python/nx-cugraph/_nx_cugraph/__init__.py | 1 + .../algorithms/community/louvain.py | 93 ++++++++++++++----- .../nx_cugraph/tests/test_match_api.py | 5 - 9 files changed, 80 insertions(+), 37 deletions(-) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 7753f52d799..7f0b95e3573 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -162,7 +162,7 @@ jobs: with: build_type: pull-request script: ci/test_wheel_cugraph-pyg.sh - matrix_filter: map(select(.ARCH == "amd64" and .CUDA_VER == "11.8.0")) + matrix_filter: map(select(.ARCH == "amd64")) wheel-build-cugraph-equivariant: secrets: inherit uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-24.04 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index f6a04fcc776..32fb2d62b29 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -76,7 +76,7 @@ jobs: date: ${{ inputs.date }} sha: ${{ inputs.sha }} script: ci/test_wheel_cugraph-pyg.sh - matrix_filter: map(select(.ARCH == "amd64" and .CUDA_VER == "11.8.0")) + matrix_filter: map(select(.ARCH == "amd64")) wheel-tests-cugraph-equivariant: secrets: inherit uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-24.04 diff --git a/ci/test_python.sh b/ci/test_python.sh index 9fa1de2e5e7..e05160239ab 100755 --- a/ci/test_python.sh +++ b/ci/test_python.sh @@ -166,7 +166,7 @@ if [[ "${RAPIDS_CUDA_VERSION}" == "11.8.0" ]]; then pylibcugraphops \ cugraph \ cugraph-dgl \ - 'dgl>=1.1.0.cu*' \ + 'dgl>=1.1.0.cu*,<=2.0.0.cu*' \ 'pytorch>=2.0' \ 'pytorch-cuda>=11.8' diff --git a/ci/test_wheel_cugraph-dgl.sh b/ci/test_wheel_cugraph-dgl.sh index 9ecaa75a86b..367b169bd13 100755 --- a/ci/test_wheel_cugraph-dgl.sh +++ b/ci/test_wheel_cugraph-dgl.sh @@ -34,6 +34,6 @@ DGL_URL="https://data.dgl.ai/wheels/cu${PYTORCH_CUDA_VER}/repo.html" rapids-logger "Installing PyTorch and DGL" rapids-retry python -m pip install torch --index-url ${PYTORCH_URL} -rapids-retry python -m pip install dgl --find-links ${DGL_URL} +rapids-retry python -m pip install dgl==2.0.0 --find-links ${DGL_URL} python -m pytest python/cugraph-dgl/tests diff --git a/python/cugraph/cugraph/structure/hypergraph.py b/python/cugraph/cugraph/structure/hypergraph.py index 4e9975e6b8a..4add74d6061 100644 --- a/python/cugraph/cugraph/structure/hypergraph.py +++ b/python/cugraph/cugraph/structure/hypergraph.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020-2023, NVIDIA CORPORATION. +# Copyright (c) 2020-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 @@ -581,7 +581,7 @@ def _create_direct_edges( def _str_scalar_to_category(size, val): return cudf.core.column.build_categorical_column( categories=cudf.core.column.as_column([val], dtype="str"), - codes=cudf.core.column.column.full(size, 0, dtype=np.int32), + codes=cudf.core.column.as_column(0, length=size, dtype=np.int32), mask=None, size=size, offset=0, diff --git a/python/cugraph/cugraph/tests/data_store/test_property_graph.py b/python/cugraph/cugraph/tests/data_store/test_property_graph.py index a33d4f753db..da5608e0193 100644 --- a/python/cugraph/cugraph/tests/data_store/test_property_graph.py +++ b/python/cugraph/cugraph/tests/data_store/test_property_graph.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2023, NVIDIA CORPORATION. +# Copyright (c) 2021-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 @@ -1424,6 +1424,10 @@ def test_extract_subgraph_graph_without_vert_props(as_pg_first): actual_edgelist = G.edgelist.edgelist_df assert G.is_directed() + expected_edgelist = expected_edgelist.sort_values( + by=["src", "dst"], ignore_index=True + ) + actual_edgelist = actual_edgelist.sort_values(by=["src", "dst"], ignore_index=True) assert_frame_equal(expected_edgelist, actual_edgelist, check_like=True) diff --git a/python/nx-cugraph/_nx_cugraph/__init__.py b/python/nx-cugraph/_nx_cugraph/__init__.py index c6c43110ac6..b2f13d25ff3 100644 --- a/python/nx-cugraph/_nx_cugraph/__init__.py +++ b/python/nx-cugraph/_nx_cugraph/__init__.py @@ -164,6 +164,7 @@ }, "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.", + "max_level : int, optional": "Upper limit of the number of macro-iterations (max: 500).", }, "pagerank": { "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/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py b/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py index f7638f47aad..ea1318060e0 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py @@ -37,18 +37,23 @@ _max_level_param = {} -@not_implemented_for("directed") -@networkx_algorithm( - extra_params={ - **_max_level_param, - **_dtype_param, - }, - is_incomplete=True, # seed not supported; self-loops not supported - is_different=True, # RNG different - version_added="23.10", - _plc="louvain", -) -def louvain_communities( +def _louvain_communities_nx32( + G, + weight="weight", + resolution=1, + threshold=0.0000001, + seed=None, + *, + max_level=None, + dtype=None, +): + """`seed` parameter is currently ignored, and self-loops are not yet supported.""" + return _louvain_communities( + G, weight, resolution, threshold, max_level, seed, dtype=dtype + ) + + +def _louvain_communities( G, weight="weight", resolution=1, @@ -85,16 +90,54 @@ def louvain_communities( return [set(G._nodearray_to_list(ids)) for ids in groups.values()] -@louvain_communities._can_run -def _( - G, - weight="weight", - resolution=1, - threshold=0.0000001, - max_level=None, - seed=None, - *, - dtype=None, -): - # NetworkX allows both directed and undirected, but cugraph only allows undirected. - return not G.is_directed() +_louvain_decorator = networkx_algorithm( + extra_params={ + **_max_level_param, + **_dtype_param, + }, + is_incomplete=True, # seed not supported; self-loops not supported + is_different=True, # RNG different + version_added="23.10", + _plc="louvain", + name="louvain_communities", +) + +if _max_level_param: # networkx <= 3.2 + _louvain_communities_nx32.__name__ = "louvain_communities" + louvain_communities = not_implemented_for("directed")( + _louvain_decorator(_louvain_communities_nx32) + ) + + @louvain_communities._can_run + def _( + G, + weight="weight", + resolution=1, + threshold=0.0000001, + seed=None, + *, + max_level=None, + dtype=None, + ): + # NetworkX allows both directed and undirected, but cugraph only undirected. + return not G.is_directed() + +else: # networkx >= 3.3 + _louvain_communities.__name__ = "louvain_communities" + louvain_communities = not_implemented_for("directed")( + _louvain_decorator(_louvain_communities) + ) + + @louvain_communities._can_run + def _( + G, + weight="weight", + resolution=1, + threshold=0.0000001, + max_level=None, + seed=None, + *, + dtype=None, + ): + # NetworkX allows both directed and undirected, but cugraph only undirected. + return not G.is_directed() diff --git a/python/nx-cugraph/nx_cugraph/tests/test_match_api.py b/python/nx-cugraph/nx_cugraph/tests/test_match_api.py index 595f7819ac1..bd6f20e84f0 100644 --- a/python/nx-cugraph/nx_cugraph/tests/test_match_api.py +++ b/python/nx-cugraph/nx_cugraph/tests/test_match_api.py @@ -44,11 +44,6 @@ def test_match_signature_and_names(): else: orig_func = dispatchable_func.orig_func - if nxver.major == 3 and nxver.minor <= 2 and name == "louvain_communities": - # The signature of louvain_communities changed in NetworkX 3.3, and - # we updated to match, so we skip this check in older versions. - continue - # Matching signatures? orig_sig = inspect.signature(orig_func) func_sig = inspect.signature(func)