From 1f0481b032f1416c0469a0133f5944f4dcee4ee5 Mon Sep 17 00:00:00 2001 From: Jim Kitchen Date: Fri, 28 Oct 2022 16:53:17 -0500 Subject: [PATCH 1/4] More changes needed for NetworkX plugin - k-truss returns a normal Graph object - Rename convert to convert_from_nx - Add convert_to_nx method - Make algorithms accept Graph, Matrix, or Matrix-variant (expression, transpose, etc) --- graphblas_algorithms/classes/_utils.py | 4 +++- graphblas_algorithms/classes/digraph.py | 8 ++++++-- graphblas_algorithms/classes/graph.py | 5 ++++- graphblas_algorithms/interface.py | 10 +++++++++- graphblas_algorithms/nxapi/core.py | 4 +--- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/graphblas_algorithms/classes/_utils.py b/graphblas_algorithms/classes/_utils.py index 4c60998..ebb2e1c 100644 --- a/graphblas_algorithms/classes/_utils.py +++ b/graphblas_algorithms/classes/_utils.py @@ -19,7 +19,9 @@ def from_networkx(cls, G, weight=None, dtype=None): def from_graphblas(cls, A, *, key_to_id=None): - # Does not copy! + # Does not copy if A is a Matrix! + if type(A) is not Matrix: + A = A.new() if A.nrows != A.ncols: raise ValueError(f"Adjacency matrix must be square; got {A.nrows} x {A.ncols}") rv = cls() diff --git a/graphblas_algorithms/classes/digraph.py b/graphblas_algorithms/classes/digraph.py index 69b85be..76a4fb2 100644 --- a/graphblas_algorithms/classes/digraph.py +++ b/graphblas_algorithms/classes/digraph.py @@ -1,6 +1,8 @@ from collections import defaultdict from graphblas import Matrix, Vector, binary, replace, select, unary +from graphblas.core import utils as gbutils +from graphblas.core.matrix import TransposedMatrix import graphblas_algorithms as ga @@ -414,7 +416,8 @@ def to_directed_graph(G, weight=None, dtype=None): # We should do some sanity checks here to ensure we're returning a valid directed graph if isinstance(G, DiGraph): return G - if isinstance(G, Matrix): + typ = gbutils.output_type(G) + if typ in {Matrix, TransposedMatrix}: return DiGraph.from_graphblas(G) try: @@ -431,7 +434,8 @@ def to_directed_graph(G, weight=None, dtype=None): def to_graph(G, weight=None, dtype=None): if isinstance(G, (DiGraph, ga.Graph)): return G - if isinstance(G, Matrix): + typ = gbutils.output_type(G) + if typ in {Matrix, TransposedMatrix}: # Should we check if it can be undirected? return DiGraph.from_graphblas(G) diff --git a/graphblas_algorithms/classes/graph.py b/graphblas_algorithms/classes/graph.py index b679fa7..919fff5 100644 --- a/graphblas_algorithms/classes/graph.py +++ b/graphblas_algorithms/classes/graph.py @@ -1,6 +1,8 @@ from collections import defaultdict from graphblas import Matrix, Vector, select +from graphblas.core import utils as gbutils +from graphblas.core.matrix import TransposedMatrix import graphblas_algorithms as ga @@ -152,7 +154,8 @@ def to_undirected_graph(G, weight=None, dtype=None): # We should do some sanity checks here to ensure we're returning a valid undirected graph if isinstance(G, Graph): return G - if isinstance(G, Matrix): + typ = gbutils.output_type(G) + if typ in {Matrix, TransposedMatrix}: return Graph.from_graphblas(G) try: diff --git a/graphblas_algorithms/interface.py b/graphblas_algorithms/interface.py index da09ad7..a972718 100644 --- a/graphblas_algorithms/interface.py +++ b/graphblas_algorithms/interface.py @@ -69,7 +69,7 @@ class Dispatcher: is_triad = nxapi.triads.is_triad @staticmethod - def convert(graph, weight=None): + def convert_from_nx(graph, weight=None): from .classes import DiGraph, Graph, MultiDiGraph, MultiGraph if isinstance(graph, nx.MultiDiGraph): @@ -82,6 +82,14 @@ def convert(graph, weight=None): return Graph.from_networkx(graph, weight=weight) raise TypeError(f"Unsupported type of graph: {type(graph)}") + @staticmethod + def convert_to_nx(obj): + from .classes import Graph + + if isinstance(obj, Graph): + obj = obj.to_networkx() + return obj + @staticmethod def on_start_tests(items): skip = [ diff --git a/graphblas_algorithms/nxapi/core.py b/graphblas_algorithms/nxapi/core.py index 52e0f9e..7da6e81 100644 --- a/graphblas_algorithms/nxapi/core.py +++ b/graphblas_algorithms/nxapi/core.py @@ -10,6 +10,4 @@ def k_truss(G, k): G = to_undirected_graph(G, dtype=bool) result = algorithms.k_truss(G, k) - # TODO: don't convert to networkx graph - # We want to be able to pass networkx tests, so we need to improve our graph objects - return result.to_networkx() + return result From 0302bc04a14d2e3f04f2550a2a23189756034abf Mon Sep 17 00:00:00 2001 From: Jim Kitchen Date: Mon, 31 Oct 2022 13:49:27 -0500 Subject: [PATCH 2/4] Use ensure_type --- graphblas_algorithms/classes/_utils.py | 4 ++-- graphblas_algorithms/classes/digraph.py | 12 ++++++------ graphblas_algorithms/classes/graph.py | 7 +++---- graphblas_algorithms/interface.py | 4 ++-- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/graphblas_algorithms/classes/_utils.py b/graphblas_algorithms/classes/_utils.py index ebb2e1c..b1f9472 100644 --- a/graphblas_algorithms/classes/_utils.py +++ b/graphblas_algorithms/classes/_utils.py @@ -2,6 +2,7 @@ import numpy as np from graphblas import Matrix, Vector, binary from graphblas.core.matrix import TransposedMatrix +from graphblas.core.utils import ensure_type ################ # Classmethods # @@ -20,8 +21,7 @@ def from_networkx(cls, G, weight=None, dtype=None): def from_graphblas(cls, A, *, key_to_id=None): # Does not copy if A is a Matrix! - if type(A) is not Matrix: - A = A.new() + A = ensure_type(A, Matrix) if A.nrows != A.ncols: raise ValueError(f"Adjacency matrix must be square; got {A.nrows} x {A.ncols}") rv = cls() diff --git a/graphblas_algorithms/classes/digraph.py b/graphblas_algorithms/classes/digraph.py index 76a4fb2..7b6890d 100644 --- a/graphblas_algorithms/classes/digraph.py +++ b/graphblas_algorithms/classes/digraph.py @@ -1,8 +1,6 @@ from collections import defaultdict from graphblas import Matrix, Vector, binary, replace, select, unary -from graphblas.core import utils as gbutils -from graphblas.core.matrix import TransposedMatrix import graphblas_algorithms as ga @@ -416,9 +414,10 @@ def to_directed_graph(G, weight=None, dtype=None): # We should do some sanity checks here to ensure we're returning a valid directed graph if isinstance(G, DiGraph): return G - typ = gbutils.output_type(G) - if typ in {Matrix, TransposedMatrix}: + try: return DiGraph.from_graphblas(G) + except TypeError: + pass try: import networkx as nx @@ -434,10 +433,11 @@ def to_directed_graph(G, weight=None, dtype=None): def to_graph(G, weight=None, dtype=None): if isinstance(G, (DiGraph, ga.Graph)): return G - typ = gbutils.output_type(G) - if typ in {Matrix, TransposedMatrix}: + try: # Should we check if it can be undirected? return DiGraph.from_graphblas(G) + except TypeError: + pass try: import networkx as nx diff --git a/graphblas_algorithms/classes/graph.py b/graphblas_algorithms/classes/graph.py index 919fff5..bc4ba24 100644 --- a/graphblas_algorithms/classes/graph.py +++ b/graphblas_algorithms/classes/graph.py @@ -1,8 +1,6 @@ from collections import defaultdict from graphblas import Matrix, Vector, select -from graphblas.core import utils as gbutils -from graphblas.core.matrix import TransposedMatrix import graphblas_algorithms as ga @@ -154,9 +152,10 @@ def to_undirected_graph(G, weight=None, dtype=None): # We should do some sanity checks here to ensure we're returning a valid undirected graph if isinstance(G, Graph): return G - typ = gbutils.output_type(G) - if typ in {Matrix, TransposedMatrix}: + try: return Graph.from_graphblas(G) + except TypeError: + pass try: import networkx as nx diff --git a/graphblas_algorithms/interface.py b/graphblas_algorithms/interface.py index a972718..a12269c 100644 --- a/graphblas_algorithms/interface.py +++ b/graphblas_algorithms/interface.py @@ -69,7 +69,7 @@ class Dispatcher: is_triad = nxapi.triads.is_triad @staticmethod - def convert_from_nx(graph, weight=None): + def convert_from_nx(graph, weight=None, *, name): from .classes import DiGraph, Graph, MultiDiGraph, MultiGraph if isinstance(graph, nx.MultiDiGraph): @@ -83,7 +83,7 @@ def convert_from_nx(graph, weight=None): raise TypeError(f"Unsupported type of graph: {type(graph)}") @staticmethod - def convert_to_nx(obj): + def convert_to_nx(obj, *, name): from .classes import Graph if isinstance(obj, Graph): From 8299883e0614e18ac935edc623f96dcde22f4115 Mon Sep 17 00:00:00 2001 From: Jim Kitchen Date: Tue, 1 Nov 2022 14:58:57 -0500 Subject: [PATCH 3/4] Make name option in convert_??_nx functions --- graphblas_algorithms/interface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graphblas_algorithms/interface.py b/graphblas_algorithms/interface.py index a12269c..02474d5 100644 --- a/graphblas_algorithms/interface.py +++ b/graphblas_algorithms/interface.py @@ -69,7 +69,7 @@ class Dispatcher: is_triad = nxapi.triads.is_triad @staticmethod - def convert_from_nx(graph, weight=None, *, name): + def convert_from_nx(graph, weight=None, *, name=None): from .classes import DiGraph, Graph, MultiDiGraph, MultiGraph if isinstance(graph, nx.MultiDiGraph): @@ -83,7 +83,7 @@ def convert_from_nx(graph, weight=None, *, name): raise TypeError(f"Unsupported type of graph: {type(graph)}") @staticmethod - def convert_to_nx(obj, *, name): + def convert_to_nx(obj, *, name=None): from .classes import Graph if isinstance(obj, Graph): From e48cec885d9cea3aeff22f21ed570c13351d2baf Mon Sep 17 00:00:00 2001 From: Jim Kitchen Date: Tue, 1 Nov 2022 15:11:58 -0500 Subject: [PATCH 4/4] Use networkx branch with changes so tests can pass --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 71c1f01..17321e1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,7 +33,7 @@ jobs: conda install -c conda-forge python-graphblas scipy pandas \ pytest-cov pytest-randomly black flake8-comprehensions flake8-bugbear # matplotlib lxml pygraphviz pydot sympy # Extra networkx deps we don't need yet - pip install git+https://github.com/mriduls/networkx.git@nx-sparse --no-deps + pip install git+https://github.com/jim22k/networkx.git@nx-sparse --no-deps pip install -e . --no-deps - name: Style checks run: |