Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH : Adding backend_info entry point #27

Merged
merged 26 commits into from
Jan 28, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7561cf9
added n_jobs kwarg and removed unnecessary docs
Schefflera-Arboricola Jan 1, 2024
3da6091
added back cpu_count
Schefflera-Arboricola Jan 1, 2024
0d6ac99
rm cpu_count
Schefflera-Arboricola Jan 2, 2024
baf17a3
updated cpu_count and pyproject.toml
Schefflera-Arboricola Jan 2, 2024
56db8b2
updated .pre-commit-config.yaml
Schefflera-Arboricola Jan 2, 2024
ada49f9
backends to plugins(already done in PR 26)
Schefflera-Arboricola Jan 2, 2024
7e06a5f
updated docs
Schefflera-Arboricola Jan 2, 2024
8c3c163
Merge branch 'networkx:main' into update_all
Schefflera-Arboricola Jan 5, 2024
aa2a159
updated docs of betweenness_centrality and interface.py
Schefflera-Arboricola Jan 6, 2024
3ee2650
updated docs of all funcs and removed n_jobs
Schefflera-Arboricola Jan 6, 2024
c3a4427
added desc about parallel implementation step in all funcs
Schefflera-Arboricola Jan 6, 2024
7102d24
Merge branch 'networkx:main' into update_all
Schefflera-Arboricola Jan 6, 2024
312833f
Added egs to vitality.py
Schefflera-Arboricola Jan 6, 2024
360e13f
added get_info func and Parallel Computation section in all funcs
Schefflera-Arboricola Jan 11, 2024
b37e266
fixed get_info()
Schefflera-Arboricola Jan 12, 2024
4815a29
rm docs and backend_examples
Schefflera-Arboricola Jan 17, 2024
d85ab43
fixed get_info
Schefflera-Arboricola Jan 17, 2024
b1bb3ba
moved get_info to backend.py
Schefflera-Arboricola Jan 17, 2024
d9c4d6a
removed docstring pytest(bcoz no egs in docs)
Schefflera-Arboricola Jan 17, 2024
b07eace
style fix
Schefflera-Arboricola Jan 18, 2024
827c288
made this PR independent of PR-7219
Schefflera-Arboricola Jan 19, 2024
8fa0ba2
Merge branch 'networkx:main' into update_all
Schefflera-Arboricola Jan 19, 2024
12ea240
making new PR for pre-commit-config update
Schefflera-Arboricola Jan 21, 2024
9a06c2a
updated get_info()
Schefflera-Arboricola Jan 21, 2024
f3e62e3
moved tournament func renaming to pr 32
Schefflera-Arboricola Jan 22, 2024
544648f
Merge branch 'main' into update_all
Schefflera-Arboricola Jan 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@
# pre-commit install

repos:
- repo: https://github.com/psf/black
rev: 23.9.1
- repo: https://github.com/adamchainz/blacken-docs
rev: 1.16.0
hooks:
- id: black
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.292
- id: blacken-docs
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.1.0
hooks:
- id: prettier
files: \.(html|md|toml|yml|yaml)
args: [--prose-wrap=preserve]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.8
hooks:
- id: ruff
args:
- --fix
- id: ruff-format

24 changes: 12 additions & 12 deletions nx_parallel/algorithms/centrality/betweenness.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,16 @@
_single_source_shortest_path_basic,
)
from networkx.utils import py_random_state

import nx_parallel as nxp

__all__ = ["betweenness_centrality"]


@py_random_state(5)
def betweenness_centrality(
G, k=None, normalized=True, weight=None, endpoints=False, seed=None
G, k=None, normalized=True, weight=None, endpoints=False, seed=None, n_jobs=-1
):
r"""Parallel Compute shortest-path betweenness centrality for nodes
r"""Parallel Computes shortest-path betweenness centrality for nodes

Betweenness centrality of a node $v$ is the sum of the
fraction of all-pairs shortest paths that pass through $v$
Expand Down Expand Up @@ -61,6 +60,11 @@ def betweenness_centrality(
See :ref:`Randomness<randomness>`.
Note that this is only used if k is not None.

n_jobs : int, optional (default=-1)
The number of logical CPUs or cores you want to use.
For `n_jobs` less than 0, (`n_cpus + 1 + n_jobs`) are used.
If an invalid value is given, then `n_jobs` is set to `n_cpus`.

Returns
-------
nodes : dictionary
Expand All @@ -80,17 +84,13 @@ def betweenness_centrality(
else:
nodes = seed.sample(list(G.nodes), k)

total_cores = nxp.cpu_count()
num_in_chunk = max(len(nodes) // total_cores, 1)
cpu_count = nxp.cpu_count(n_jobs)

num_in_chunk = max(len(nodes) // cpu_count, 1)
node_chunks = nxp.chunks(nodes, num_in_chunk)

bt_cs = Parallel(n_jobs=total_cores)(
delayed(_betweenness_centrality_node_subset)(
G,
chunk,
weight,
endpoints,
)
bt_cs = Parallel(n_jobs=cpu_count)(
delayed(_betweenness_centrality_node_subset)(G, chunk, weight, endpoints,)
for chunk in node_chunks
)

Expand Down
35 changes: 14 additions & 21 deletions nx_parallel/algorithms/efficiency_measures.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
"""Provides functions for computing the efficiency of nodes and graphs."""
import networkx as nx
from joblib import Parallel, delayed

import nx_parallel as nxp

__all__ = ["local_efficiency"]

"""Helper to interface between graph types"""


def local_efficiency(G):
def local_efficiency(G, n_jobs=-1):
"""Returns the average local efficiency of the graph.

The *efficiency* of a pair of nodes in a graph is the multiplicative
Expand All @@ -23,44 +20,40 @@ def local_efficiency(G):
G : :class:`networkx.Graph`
An undirected graph for which to compute the average local efficiency.

n_jobs : int, optional (default=-1)
The number of logical CPUs or cores you want to use.
For `n_jobs` less than 0, (`n_cpus + 1 + n_jobs`) are used.
If an invalid value is given, then `n_jobs` is set to `n_cpus`.

Returns
-------
float
The average local efficiency of the graph.

Examples
--------
>>> G = nx.Graph([(0, 1), (0, 2), (0, 3), (1, 2), (1, 3)])
>>> nx.local_efficiency(G)
0.9166666666666667

Notes
-----
Edge weights are ignored when computing the shortest path distances.

See also
--------
global_efficiency

References
----------
.. [1] Latora, Vito, and Massimo Marchiori.
"Efficient behavior of small-world networks."
*Physical Review Letters* 87.19 (2001): 198701.
<https://doi.org/10.1103/PhysRevLett.87.198701>
"""

def _local_efficiency_node_subset(G, nodes):
return sum(nx.global_efficiency(G.subgraph(G[v])) for v in nodes)

if hasattr(G, "graph_object"):
G = G.graph_object

total_cores = nxp.cpu_count()
num_in_chunk = max(len(G.nodes) // total_cores, 1)
cpu_count = nxp.cpu_count()

num_in_chunk = max(len(G.nodes) // cpu_count, 1)
node_chunks = list(nxp.chunks(G.nodes, num_in_chunk))

efficiencies = Parallel(n_jobs=total_cores)(
efficiencies = Parallel(n_jobs=cpu_count)(
delayed(_local_efficiency_node_subset)(G, chunk) for chunk in node_chunks
)
return sum(efficiencies) / len(G)


def _local_efficiency_node_subset(G, nodes):
return sum(nx.global_efficiency(G.subgraph(G[v])) for v in nodes)
17 changes: 13 additions & 4 deletions nx_parallel/algorithms/isolate.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import networkx as nx
from joblib import Parallel, delayed

import nx_parallel as nxp

__all__ = ["number_of_isolates"]


def number_of_isolates(G):
def number_of_isolates(G, n_jobs=-1):
"""Returns the number of isolates in the graph. Parallel implementation.

An *isolate* is a node with no neighbors (that is, with degree
Expand All @@ -17,6 +16,11 @@ def number_of_isolates(G):
----------
G : NetworkX graph

n_jobs : int, optional (default=-1)
The number of logical CPUs or cores you want to use.
For `n_jobs` less than 0, (`n_cpus + 1 + n_jobs`) are used.
If an invalid value is given, then `n_jobs` is set to `n_cpus`.

Returns
-------
int
Expand All @@ -25,8 +29,13 @@ def number_of_isolates(G):
"""
if hasattr(G, "graph_object"):
G = G.graph_object

cpu_count = nxp.cpu_count()

isolates_list = list(nx.isolates(G))
num_in_chunk = max(len(isolates_list) // nxp.cpu_count(), 1)
num_in_chunk = max(len(isolates_list) // cpu_count, 1)
isolate_chunks = nxp.chunks(isolates_list, num_in_chunk)
results = Parallel(n_jobs=-1)(delayed(len)(chunk) for chunk in isolate_chunks)
results = Parallel(n_jobs=cpu_count)(
delayed(len)(chunk) for chunk in isolate_chunks
)
return sum(results)
19 changes: 11 additions & 8 deletions nx_parallel/algorithms/shortest_paths/weighted.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
from joblib import Parallel, delayed
from networkx.algorithms.shortest_paths.weighted import single_source_bellman_ford_path

import nx_parallel as nxp

__all__ = ["all_pairs_bellman_ford_path"]


def all_pairs_bellman_ford_path(G, weight="weight"):
"""Compute shortest paths between all nodes in a weighted graph.
def all_pairs_bellman_ford_path(G, weight="weight", n_jobs=-1):
"""Computes shortest paths between all nodes in a weighted graph.

Parameters
----------
Expand All @@ -26,6 +25,11 @@ def all_pairs_bellman_ford_path(G, weight="weight"):
dictionary of edge attributes for that edge. The function must
return a number.

n_jobs : int, optional (default=-1)
The number of logical CPUs or cores you want to use.
For `n_jobs` less than 0, (`n_cpus + 1 + n_jobs`) are used.
If an invalid value is given, then `n_jobs` is set to `n_cpus`.

Returns
-------
paths : iterator
Expand All @@ -45,14 +49,13 @@ def all_pairs_bellman_ford_path(G, weight="weight"):
>>> path = dict(nx.all_pairs_bellman_ford_path(G))
>>> path[0][2]
[0, 1, 2]
>>> parallel_path = dict(nx.all_pairs_bellman_ford_path(G, backend="parallel"))
>>> parallel_path = dict(nx.all_pairs_bellman_ford_path(G, backend="parallel", n_jobs=3))
>>> parallel_path[0][2]
[0, 1, 2]
>>> import nx_parallel as nxp
>>> parallel_path_ = dict(nx.all_pairs_bellman_ford_path(nxp.ParallelGraph(G)))
>>> parallel_path_
{1: {1: [1], 0: [1, 0], 2: [1, 2]}, 0: {0: [0], 1: [0, 1], 2: [0, 1, 2]}, 2: {2: [2], 1: [2, 1], 0: [2, 1, 0]}}

"""

def _calculate_shortest_paths_subset(source):
Expand All @@ -61,11 +64,11 @@ def _calculate_shortest_paths_subset(source):
if hasattr(G, "graph_object"):
G = G.graph_object

nodes = G.nodes
cpu_count = nxp.cpu_count(n_jobs)

total_cores = nxp.cpu_count()
nodes = G.nodes

paths = Parallel(n_jobs=total_cores, return_as="generator")(
paths = Parallel(n_jobs=cpu_count, return_as="generator")(
delayed(_calculate_shortest_paths_subset)(source) for source in nodes
)
return paths
29 changes: 21 additions & 8 deletions nx_parallel/algorithms/tournament.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from joblib import Parallel, delayed

import nx_parallel as nxp

__all__ = [
Expand All @@ -8,7 +7,7 @@
]


def is_reachable(G, s, t):
def is_reachable(G, s, t, n_jobs=-1):
"""Decides whether there is a path from `s` to `t` in the tournament

This function is more theoretically efficient than the reachability
Expand All @@ -29,6 +28,11 @@ def is_reachable(G, s, t):
t : node
A node in the graph.

n_jobs : int, optional (default=-1)
The number of logical CPUs or cores you want to use.
For `n_jobs` less than 0, (`n_cpus + 1 + n_jobs`) are used.
If an invalid value is given, then `n_jobs` is set to `n_cpus`.

Returns
-------
bool
Expand Down Expand Up @@ -71,6 +75,8 @@ def is_reachable(G, s, t):
G_adj = G._adj
setG = set(G)

cpu_count = nxp.cpu_count(n_jobs)

def two_nbrhood_subset(G, chunk):
result = []
for v in chunk:
Expand All @@ -85,22 +91,22 @@ def check_closure_subset(chunk):
return all(not (s in S and t not in S and is_closed(G, S)) for S in chunk)

# send chunk of vertices to each process (calculating neighborhoods)
num_in_chunk = max(len(G) // nxp.cpu_count(), 1)
num_in_chunk = max(len(G) // cpu_count, 1)

# neighborhoods = [two_neighborhood_subset(G, chunk) for chunk in node_chunks]
neighborhoods = Parallel(n_jobs=-1)(
neighborhoods = Parallel(n_jobs=cpu_count)(
delayed(two_nbrhood_subset)(G, chunk) for chunk in nxp.chunks(G, num_in_chunk)
)

# send chunk of neighborhoods to each process (checking closure conditions)
nbrhoods = (nhood for nh_chunk in neighborhoods for nhood in nh_chunk)
results = Parallel(n_jobs=-1, backend="loky")(
results = Parallel(n_jobs=cpu_count)(
delayed(check_closure_subset)(ch) for ch in nxp.chunks(nbrhoods, num_in_chunk)
)
return all(results)


def tournament_is_strongly_connected(G):
def tournament_is_strongly_connected(G, n_jobs=-1):
"""Decides whether the given tournament is strongly connected.

This function is more theoretically efficient than the
Expand All @@ -114,6 +120,11 @@ def tournament_is_strongly_connected(G):
----------
G : NetworkX graph
A directed graph representing a tournament.

n_jobs : int, optional (default=-1)
The number of logical CPUs or cores you want to use.
For `n_jobs` less than 0, (`n_cpus + 1 + n_jobs`) are used.
If an invalid value is given, then `n_jobs` is set to `n_cpus`.

Returns
-------
Expand Down Expand Up @@ -159,12 +170,14 @@ def tournament_is_strongly_connected(G):
if hasattr(G, "graph_object"):
G = G.graph_object

cpu_count = nxp.cpu_count()

# Subset version of is_reachable
def is_reachable_subset(G, chunk):
return all(is_reachable(G, u, v) for v in chunk for u in G)

num_in_chunk = max(len(G) // nxp.cpu_count(), 1)
results = Parallel(n_jobs=-1)(
num_in_chunk = max(len(G) // cpu_count, 1)
results = Parallel(n_jobs=cpu_count)(
delayed(is_reachable_subset)(G, ch) for ch in nxp.chunks(G, num_in_chunk)
)
return all(results)
Loading
Loading