-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement
.generators.ego.ego_graph
(#61)
* Implement `.generators.ego.ego_graph` Also, clean up shared BFS functions and move to `_bfs.py`. * use external images in README so they render on PyPI Support and test against Python 3.11 Change development status to Beta (was Alpha).
- Loading branch information
Showing
27 changed files
with
289 additions
and
240 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
"""BFS routines used by other algorithms""" | ||
|
||
import numpy as np | ||
from graphblas import Matrix, Vector, binary, replace, unary | ||
from graphblas.semiring import any_pair | ||
|
||
|
||
def _get_cutoff(n, cutoff): | ||
if cutoff is None or cutoff >= n: | ||
return n # Everything | ||
return cutoff + 1 # Inclusive | ||
|
||
|
||
def _plain_bfs(G, source, *, cutoff=None): | ||
index = G._key_to_id[source] | ||
A = G.get_property("offdiag") | ||
n = A.nrows | ||
v = Vector(bool, n, name="bfs_plain") | ||
q = Vector(bool, n, name="q") | ||
v[index] = True | ||
q[index] = True | ||
any_pair_bool = any_pair[bool] | ||
cutoff = _get_cutoff(n, cutoff) | ||
for _i in range(1, cutoff): | ||
q(~v.S, replace) << any_pair_bool(q @ A) | ||
if q.nvals == 0: | ||
break | ||
v(q.S) << True | ||
return v | ||
|
||
|
||
def _bfs_level(G, source, cutoff=None, *, transpose=False, dtype=int): | ||
if dtype == bool: | ||
dtype = int | ||
index = G._key_to_id[source] | ||
A = G.get_property("offdiag") | ||
if transpose and G.is_directed(): | ||
A = A.T # TODO: should we use "AT" instead? | ||
n = A.nrows | ||
v = Vector(dtype, n, name="bfs_level") | ||
q = Vector(bool, n, name="q") | ||
v[index] = 0 | ||
q[index] = True | ||
any_pair_bool = any_pair[bool] | ||
cutoff = _get_cutoff(n, cutoff) | ||
for i in range(1, cutoff): | ||
q(~v.S, replace) << any_pair_bool(q @ A) | ||
if q.nvals == 0: | ||
break | ||
v(q.S) << i | ||
return v | ||
|
||
|
||
def _bfs_levels(G, nodes, cutoff=None, *, dtype=int): | ||
if dtype == bool: | ||
dtype = int | ||
A = G.get_property("offdiag") | ||
n = A.nrows | ||
if nodes is None: | ||
# TODO: `D = Vector.from_scalar(0, n, dtype).diag()` | ||
D = Vector(dtype, n, name="bfs_levels_vector") | ||
D << 0 | ||
D = D.diag(name="bfs_levels") | ||
else: | ||
ids = G.list_to_ids(nodes) | ||
D = Matrix.from_coo( | ||
np.arange(len(ids), dtype=np.uint64), | ||
ids, | ||
0, | ||
dtype, | ||
nrows=len(ids), | ||
ncols=n, | ||
name="bfs_levels", | ||
) | ||
Q = unary.one[bool](D).new(name="Q") | ||
any_pair_bool = any_pair[bool] | ||
cutoff = _get_cutoff(n, cutoff) | ||
for i in range(1, cutoff): | ||
Q(~D.S, replace) << any_pair_bool(Q @ A) | ||
if Q.nvals == 0: | ||
break | ||
D(Q.S) << i | ||
return D | ||
|
||
|
||
# TODO: benchmark this and the version commented out below | ||
def _plain_bfs_bidirectional(G, source): | ||
# Bi-directional BFS w/o symmetrizing the adjacency matrix | ||
index = G._key_to_id[source] | ||
A = G.get_property("offdiag") | ||
# XXX: should we use `AT` if available? | ||
n = A.nrows | ||
v = Vector(bool, n, name="bfs_plain") | ||
q_out = Vector(bool, n, name="q_out") | ||
q_in = Vector(bool, n, name="q_in") | ||
v[index] = True | ||
q_in[index] = True | ||
any_pair_bool = any_pair[bool] | ||
is_out_empty = True | ||
is_in_empty = False | ||
for _i in range(1, n): | ||
# Traverse out-edges from the most recent `q_in` and `q_out` | ||
if is_out_empty: | ||
q_out(~v.S) << any_pair_bool(q_in @ A) | ||
else: | ||
q_out << binary.any(q_out | q_in) | ||
q_out(~v.S, replace) << any_pair_bool(q_out @ A) | ||
is_out_empty = q_out.nvals == 0 | ||
if not is_out_empty: | ||
v(q_out.S) << True | ||
elif is_in_empty: | ||
break | ||
# Traverse in-edges from the most recent `q_in` and `q_out` | ||
if is_in_empty: | ||
q_in(~v.S) << any_pair_bool(A @ q_out) | ||
else: | ||
q_in << binary.any(q_out | q_in) | ||
q_in(~v.S, replace) << any_pair_bool(A @ q_in) | ||
is_in_empty = q_in.nvals == 0 | ||
if not is_in_empty: | ||
v(q_in.S) << True | ||
elif is_out_empty: | ||
break | ||
return v | ||
|
||
|
||
""" | ||
def _plain_bfs_bidirectional(G, source): | ||
# Bi-directional BFS w/o symmetrizing the adjacency matrix | ||
index = G._key_to_id[source] | ||
A = G.get_property("offdiag") | ||
n = A.nrows | ||
v = Vector(bool, n, name="bfs_plain") | ||
q = Vector(bool, n, name="q") | ||
q2 = Vector(bool, n, name="q_2") | ||
v[index] = True | ||
q[index] = True | ||
any_pair_bool = any_pair[bool] | ||
for _i in range(1, n): | ||
q2(~v.S, replace) << any_pair_bool(q @ A) | ||
v(q2.S) << True | ||
q(~v.S, replace) << any_pair_bool(A @ q) | ||
if q.nvals == 0: | ||
if q2.nvals == 0: | ||
break | ||
q, q2 = q2, q | ||
elif q2.nvals != 0: | ||
q << binary.any(q | q2) | ||
return v | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 3 additions & 72 deletions
75
graphblas_algorithms/algorithms/components/weakly_connected.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,77 +1,8 @@ | ||
from graphblas import Vector, binary, replace | ||
from graphblas.semiring import any_pair | ||
|
||
from graphblas_algorithms.algorithms.exceptions import PointlessConcept | ||
from .._bfs import _plain_bfs_bidirectional | ||
from ..exceptions import PointlessConcept | ||
|
||
|
||
def is_weakly_connected(G): | ||
if len(G) == 0: | ||
raise PointlessConcept("Connectivity is undefined for the null graph.") | ||
return _plain_bfs(G, next(iter(G))).nvals == len(G) | ||
|
||
|
||
# TODO: benchmark this and the version commented out below | ||
def _plain_bfs(G, source): | ||
# Bi-directional BFS w/o symmetrizing the adjacency matrix | ||
index = G._key_to_id[source] | ||
A = G.get_property("offdiag") | ||
# XXX: should we use `AT` if available? | ||
n = A.nrows | ||
v = Vector(bool, n, name="bfs_plain") | ||
q_out = Vector(bool, n, name="q_out") | ||
q_in = Vector(bool, n, name="q_in") | ||
v[index] = True | ||
q_in[index] = True | ||
any_pair_bool = any_pair[bool] | ||
is_out_empty = True | ||
is_in_empty = False | ||
for _i in range(1, n): | ||
# Traverse out-edges from the most recent `q_in` and `q_out` | ||
if is_out_empty: | ||
q_out(~v.S) << any_pair_bool(q_in @ A) | ||
else: | ||
q_out << binary.any(q_out | q_in) | ||
q_out(~v.S, replace) << any_pair_bool(q_out @ A) | ||
is_out_empty = q_out.nvals == 0 | ||
if not is_out_empty: | ||
v(q_out.S) << True | ||
elif is_in_empty: | ||
break | ||
# Traverse in-edges from the most recent `q_in` and `q_out` | ||
if is_in_empty: | ||
q_in(~v.S) << any_pair_bool(A @ q_out) | ||
else: | ||
q_in << binary.any(q_out | q_in) | ||
q_in(~v.S, replace) << any_pair_bool(A @ q_in) | ||
is_in_empty = q_in.nvals == 0 | ||
if not is_in_empty: | ||
v(q_in.S) << True | ||
elif is_out_empty: | ||
break | ||
return v | ||
|
||
|
||
""" | ||
def _plain_bfs(G, source): | ||
# Bi-directional BFS w/o symmetrizing the adjacency matrix | ||
index = G._key_to_id[source] | ||
A = G.get_property("offdiag") | ||
n = A.nrows | ||
v = Vector(bool, n, name="bfs_plain") | ||
q = Vector(bool, n, name="q") | ||
q2 = Vector(bool, n, name="q_2") | ||
v[index] = True | ||
q[index] = True | ||
any_pair_bool = any_pair[bool] | ||
for _i in range(1, n): | ||
q2(~v.S, replace) << any_pair_bool(q @ A) | ||
v(q2.S) << True | ||
q(~v.S, replace) << any_pair_bool(A @ q) | ||
if q.nvals == 0: | ||
if q2.nvals == 0: | ||
break | ||
q, q2 = q2, q | ||
elif q2.nvals != 0: | ||
q << binary.any(q | q2) | ||
return v | ||
""" | ||
return _plain_bfs_bidirectional(G, next(iter(G))).nvals == len(G) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.