Skip to content

Commit

Permalink
Merge branch 'branch-24.02' into fix-explicit-pool-size
Browse files Browse the repository at this point in the history
  • Loading branch information
harrism authored Jan 17, 2024
2 parents de1de21 + 8672534 commit 3750d54
Show file tree
Hide file tree
Showing 12 changed files with 263 additions and 76 deletions.
16 changes: 11 additions & 5 deletions python/nx-cugraph/_nx_cugraph/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2023, NVIDIA CORPORATION.
# 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
Expand Down Expand Up @@ -70,6 +70,8 @@
"in_degree_centrality",
"is_connected",
"is_isolate",
"is_strongly_connected",
"is_weakly_connected",
"isolates",
"k_truss",
"karate_club_graph",
Expand All @@ -85,6 +87,8 @@
"number_connected_components",
"number_of_isolates",
"number_of_selfloops",
"number_strongly_connected_components",
"number_weakly_connected_components",
"octahedral_graph",
"out_degree_centrality",
"pagerank",
Expand All @@ -95,33 +99,35 @@
"single_source_shortest_path_length",
"single_target_shortest_path_length",
"star_graph",
"strongly_connected_components",
"tadpole_graph",
"tetrahedral_graph",
"trivial_graph",
"truncated_cube_graph",
"truncated_tetrahedron_graph",
"turan_graph",
"tutte_graph",
"weakly_connected_components",
"wheel_graph",
# END: functions
},
"extra_docstrings": {
# BEGIN: extra_docstrings
"betweenness_centrality": "`weight` parameter is not yet supported.",
"betweenness_centrality": "`weight` parameter is not yet supported, and RNG with seed may be different.",
"bfs_edges": "`sort_neighbors` parameter is not yet supported.",
"bfs_predecessors": "`sort_neighbors` parameter is not yet supported.",
"bfs_successors": "`sort_neighbors` parameter is not yet supported.",
"bfs_tree": "`sort_neighbors` parameter is not yet supported.",
"edge_betweenness_centrality": "`weight` parameter is not yet supported.",
"edge_betweenness_centrality": "`weight` parameter is not yet supported, and RNG with seed may be different.",
"eigenvector_centrality": "`nstart` parameter is not used, but it is checked for validity.",
"from_pandas_edgelist": "cudf.DataFrame inputs also supported.",
"from_pandas_edgelist": "cudf.DataFrame inputs also supported; value columns with str is unsuppported.",
"generic_bfs_edges": "`neighbors` and `sort_neighbors` parameters are not yet supported.",
"k_truss": (
"Currently raises `NotImplementedError` for graphs with more than one connected\n"
"component when k >= 3. We expect to fix this soon."
),
"katz_centrality": "`nstart` isn't used (but is checked), and `normalized=False` is not supported.",
"louvain_communities": "`seed` parameter is currently ignored.",
"louvain_communities": "`seed` parameter is currently ignored, and self-loops are not yet supported.",
"pagerank": "`dangling` parameter is not supported, but it is checked for validity.",
# END: extra_docstrings
},
Expand Down
6 changes: 2 additions & 4 deletions python/nx-cugraph/nx_cugraph/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2023, NVIDIA CORPORATION.
# 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.
Expand All @@ -11,12 +11,10 @@
# 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

# Read VERSION file from the module that is symlinked to VERSION file
# in the root of the repo at build time or copied to the moudle at
# in the root of the repo at build time or copied to the module at
# installation. VERSION is a separate file that allows CI build-time scripts
# to update version info (including commit hashes) without modifying
# source files.
Expand Down
12 changes: 1 addition & 11 deletions python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
not_implemented_for,
)

from ..isolate import _isolates

__all__ = ["louvain_communities"]


Expand Down Expand Up @@ -56,7 +54,6 @@ def louvain_communities(
seed = _seed_to_int(seed) # Unused, but ensure it's valid for future compatibility
G = _to_undirected_graph(G, weight)
if G.src_indices.size == 0:
# TODO: PLC doesn't handle empty graphs gracefully!
return [{key} for key in G._nodeiter_to_iter(range(len(G)))]
if max_level is None:
max_level = 500
Expand All @@ -76,14 +73,7 @@ def louvain_communities(
do_expensive_check=False,
)
groups = _groupby(clusters, node_ids, groups_are_canonical=True)
rv = [set(G._nodearray_to_list(ids)) for ids in groups.values()]
# TODO: PLC doesn't handle isolated node_ids yet, so this is a temporary fix
isolates = _isolates(G)
if isolates.size > 0:
isolates = isolates[isolates > node_ids.max()]
if isolates.size > 0:
rv.extend({node} for node in G._nodearray_to_list(isolates))
return rv
return [set(G._nodearray_to_list(ids)) for ids in groups.values()]


@louvain_communities._can_run
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2023, NVIDIA CORPORATION.
# 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
Expand All @@ -11,3 +11,5 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from .connected import *
from .strongly_connected import *
from .weakly_connected import *
82 changes: 39 additions & 43 deletions python/nx-cugraph/nx_cugraph/algorithms/components/connected.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,13 @@
# 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 itertools

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 _groupby, networkx_algorithm, not_implemented_for

from ..isolate import _isolates

__all__ = [
"number_connected_components",
"connected_components",
Expand All @@ -32,19 +28,23 @@
@not_implemented_for("directed")
@networkx_algorithm(plc="weakly_connected_components", version_added="23.12")
def number_connected_components(G):
return sum(1 for _ in connected_components(G))
# PREFERRED IMPLEMENTATION, BUT PLC DOES NOT HANDLE ISOLATED VERTICES WELL
# G = _to_undirected_graph(G)
# unused_node_ids, labels = plc.weakly_connected_components(
# resource_handle=plc.ResourceHandle(),
# graph=G._get_plc_graph(),
# offsets=None,
# indices=None,
# weights=None,
# labels=None,
# do_expensive_check=False,
# )
# return cp.unique(labels).size
G = _to_undirected_graph(G)
return _number_connected_components(G)


def _number_connected_components(G, symmetrize=None):
if G.src_indices.size == 0:
return len(G)
unused_node_ids, labels = plc.weakly_connected_components(
resource_handle=plc.ResourceHandle(),
graph=G._get_plc_graph(symmetrize=symmetrize),
offsets=None,
indices=None,
weights=None,
labels=None,
do_expensive_check=False,
)
return cp.unique(labels).size


@number_connected_components._can_run
Expand All @@ -60,53 +60,49 @@ def _(G):
@networkx_algorithm(plc="weakly_connected_components", version_added="23.12")
def connected_components(G):
G = _to_undirected_graph(G)
return _connected_components(G)


def _connected_components(G, symmetrize=None):
if G.src_indices.size == 0:
# TODO: PLC doesn't handle empty graphs (or isolated nodes) gracefully!
return [{key} for key in G._nodeiter_to_iter(range(len(G)))]
node_ids, labels = plc.weakly_connected_components(
resource_handle=plc.ResourceHandle(),
graph=G._get_plc_graph(),
graph=G._get_plc_graph(symmetrize=symmetrize),
offsets=None,
indices=None,
weights=None,
labels=None,
do_expensive_check=False,
)
groups = _groupby(labels, node_ids)
it = (G._nodearray_to_set(connected_ids) for connected_ids in groups.values())
# TODO: PLC doesn't handle isolated vertices yet, so this is a temporary fix
isolates = _isolates(G)
if isolates.size > 0:
isolates = isolates[isolates > node_ids.max()]
if isolates.size > 0:
it = itertools.chain(
it, ({node} for node in G._nodearray_to_list(isolates))
)
return it
return (G._nodearray_to_set(connected_ids) for connected_ids in groups.values())


@not_implemented_for("directed")
@networkx_algorithm(plc="weakly_connected_components", version_added="23.12")
def is_connected(G):
G = _to_undirected_graph(G)
return _is_connected(G)


def _is_connected(G, symmetrize=None):
if len(G) == 0:
raise nx.NetworkXPointlessConcept(
"Connectivity is undefined for the null graph."
)
for community in connected_components(G):
return len(community) == len(G)
raise RuntimeError # pragma: no cover
# PREFERRED IMPLEMENTATION, BUT PLC DOES NOT HANDLE ISOLATED VERTICES WELL
# unused_node_ids, labels = plc.weakly_connected_components(
# resource_handle=plc.ResourceHandle(),
# graph=G._get_plc_graph(),
# offsets=None,
# indices=None,
# weights=None,
# labels=None,
# do_expensive_check=False,
# )
# return labels.size == len(G) and cp.unique(labels).size == 1
if G.src_indices.size == 0:
return len(G) == 1
unused_node_ids, labels = plc.weakly_connected_components(
resource_handle=plc.ResourceHandle(),
graph=G._get_plc_graph(symmetrize=symmetrize),
offsets=None,
indices=None,
weights=None,
labels=None,
do_expensive_check=False,
)
return bool((labels == labels[0]).all())


@not_implemented_for("directed")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# 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 cupy as cp
import networkx as nx
import pylibcugraph as plc

from nx_cugraph.convert import _to_directed_graph
from nx_cugraph.utils import (
_groupby,
index_dtype,
networkx_algorithm,
not_implemented_for,
)

__all__ = [
"number_strongly_connected_components",
"strongly_connected_components",
"is_strongly_connected",
]


def _strongly_connected_components(G):
# TODO: create utility function to convert just the indices to CSR
# TODO: this uses a legacy PLC function (strongly_connected_components)
N = len(G)
indices = cp.lexsort(cp.vstack((G.dst_indices, G.src_indices)))
dst_indices = G.dst_indices[indices]
offsets = cp.searchsorted(
G.src_indices, cp.arange(N + 1, dtype=index_dtype), sorter=indices
).astype(index_dtype)
labels = cp.zeros(N, dtype=index_dtype)
plc.strongly_connected_components(
offsets=offsets,
indices=dst_indices,
weights=None,
num_verts=N,
num_edges=dst_indices.size,
labels=labels,
)
return labels


@not_implemented_for("undirected")
@networkx_algorithm(version_added="24.02", plc="strongly_connected_components")
def strongly_connected_components(G):
G = _to_directed_graph(G)
if G.src_indices.size == 0:
return [{key} for key in G._nodeiter_to_iter(range(len(G)))]
labels = _strongly_connected_components(G)
groups = _groupby(labels, cp.arange(len(G), dtype=index_dtype))
return (G._nodearray_to_set(connected_ids) for connected_ids in groups.values())


@not_implemented_for("undirected")
@networkx_algorithm(version_added="24.02", plc="strongly_connected_components")
def number_strongly_connected_components(G):
G = _to_directed_graph(G)
if G.src_indices.size == 0:
return len(G)
labels = _strongly_connected_components(G)
return cp.unique(labels).size


@not_implemented_for("undirected")
@networkx_algorithm(version_added="24.02", plc="strongly_connected_components")
def is_strongly_connected(G):
G = _to_directed_graph(G)
if len(G) == 0:
raise nx.NetworkXPointlessConcept(
"Connectivity is undefined for the null graph."
)
if G.src_indices.size == 0:
return len(G) == 1
labels = _strongly_connected_components(G)
return bool((labels == labels[0]).all())
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# 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 nx_cugraph.convert import _to_directed_graph
from nx_cugraph.utils import networkx_algorithm, not_implemented_for

from .connected import (
_connected_components,
_is_connected,
_number_connected_components,
)

__all__ = [
"number_weakly_connected_components",
"weakly_connected_components",
"is_weakly_connected",
]


@not_implemented_for("undirected")
@networkx_algorithm(plc="weakly_connected_components", version_added="24.02")
def weakly_connected_components(G):
G = _to_directed_graph(G)
return _connected_components(G, symmetrize="union")


@not_implemented_for("undirected")
@networkx_algorithm(plc="weakly_connected_components", version_added="24.02")
def number_weakly_connected_components(G):
G = _to_directed_graph(G)
return _number_connected_components(G, symmetrize="union")


@not_implemented_for("undirected")
@networkx_algorithm(plc="weakly_connected_components", version_added="24.02")
def is_weakly_connected(G):
G = _to_directed_graph(G)
return _is_connected(G, symmetrize="union")
Loading

0 comments on commit 3750d54

Please sign in to comment.