diff --git a/python/nx-cugraph/README.md b/python/nx-cugraph/README.md index 088f2fd2072..8a1824a7a0e 100644 --- a/python/nx-cugraph/README.md +++ b/python/nx-cugraph/README.md @@ -173,11 +173,19 @@ Below is the list of algorithms that are currently supported in nx-cugraph. └─ weighted ├─ all_pairs_bellman_ford_path ├─ all_pairs_bellman_ford_path_length + ├─ all_pairs_dijkstra + ├─ all_pairs_dijkstra_path + ├─ all_pairs_dijkstra_path_length ├─ bellman_ford_path ├─ bellman_ford_path_length + ├─ dijkstra_path + ├─ dijkstra_path_length ├─ single_source_bellman_ford ├─ single_source_bellman_ford_path - └─ single_source_bellman_ford_path_length + ├─ single_source_bellman_ford_path_length + ├─ single_source_dijkstra + ├─ single_source_dijkstra_path + └─ single_source_dijkstra_path_length traversal └─ breadth_first_search ├─ bfs_edges diff --git a/python/nx-cugraph/_nx_cugraph/__init__.py b/python/nx-cugraph/_nx_cugraph/__init__.py index 3d27e4b9e9d..2d6017fa219 100644 --- a/python/nx-cugraph/_nx_cugraph/__init__.py +++ b/python/nx-cugraph/_nx_cugraph/__init__.py @@ -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", @@ -75,6 +78,8 @@ "descendants", "descendants_at_distance", "diamond_graph", + "dijkstra_path", + "dijkstra_path_length", "dodecahedral_graph", "edge_betweenness_centrality", "ego_graph", @@ -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", @@ -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.", @@ -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.", }, @@ -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 }, } diff --git a/python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/generic.py b/python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/generic.py index 68dbbace93d..7d6d77f34a4 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/generic.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/generic.py @@ -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: @@ -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) @@ -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 ) @@ -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: @@ -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 ) @@ -127,9 +131,11 @@ 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 ) @@ -137,21 +143,21 @@ def shortest_path_length( # 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 diff --git a/python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/unweighted.py b/python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/unweighted.py index 714289c5b4b..0e98c366e4a 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/unweighted.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/unweighted.py @@ -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) @@ -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( diff --git a/python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/weighted.py b/python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/weighted.py index 32323dd45f3..032ef2c7fdf 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/weighted.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/weighted.py @@ -25,6 +25,14 @@ from .unweighted import _bfs __all__ = [ + "dijkstra_path", + "dijkstra_path_length", + "single_source_dijkstra", + "single_source_dijkstra_path", + "single_source_dijkstra_path_length", + "all_pairs_dijkstra", + "all_pairs_dijkstra_path", + "all_pairs_dijkstra_path_length", "bellman_ford_path", "bellman_ford_path_length", "single_source_bellman_ford", @@ -44,14 +52,24 @@ def _add_doc(func): return func -@networkx_algorithm(extra_params=_dtype_param, version_added="24.04", _plc="sssp") -@_add_doc -def bellman_ford_path(G, source, target, weight="weight", *, dtype=None): +@networkx_algorithm(extra_params=_dtype_param, version_added="24.08", _plc="sssp") +def dijkstra_path(G, source, target, weight="weight", *, dtype=None): G = _to_graph(G, weight, 1, np.float32) dtype = _get_float_dtype(dtype, graph=G, weight=weight) return _sssp(G, source, weight, target, return_type="path", dtype=dtype) +@dijkstra_path._can_run +def _(G, source, target, weight="weight", *, dtype=None): + return not callable(weight) + + +@networkx_algorithm(extra_params=_dtype_param, version_added="24.04", _plc="sssp") +@_add_doc +def bellman_ford_path(G, source, target, weight="weight", *, dtype=None): + return dijkstra_path(G, source, target, weight=weight, dtype=dtype) + + @bellman_ford_path._can_run def _(G, source, target, weight="weight", *, dtype=None): return ( @@ -61,14 +79,24 @@ def _(G, source, target, weight="weight", *, dtype=None): ) -@networkx_algorithm(extra_params=_dtype_param, version_added="24.04", _plc="sssp") -@_add_doc -def bellman_ford_path_length(G, source, target, weight="weight", *, dtype=None): +@networkx_algorithm(extra_params=_dtype_param, version_added="24.08", _plc="sssp") +def dijkstra_path_length(G, source, target, weight="weight", *, dtype=None): G = _to_graph(G, weight, 1, np.float32) dtype = _get_float_dtype(dtype, graph=G, weight=weight) return _sssp(G, source, weight, target, return_type="length", dtype=dtype) +@dijkstra_path._can_run +def _(G, source, target, weight="weight", *, dtype=None): + return not callable(weight) + + +@networkx_algorithm(extra_params=_dtype_param, version_added="24.04", _plc="sssp") +@_add_doc +def bellman_ford_path_length(G, source, target, weight="weight", *, dtype=None): + return dijkstra_path_length(G, source, target, weight=weight, dtype=dtype) + + @bellman_ford_path_length._can_run def _(G, source, target, weight="weight", *, dtype=None): return ( @@ -78,12 +106,22 @@ def _(G, source, target, weight="weight", *, dtype=None): ) +@networkx_algorithm(extra_params=_dtype_param, version_added="24.08", _plc="sssp") +def single_source_dijkstra_path(G, source, cutoff=None, weight="weight", *, dtype=None): + G = _to_graph(G, weight, 1, np.float32) + dtype = _get_float_dtype(dtype, graph=G, weight=weight) + return _sssp(G, source, weight, return_type="path", dtype=dtype, cutoff=cutoff) + + +@single_source_dijkstra_path._can_run +def _(G, source, cutoff=None, weight="weight", *, dtype=None): + return not callable(weight) + + @networkx_algorithm(extra_params=_dtype_param, version_added="24.04", _plc="sssp") @_add_doc def single_source_bellman_ford_path(G, source, weight="weight", *, dtype=None): - G = _to_graph(G, weight, 1, np.float32) - dtype = _get_float_dtype(dtype, graph=G, weight=weight) - return _sssp(G, source, weight, return_type="path", dtype=dtype) + return single_source_dijkstra_path(G, source, weight=weight, dtype=dtype) @single_source_bellman_ford_path._can_run @@ -95,12 +133,24 @@ def _(G, source, weight="weight", *, dtype=None): ) +@networkx_algorithm(extra_params=_dtype_param, version_added="24.08", _plc="sssp") +def single_source_dijkstra_path_length( + G, source, cutoff=None, weight="weight", *, dtype=None +): + G = _to_graph(G, weight, 1, np.float32) + dtype = _get_float_dtype(dtype, graph=G, weight=weight) + return _sssp(G, source, weight, return_type="length", dtype=dtype, cutoff=cutoff) + + +@single_source_dijkstra_path_length._can_run +def _(G, source, cutoff=None, weight="weight", *, dtype=None): + return not callable(weight) + + @networkx_algorithm(extra_params=_dtype_param, version_added="24.04", _plc="sssp") @_add_doc def single_source_bellman_ford_path_length(G, source, weight="weight", *, dtype=None): - G = _to_graph(G, weight, 1, np.float32) - dtype = _get_float_dtype(dtype, graph=G, weight=weight) - return _sssp(G, source, weight, return_type="length", dtype=dtype) + return single_source_dijkstra_path_length(G, source, weight=weight, dtype=dtype) @single_source_bellman_ford_path_length._can_run @@ -112,12 +162,26 @@ def _(G, source, weight="weight", *, dtype=None): ) +@networkx_algorithm(extra_params=_dtype_param, version_added="24.08", _plc="sssp") +def single_source_dijkstra( + G, source, target=None, cutoff=None, weight="weight", *, dtype=None +): + G = _to_graph(G, weight, 1, np.float32) + dtype = _get_float_dtype(dtype, graph=G, weight=weight) + return _sssp( + G, source, weight, target, return_type="length-path", dtype=dtype, cutoff=cutoff + ) + + +@single_source_dijkstra._can_run +def _(G, source, target=None, cutoff=None, weight="weight", *, dtype=None): + return not callable(weight) + + @networkx_algorithm(extra_params=_dtype_param, version_added="24.04", _plc="sssp") @_add_doc def single_source_bellman_ford(G, source, target=None, weight="weight", *, dtype=None): - G = _to_graph(G, weight, 1, np.float32) - dtype = _get_float_dtype(dtype, graph=G, weight=weight) - return _sssp(G, source, weight, target, return_type="length-path", dtype=dtype) + return single_source_dijkstra(G, source, target=target, weight=weight, dtype=dtype) @single_source_bellman_ford._can_run @@ -129,14 +193,41 @@ def _(G, source, target=None, weight="weight", *, dtype=None): ) -@networkx_algorithm(extra_params=_dtype_param, version_added="24.04", _plc="sssp") -@_add_doc -def all_pairs_bellman_ford_path_length(G, weight="weight", *, dtype=None): +@networkx_algorithm(extra_params=_dtype_param, version_added="24.08", _plc="sssp") +def all_pairs_dijkstra(G, cutoff=None, weight="weight", *, dtype=None): + # TODO PERF: batched bfs to compute many at once + G = _to_graph(G, weight, 1, np.float32) + dtype = _get_float_dtype(dtype, graph=G, weight=weight) + for n in G: + yield ( + n, + _sssp(G, n, weight, return_type="length-path", dtype=dtype, cutoff=cutoff), + ) + + +@all_pairs_dijkstra._can_run +def _(G, cutoff=None, weight="weight", *, dtype=None): + return not callable(weight) + + +@networkx_algorithm(extra_params=_dtype_param, version_added="24.08", _plc="sssp") +def all_pairs_dijkstra_path_length(G, cutoff=None, weight="weight", *, dtype=None): # TODO PERF: batched bfs to compute many at once G = _to_graph(G, weight, 1, np.float32) dtype = _get_float_dtype(dtype, graph=G, weight=weight) for n in G: - yield (n, _sssp(G, n, weight, return_type="length", dtype=dtype)) + yield (n, _sssp(G, n, weight, return_type="length", dtype=dtype, cutoff=cutoff)) + + +@all_pairs_dijkstra_path_length._can_run +def _(G, cutoff=None, weight="weight", *, dtype=None): + return not callable(weight) + + +@networkx_algorithm(extra_params=_dtype_param, version_added="24.04", _plc="sssp") +@_add_doc +def all_pairs_bellman_ford_path_length(G, weight="weight", *, dtype=None): + return all_pairs_dijkstra_path_length(G, weight=weight, dtype=None) @all_pairs_bellman_ford_path_length._can_run @@ -148,14 +239,24 @@ def _(G, weight="weight", *, dtype=None): ) -@networkx_algorithm(extra_params=_dtype_param, version_added="24.04", _plc="sssp") -@_add_doc -def all_pairs_bellman_ford_path(G, weight="weight", *, dtype=None): +@networkx_algorithm(extra_params=_dtype_param, version_added="24.08", _plc="sssp") +def all_pairs_dijkstra_path(G, cutoff=None, weight="weight", *, dtype=None): # TODO PERF: batched bfs to compute many at once G = _to_graph(G, weight, 1, np.float32) dtype = _get_float_dtype(dtype, graph=G, weight=weight) for n in G: - yield (n, _sssp(G, n, weight, return_type="path", dtype=dtype)) + yield (n, _sssp(G, n, weight, return_type="path", dtype=dtype, cutoff=cutoff)) + + +@all_pairs_dijkstra_path._can_run +def _(G, cutoff=None, weight="weight", *, dtype=None): + return not callable(weight) + + +@networkx_algorithm(extra_params=_dtype_param, version_added="24.04", _plc="sssp") +@_add_doc +def all_pairs_bellman_ford_path(G, weight="weight", *, dtype=None): + return all_pairs_dijkstra_path(G, weight=weight, dtype=None) @all_pairs_bellman_ford_path._can_run @@ -167,7 +268,17 @@ def _(G, weight="weight", *, dtype=None): ) -def _sssp(G, source, weight, target=None, *, return_type, dtype, reverse_path=False): +def _sssp( + G, + source, + weight, + target=None, + *, + return_type, + dtype, + reverse_path=False, + cutoff=None, +): """SSSP for weighted shortest paths. Parameters @@ -201,7 +312,7 @@ def _sssp(G, source, weight, target=None, *, return_type, dtype, reverse_path=Fa if weight not in G.edge_values: # No edge values, so use BFS instead - return _bfs(G, source, None, "Source", return_type=return_type, target=target) + return _bfs(G, source, cutoff, "Source", return_type=return_type, target=target) # Check for negative values since we don't support negative cycles edge_vals = G.edge_values[weight] @@ -217,7 +328,7 @@ def _sssp(G, source, weight, target=None, *, return_type, dtype, reverse_path=Fa return _bfs( G, source, - None, + None if cutoff is None else cutoff / edge_val, "Source", return_type=return_type, target=target, @@ -226,11 +337,16 @@ def _sssp(G, source, weight, target=None, *, return_type, dtype, reverse_path=Fa ) src_index = source if G.key_to_id is None else G.key_to_id[source] + if cutoff is None: + cutoff = np.inf + else: + cutoff = np.nextafter(cutoff, np.inf, dtype=np.float64) + node_ids, distances, predecessors = plc.sssp( resource_handle=plc.ResourceHandle(), graph=G._get_plc_graph(weight, 1, dtype), source=src_index, - cutoff=np.inf, + cutoff=cutoff, compute_predecessors=True, # TODO: False is not yet supported # compute_predecessors=return_type != "length", do_expensive_check=False, 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 d784d8c13cb..176b531a6e7 100644 --- a/python/nx-cugraph/nx_cugraph/tests/test_match_api.py +++ b/python/nx-cugraph/nx_cugraph/tests/test_match_api.py @@ -48,7 +48,7 @@ def test_match_signature_and_names(): orig_sig = inspect.signature(orig_func) func_sig = inspect.signature(func) if not func.extra_params: - assert orig_sig == func_sig + assert orig_sig == func_sig, name else: # Ignore extra parameters added to nx-cugraph algorithm # The key of func.extra_params may be like "max_level : int, optional", @@ -60,14 +60,14 @@ def test_match_signature_and_names(): for name, p in func_sig.parameters.items() if name not in extra_params ] - ) + ), name if func.can_run is not nxcg.utils.decorators._default_can_run: - assert func_sig == inspect.signature(func.can_run) + assert func_sig == inspect.signature(func.can_run), name if func.should_run is not nxcg.utils.decorators._default_should_run: - assert func_sig == inspect.signature(func.should_run) + assert func_sig == inspect.signature(func.should_run), name # Matching function names? - assert func.__name__ == dispatchable_func.__name__ == orig_func.__name__ + assert func.__name__ == dispatchable_func.__name__ == orig_func.__name__, name # Matching dispatch names? # nx version >=3.2 uses name, version >=3.0,<3.2 uses dispatchname @@ -75,14 +75,14 @@ def test_match_signature_and_names(): dispatchname = dispatchable_func.dispatchname else: dispatchname = dispatchable_func.name - assert func.name == dispatchname + assert func.name == dispatchname, name # Matching modules (i.e., where function defined)? assert ( "networkx." + func.__module__.split(".", 1)[1] == dispatchable_func.__module__ == orig_func.__module__ - ) + ), name # Matching package layout (i.e., which modules have the function)? nxcg_path = func.__module__