Skip to content

Commit

Permalink
nx-cugraph: add dijkstra sssp functions (#4538)
Browse files Browse the repository at this point in the history
This does add new functionality (so it's not just sugar!), since dijstra methods have `cutoff=` arguments.

Note that we are missing multi-source dijstra methods, since `plc.sssp` only accepts a single source.

We also do not have `bidirectional_dijkstra`, since PLC does not implement this algorithm.

Authors:
  - Erik Welch (https://github.com/eriknw)
  - Rick Ratzel (https://github.com/rlratzel)
  - Ralph Liu (https://github.com/nv-rliu)

Approvers:
  - Rick Ratzel (https://github.com/rlratzel)

URL: #4538
  • Loading branch information
eriknw authored Jul 30, 2024
1 parent f20ccdd commit 0e69733
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 63 deletions.
10 changes: 9 additions & 1 deletion python/nx-cugraph/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,19 @@ Below is the list of algorithms that are currently supported in nx-cugraph.
└─ <a href="https://networkx.org/documentation/stable/reference/algorithms/shortest_paths.html#module-networkx.algorithms.shortest_paths.weighted">weighted</a>
├─ <a href="https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.weighted.all_pairs_bellman_ford_path.html#networkx.algorithms.shortest_paths.weighted.all_pairs_bellman_ford_path">all_pairs_bellman_ford_path</a>
├─ <a href="https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.weighted.all_pairs_bellman_ford_path_length.html#networkx.algorithms.shortest_paths.weighted.all_pairs_bellman_ford_path_length">all_pairs_bellman_ford_path_length</a>
├─ <a href="https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.weighted.all_pairs_dijkstra.html#networkx.algorithms.shortest_paths.weighted.all_pairs_dijkstra">all_pairs_dijkstra</a>
├─ <a href="https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.weighted.all_pairs_dijkstra_path.html#networkx.algorithms.shortest_paths.weighted.all_pairs_dijkstra_path">all_pairs_dijkstra_path</a>
├─ <a href="https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.weighted.all_pairs_dijkstra_path_length.html#networkx.algorithms.shortest_paths.weighted.all_pairs_dijkstra_path_length">all_pairs_dijkstra_path_length</a>
├─ <a href="https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford_path.html#networkx.algorithms.shortest_paths.weighted.bellman_ford_path">bellman_ford_path</a>
├─ <a href="https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford_path_length.html#networkx.algorithms.shortest_paths.weighted.bellman_ford_path_length">bellman_ford_path_length</a>
├─ <a href="https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.weighted.dijkstra_path.html#networkx.algorithms.shortest_paths.weighted.dijkstra_path">dijkstra_path</a>
├─ <a href="https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.weighted.dijkstra_path_length.html#networkx.algorithms.shortest_paths.weighted.dijkstra_path_length">dijkstra_path_length</a>
├─ <a href="https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.weighted.single_source_bellman_ford.html#networkx.algorithms.shortest_paths.weighted.single_source_bellman_ford">single_source_bellman_ford</a>
├─ <a href="https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.weighted.single_source_bellman_ford_path.html#networkx.algorithms.shortest_paths.weighted.single_source_bellman_ford_path">single_source_bellman_ford_path</a>
└─ <a href="https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.weighted.single_source_bellman_ford_path_length.html#networkx.algorithms.shortest_paths.weighted.single_source_bellman_ford_path_length">single_source_bellman_ford_path_length</a>
├─ <a href="https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.weighted.single_source_bellman_ford_path_length.html#networkx.algorithms.shortest_paths.weighted.single_source_bellman_ford_path_length">single_source_bellman_ford_path_length</a>
├─ <a href="https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.weighted.single_source_dijkstra.html#networkx.algorithms.shortest_paths.weighted.single_source_dijkstra">single_source_dijkstra</a>
├─ <a href="https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.weighted.single_source_dijkstra_path.html#networkx.algorithms.shortest_paths.weighted.single_source_dijkstra_path">single_source_dijkstra_path</a>
└─ <a href="https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.weighted.single_source_dijkstra_path_length.html#networkx.algorithms.shortest_paths.weighted.single_source_dijkstra_path_length">single_source_dijkstra_path_length</a>
<a href="https://networkx.org/documentation/stable/reference/algorithms/traversal.html">traversal</a>
└─ <a href="https://networkx.org/documentation/stable/reference/algorithms/traversal.html#module-networkx.algorithms.traversal.breadth_first_search">breadth_first_search</a>
├─ <a href="https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.traversal.breadth_first_search.bfs_edges.html#networkx.algorithms.traversal.breadth_first_search.bfs_edges">bfs_edges</a>
Expand Down
36 changes: 34 additions & 2 deletions python/nx-cugraph/_nx_cugraph/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
# BEGIN: functions
"all_pairs_bellman_ford_path",
"all_pairs_bellman_ford_path_length",
"all_pairs_dijkstra",
"all_pairs_dijkstra_path",
"all_pairs_dijkstra_path_length",
"all_pairs_shortest_path",
"all_pairs_shortest_path_length",
"ancestors",
Expand Down Expand Up @@ -75,6 +78,8 @@
"descendants",
"descendants_at_distance",
"diamond_graph",
"dijkstra_path",
"dijkstra_path_length",
"dodecahedral_graph",
"edge_betweenness_centrality",
"ego_graph",
Expand Down Expand Up @@ -132,6 +137,9 @@
"single_source_bellman_ford",
"single_source_bellman_ford_path",
"single_source_bellman_ford_path_length",
"single_source_dijkstra",
"single_source_dijkstra_path",
"single_source_dijkstra_path_length",
"single_source_shortest_path",
"single_source_shortest_path_length",
"single_target_shortest_path",
Expand Down Expand Up @@ -173,8 +181,8 @@
"katz_centrality": "`nstart` isn't used (but is checked), and `normalized=False` is not supported.",
"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.",
"shortest_path": "Negative weights are not yet supported, and method is ununsed.",
"shortest_path_length": "Negative weights are not yet supported, and method is ununsed.",
"shortest_path": "Negative weights are not yet supported.",
"shortest_path_length": "Negative weights are not yet supported.",
"single_source_bellman_ford": "Negative cycles are not yet supported. ``NotImplementedError`` will be raised if there are negative edge weights. We plan to support negative edge weights soon. Also, callable ``weight`` argument is not supported.",
"single_source_bellman_ford_path": "Negative cycles are not yet supported. ``NotImplementedError`` will be raised if there are negative edge weights. We plan to support negative edge weights soon. Also, callable ``weight`` argument is not supported.",
"single_source_bellman_ford_path_length": "Negative cycles are not yet supported. ``NotImplementedError`` will be raised if there are negative edge weights. We plan to support negative edge weights soon. Also, callable ``weight`` argument is not supported.",
Expand All @@ -189,12 +197,27 @@
"all_pairs_bellman_ford_path_length": {
"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.",
},
"all_pairs_dijkstra": {
"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.",
},
"all_pairs_dijkstra_path": {
"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.",
},
"all_pairs_dijkstra_path_length": {
"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.",
},
"bellman_ford_path": {
"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.",
},
"bellman_ford_path_length": {
"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.",
},
"dijkstra_path": {
"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.",
},
"dijkstra_path_length": {
"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.",
},
"ego_graph": {
"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.",
},
Expand Down Expand Up @@ -229,6 +252,15 @@
"single_source_bellman_ford_path_length": {
"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.",
},
"single_source_dijkstra": {
"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.",
},
"single_source_dijkstra_path": {
"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.",
},
"single_source_dijkstra_path_length": {
"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.",
},
# END: additional_parameters
},
}
Expand Down
54 changes: 30 additions & 24 deletions python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def has_path(G, source, target):
def shortest_path(
G, source=None, target=None, weight=None, method="dijkstra", *, dtype=None
):
"""Negative weights are not yet supported, and method is ununsed."""
"""Negative weights are not yet supported."""
if method not in {"dijkstra", "bellman-ford"}:
raise ValueError(f"method not supported: {method}")
if weight is None:
Expand All @@ -53,9 +53,9 @@ def shortest_path(
# All pairs
if method == "unweighted":
paths = nxcg.all_pairs_shortest_path(G)
else:
# method == "dijkstra":
# method == 'bellman-ford':
elif method == "dijkstra":
paths = nxcg.all_pairs_dijkstra_path(G, weight=weight, dtype=dtype)
else: # method == 'bellman-ford':
paths = nxcg.all_pairs_bellman_ford_path(G, weight=weight, dtype=dtype)
if nx.__version__[:3] <= "3.4":
paths = dict(paths)
Expand All @@ -75,9 +75,11 @@ def shortest_path(
# From source
if method == "unweighted":
paths = nxcg.single_source_shortest_path(G, source)
else:
# method == "dijkstra":
# method == 'bellman-ford':
elif method == "dijkstra":
paths = nxcg.single_source_dijkstra_path(
G, source, weight=weight, dtype=dtype
)
else: # method == 'bellman-ford':
paths = nxcg.single_source_bellman_ford_path(
G, source, weight=weight, dtype=dtype
)
Expand Down Expand Up @@ -106,7 +108,7 @@ def _(G, source=None, target=None, weight=None, method="dijkstra", *, dtype=None
def shortest_path_length(
G, source=None, target=None, weight=None, method="dijkstra", *, dtype=None
):
"""Negative weights are not yet supported, and method is ununsed."""
"""Negative weights are not yet supported."""
if method not in {"dijkstra", "bellman-ford"}:
raise ValueError(f"method not supported: {method}")
if weight is None:
Expand All @@ -116,9 +118,11 @@ def shortest_path_length(
# All pairs
if method == "unweighted":
lengths = nxcg.all_pairs_shortest_path_length(G)
else:
# method == "dijkstra":
# method == 'bellman-ford':
elif method == "dijkstra":
lengths = nxcg.all_pairs_dijkstra_path_length(
G, weight=weight, dtype=dtype
)
else: # method == 'bellman-ford':
lengths = nxcg.all_pairs_bellman_ford_path_length(
G, weight=weight, dtype=dtype
)
Expand All @@ -127,31 +131,33 @@ def shortest_path_length(
lengths = nxcg.single_target_shortest_path_length(G, target)
if nx.__version__[:3] <= "3.4":
lengths = dict(lengths)
else:
# method == "dijkstra":
# method == 'bellman-ford':
elif method == "dijkstra":
lengths = nxcg.single_source_dijkstra_path_length(
G, target, weight=weight, dtype=dtype
)
else: # method == 'bellman-ford':
lengths = nxcg.single_source_bellman_ford_path_length(
G, target, weight=weight, dtype=dtype
)
elif target is None:
# From source
if method == "unweighted":
lengths = nxcg.single_source_shortest_path_length(G, source)
else:
# method == "dijkstra":
# method == 'bellman-ford':
lengths = dict(
nxcg.single_source_bellman_ford_path_length(
G, source, weight=weight, dtype=dtype
)
elif method == "dijkstra":
lengths = nxcg.single_source_dijkstra_path_length(
G, source, weight=weight, dtype=dtype
)
else: # method == 'bellman-ford':
lengths = nxcg.single_source_bellman_ford_path_length(
G, source, weight=weight, dtype=dtype
)
# From source to target
elif method == "unweighted":
G = _to_graph(G)
lengths = _bfs(G, source, None, "Source", return_type="length", target=target)
else:
# method == "dijkstra":
# method == 'bellman-ford':
elif method == "dijkstra":
lengths = nxcg.dijkstra_path_length(G, source, target, weight, dtype=dtype)
else: # method == 'bellman-ford':
lengths = nxcg.bellman_ford_path_length(G, source, target, weight, dtype=dtype)
return lengths

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,12 @@ def bidirectional_shortest_path(G, source, target):
# TODO PERF: do bidirectional traversal in core
G = _to_graph(G)
if source not in G or target not in G:
raise nx.NodeNotFound(f"Either source {source} or target {target} is not in G")
if nx.__version__[:3] <= "3.3":
raise nx.NodeNotFound(
f"Either source {source} or target {target} is not in G"
)
missing = f"Source {source}" if source not in G else f"Target {target}"
raise nx.NodeNotFound(f"{missing} is not in G")
return _bfs(G, source, None, "Source", return_type="path", target=target)


Expand Down Expand Up @@ -131,7 +136,7 @@ def _bfs(
# return_type == "length-path"
return {source: 0}, {source: [source]}

if cutoff is None:
if cutoff is None or np.isinf(cutoff):
cutoff = -1
src_index = source if G.key_to_id is None else G.key_to_id[source]
distances, predecessors, node_ids = plc.bfs(
Expand Down
Loading

0 comments on commit 0e69733

Please sign in to comment.