Skip to content

Commit

Permalink
Add and fix a couple generators
Browse files Browse the repository at this point in the history
  • Loading branch information
eriknw committed Oct 26, 2023
1 parent 13c8a6c commit 98e87a7
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 67 deletions.
2 changes: 2 additions & 0 deletions python/nx-cugraph/_nx_cugraph/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"circular_ladder_graph",
"complete_bipartite_graph",
"complete_graph",
"complete_multipartite_graph",
"cubical_graph",
"cycle_graph",
"davis_southern_women_graph",
Expand Down Expand Up @@ -72,6 +73,7 @@
"trivial_graph",
"truncated_cube_graph",
"truncated_tetrahedron_graph",
"turan_graph",
"tutte_graph",
"wheel_graph",
# END: functions
Expand Down
25 changes: 13 additions & 12 deletions python/nx-cugraph/nx_cugraph/algorithms/bipartite/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
# 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 numbers import Integral

import cupy as cp
import networkx as nx
import numpy as np
Expand All @@ -28,6 +30,8 @@ def complete_bipartite_graph(n1, n2, create_using=None):
graph_class, inplace = _create_using_class(create_using)
if graph_class.is_directed():
raise nx.NetworkXError("Directed Graph not supported")
orig_n1, unused_nodes1 = n1
orig_n2, unused_nodes2 = n2
n1, nodes1 = _number_and_nodes(n1)
n2, nodes2 = _number_and_nodes(n2)
all_indices = cp.indices((n1, n2), dtype=index_dtype)
Expand All @@ -37,24 +41,21 @@ def complete_bipartite_graph(n1, n2, create_using=None):
src_indices = cp.hstack((indices0, indices1))
dst_indices = cp.hstack((indices1, indices0))
bipartite = cp.zeros(n1 + n2, np.int8)
bipartite[n1 : n1 + n2] = 1
if nodes1 is None:
if nodes2 is None:
nodes = None
else:
nodes = list(range(n1))
nodes.extend(nodes2)
bipartite[n1:] = 1
if isinstance(orig_n1, Integral) and isinstance(orig_n2, Integral):
nodes = None
else:
nodes = nodes1
nodes.extend(range(n1, n1 + n2) if nodes2 is None else nodes2)
if nodes is not None and len(set(nodes)) != len(nodes):
raise nx.NetworkXError("Inputs n1 and n2 must contain distinct nodes")
nodes = list(range(n1)) if nodes1 is None else nodes1
nodes.extend(range(n2) if nodes2 is None else nodes2)
if len(set(nodes)) != len(nodes):
raise nx.NetworkXError("Inputs n1 and n2 must contain distinct nodes")
G = graph_class.from_coo(
n1 + n2,
src_indices,
dst_indices,
node_values={"bipartite": bipartite},
name=f"complete_bipartite_graph({n1}, {n2})",
id_to_key=nodes,
name=f"complete_bipartite_graph({orig_n1}, {orig_n2})",
)
if inplace:
return create_using._become(G)
Expand Down
7 changes: 6 additions & 1 deletion python/nx-cugraph/nx_cugraph/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,14 +483,17 @@ def _iter_attr_dicts(
return full_dicts


def to_networkx(G: nxcg.Graph) -> nx.Graph:
def to_networkx(G: nxcg.Graph, *, sort_edges: bool = True) -> nx.Graph:
"""Convert a nx_cugraph graph to networkx graph.
All edge and node attributes and ``G.graph`` properties are converted.
Parameters
----------
G : nx_cugraph.Graph
sort_edges : bool, default True
Whether to sort the edge data of the input graph by (src, dst) indices
before converting. This can be useful for consistent conversions.
Returns
-------
Expand All @@ -502,6 +505,8 @@ def to_networkx(G: nxcg.Graph) -> nx.Graph:
"""
rv = G.to_networkx_class()()
id_to_key = G.id_to_key
if sort_edges:
G._sort_edge_indices()

node_values = G.node_values
node_masks = G.node_masks
Expand Down
17 changes: 13 additions & 4 deletions python/nx-cugraph/nx_cugraph/generators/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,15 @@ def _create_using_class(create_using, *, default=nxcg.Graph):
create_using, "is_multigraph"
):
raise TypeError("create_using is not a valid graph type or instance")
elif not isinstance(create_using, nxcg.Graph):
raise NotImplementedError(
f"create_using with object of type {type(create_using)} is not supported "
"by the cugraph backend; only nx_cugraph.Graph objects are allowed."
)
else:
if isinstance(create_using, nxcg.Graph):
create_using.clear()
inplace = True
# What does the user intend if they give us a graph object not from nx-cugraph?
inplace = True
G = create_using
G.clear()
if not isinstance(G, nxcg.Graph):
if G.is_multigraph():
if G.is_directed():
Expand All @@ -96,6 +99,12 @@ def _create_using_class(create_using, *, default=nxcg.Graph):
graph_class = nxcg.DiGraph
else:
graph_class = nxcg.Graph
if G.__class__ not in {nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph}:
raise NotImplementedError(
f"create_using with type {type(G)} is not supported by the cugraph "
"backend; only standard networkx or nx_cugraph Graph objects are "
"allowed (but not customized subclasses derived from them)."
)
else:
graph_class = G.__class__
return graph_class, inplace
Expand Down
96 changes: 79 additions & 17 deletions python/nx-cugraph/nx_cugraph/generators/classic.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,21 @@
# 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
from numbers import Integral

import cupy as cp
import networkx as nx
import numpy as np

import nx_cugraph as nxcg

from ..utils import index_dtype, networkx_algorithm, nodes_or_number
from ._utils import (
_common_small_graph,
_complete_graph_indices,
_create_using_class,
_ensure_int,
_ensure_nonnegative_int,
_number_and_nodes,
)
Expand All @@ -26,6 +33,7 @@
"barbell_graph",
"circular_ladder_graph",
"complete_graph",
"complete_multipartite_graph",
"cycle_graph",
"empty_graph",
"ladder_graph",
Expand All @@ -34,9 +42,12 @@
"path_graph",
"star_graph",
"trivial_graph",
"turan_graph",
"wheel_graph",
]

concat = itertools.chain.from_iterable


@networkx_algorithm
def barbell_graph(m1, m2, create_using=None):
Expand Down Expand Up @@ -87,6 +98,46 @@ def complete_graph(n, create_using=None):
return G


@networkx_algorithm
def complete_multipartite_graph(*subset_sizes):
if not subset_sizes:
return nxcg.Graph()
try:
subset_sizes = [_ensure_int(size) for size in subset_sizes]
except TypeError:
subsets = [list(subset) for subset in subset_sizes]
subset_sizes = [len(subset) for subset in subsets]
nodes = list(concat(subsets))
else:
subsets = nodes = None
subset_sizes = [_ensure_nonnegative_int(size) for size in subset_sizes]
L1 = []
L2 = []
total = 0
for size in subset_sizes:
all_indices = cp.indices((total, size), dtype=index_dtype)
L1.append(all_indices[0].ravel())
L2.append(all_indices[1].ravel() + total)
total += size
src_indices = cp.hstack(L1 + L2)
dst_indices = cp.hstack(L2 + L1)
subsets_array = cp.array(
np.repeat(
np.arange(
len(subset_sizes), dtype=np.min_scalar_type(len(subset_sizes) - 1)
),
subset_sizes,
)
)
return nxcg.Graph.from_coo(
subsets_array.size,
src_indices,
dst_indices,
node_values={"subset": subsets_array},
id_to_key=nodes,
)


@nodes_or_number(0)
@networkx_algorithm
def cycle_graph(n, create_using=None):
Expand Down Expand Up @@ -190,6 +241,8 @@ def ladder_graph(n, create_using=None):
@networkx_algorithm
def lollipop_graph(m, n, create_using=None):
# Like complete_graph then path_graph
orig_m, unused_nodes1 = m
orig_n, unused_nodes2 = n
m, m_nodes = _number_and_nodes(m)
if m < 2:
raise nx.NetworkXError(
Expand All @@ -207,17 +260,13 @@ def lollipop_graph(m, n, create_using=None):
).ravel()[1:-1]
src_indices = cp.hstack((msrc_indices, nsrc_indices))
dst_indices = cp.hstack((mdst_indices, ndst_indices))
if m_nodes is None:
if n_nodes is None:
nodes = None
else:
nodes = list(range(m))
nodes.extend(n_nodes)
if isinstance(orig_m, Integral) and isinstance(orig_n, Integral):
nodes = None
else:
nodes = m_nodes
nodes.extend(range(m, m + n) if n_nodes is None else n_nodes)
if nodes is not None and len(set(nodes)) != len(nodes):
raise nx.NetworkXError("Nodes must be distinct in containers m and n")
nodes = list(range(m)) if m_nodes is None else m_nodes
nodes.extend(range(n) if n_nodes is None else n_nodes)
if len(set(nodes)) != len(nodes):
raise nx.NetworkXError("Nodes must be distinct in containers m and n")
G = graph_class.from_coo(m + n, src_indices, dst_indices, id_to_key=nodes)
if inplace:
return create_using._become(G)
Expand Down Expand Up @@ -253,19 +302,23 @@ def path_graph(n, create_using=None):
@nodes_or_number(0)
@networkx_algorithm
def star_graph(n, create_using=None):
orig_n, orig_nodes = n
n, nodes = _number_and_nodes(n)
if nodes is not None:
nodes.append(n)
if n < 2:
return _common_small_graph(n + 1, nodes, create_using, allow_directed=False)
# star_graph behaves differently whether the input was an int or iterable
if isinstance(orig_n, Integral):
if nodes is not None:
nodes.append(n)
n += 1
if n < 3:
return _common_small_graph(n, nodes, create_using, allow_directed=False)
graph_class, inplace = _create_using_class(create_using)
if graph_class.is_directed():
raise nx.NetworkXError("Directed Graph not supported")
flat = cp.zeros(n, index_dtype)
ramp = cp.arange(1, n + 1, dtype=index_dtype)
flat = cp.zeros(n - 1, index_dtype)
ramp = cp.arange(1, n, dtype=index_dtype)
src_indices = cp.hstack((flat, ramp))
dst_indices = cp.hstack((ramp, flat))
G = graph_class.from_coo(n + 1, src_indices, dst_indices, id_to_key=nodes)
G = graph_class.from_coo(n, src_indices, dst_indices, id_to_key=nodes)
if inplace:
return create_using._become(G)
return G
Expand All @@ -276,6 +329,15 @@ def trivial_graph(create_using=None):
return _common_small_graph(1, None, create_using)


@networkx_algorithm
def turan_graph(n, r):
if not 1 <= r <= n:
raise nx.NetworkXError("Must satisfy 1 <= r <= n")
n_div_r, n_mod_r = divmod(n, r)
partitions = [n_div_r] * (r - n_mod_r) + [n_div_r + 1] * n_mod_r
return complete_multipartite_graph(*partitions)


@nodes_or_number(0)
@networkx_algorithm
def wheel_graph(n, create_using=None):
Expand Down
50 changes: 21 additions & 29 deletions python/nx-cugraph/nx_cugraph/generators/social.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,31 +30,31 @@ def davis_southern_women_graph():
# fmt: off
src_indices = cp.array(
[
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4,
4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9,
9, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 13, 13, 13,
14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16,
16, 17, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 19, 20, 20, 20, 20, 21,
21, 21, 21, 21, 21, 22, 22, 22, 23, 23, 23, 24, 24, 24, 25, 25, 25, 25,
25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28,
28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31,
31, 31, 31, 31,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3,
3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8,
8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12,
12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 15, 15,
16, 16, 17, 17, 18, 18, 18, 19, 19, 19, 20, 20, 20, 20, 20, 20, 21, 21,
21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25,
25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
27, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 30, 30, 30,
31, 31, 31,
],
index_dtype,
)
dst_indices = cp.array(
[
18, 25, 26, 27, 28, 29, 30, 25, 26, 27, 29, 30, 31, 27, 28, 29, 30, 18,
24, 25, 26, 27, 28, 30, 31, 20, 31, 25, 27, 28, 30, 19, 20, 21, 29, 30,
19, 21, 22, 23, 30, 31, 18, 24, 25, 27, 28, 29, 30, 19, 21, 30, 31, 19,
20, 21, 22, 23, 28, 29, 31, 20, 31, 28, 30, 31, 27, 29, 30, 31, 19, 21,
22, 23, 29, 30, 31, 24, 25, 26, 27, 28, 29, 30, 31, 21, 29, 30, 31, 0, 4,
9, 7, 8, 10, 11, 15, 5, 7, 11, 12, 7, 8, 10, 11, 15, 17, 8, 11, 15, 8,
11, 15, 4, 9, 16, 0, 1, 4, 6, 9, 16, 0, 1, 4, 16, 0, 1, 3, 4, 6, 9, 14,
16, 0, 3, 4, 6, 9, 11, 13, 16, 0, 1, 3, 7, 9, 11, 14, 15, 16, 17, 0, 2,
3, 4, 6, 7, 8, 9, 10, 13, 14, 15, 16, 17, 2, 4, 5, 8, 10, 11, 12, 13, 14,
15, 16, 17,
18, 19, 20, 21, 22, 23, 25, 26, 18, 19, 20, 22, 23, 24, 25, 19, 20, 21,
22, 23, 24, 25, 26, 18, 20, 21, 22, 23, 24, 25, 20, 21, 22, 24, 20, 22,
23, 25, 22, 23, 24, 25, 23, 25, 26, 22, 24, 25, 26, 24, 25, 26, 29, 25,
26, 27, 29, 25, 26, 27, 29, 30, 31, 24, 25, 26, 27, 29, 30, 31, 23, 24,
26, 27, 28, 29, 30, 31, 24, 25, 27, 28, 29, 25, 26, 26, 28, 26, 28, 0, 1,
3, 0, 1, 2, 0, 1, 2, 3, 4, 5, 0, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 8, 0, 1,
2, 3, 5, 6, 7, 13, 1, 2, 3, 4, 6, 8, 9, 12, 13, 14, 0, 1, 2, 3, 5, 6, 7,
8, 9, 10, 11, 12, 14, 15, 0, 2, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 10,
11, 12, 13, 14, 13, 14, 16, 17, 9, 10, 11, 12, 13, 14, 11, 12, 13, 11,
12, 13,
],
index_dtype,
)
Expand All @@ -65,14 +65,6 @@ def davis_southern_women_graph():
],
np.int8,
)
nodes = [
"Brenda Rogers", "Charlotte McDowd", "Dorothy Murchison", "Eleanor Nye",
"Evelyn Jefferson", "Flora Price", "Frances Anderson", "Helen Lloyd",
"Katherina Rogers", "Laura Mandeville", "Myra Liddel", "Nora Fayette",
"Olivia Carleton", "Pearl Oglethorpe", "Ruth DeSand", "Sylvia Avondale",
"Theresa Anderson", "Verne Sanderson", "E1", "E10", "E11", "E12", "E13",
"E14", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9",
]
women = [
"Evelyn Jefferson", "Laura Mandeville", "Theresa Anderson", "Brenda Rogers",
"Charlotte McDowd", "Frances Anderson", "Eleanor Nye", "Pearl Oglethorpe",
Expand All @@ -90,7 +82,7 @@ def davis_southern_women_graph():
src_indices,
dst_indices,
node_values={"bipartite": bipartite},
id_to_key=nodes,
id_to_key=women + events,
top=women,
bottom=events,
)
Expand Down
Loading

0 comments on commit 98e87a7

Please sign in to comment.