Skip to content

Adding backend to pydatastructs APIs #435

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

Merged
merged 6 commits into from
Nov 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions pydatastructs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
__version__ = "1.0.1-dev"

from .linear_data_structures import *
from .trees import *
from .miscellaneous_data_structures import *
from .utils import *
from .graphs import *
from .strings import *

__version__ = "1.0.1-dev"
8 changes: 5 additions & 3 deletions pydatastructs/graphs/adjacency_list.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from pydatastructs.graphs.graph import Graph
from pydatastructs.linear_data_structures import DynamicOneDimensionalArray
from pydatastructs.utils.misc_util import GraphEdge
from pydatastructs.utils.misc_util import (
GraphEdge, Backend, raise_if_backend_is_not_python)

__all__ = [
'AdjacencyList'
Expand All @@ -15,7 +15,9 @@ class AdjacencyList(Graph):

pydatastructs.graphs.graph.Graph
"""
def __new__(cls, *vertices):
def __new__(cls, *vertices, **kwargs):
raise_if_backend_is_not_python(
cls, kwargs.get('backend', Backend.PYTHON))
obj = object.__new__(cls)
for vertex in vertices:
obj.__setattr__(vertex.name, vertex)
Expand Down
9 changes: 6 additions & 3 deletions pydatastructs/graphs/adjacency_matrix.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from pydatastructs.graphs.graph import Graph
from pydatastructs.linear_data_structures import OneDimensionalArray
from pydatastructs.utils.misc_util import AdjacencyMatrixGraphNode, GraphEdge
from pydatastructs.utils.misc_util import (
GraphEdge, raise_if_backend_is_not_python,
Backend)

__all__ = [
'AdjacencyMatrix'
Expand All @@ -15,7 +16,9 @@ class AdjacencyMatrix(Graph):

pydatastructs.graphs.graph.Graph
"""
def __new__(cls, *vertices):
def __new__(cls, *vertices, **kwargs):
raise_if_backend_is_not_python(
cls, kwargs.get('backend', Backend.PYTHON))
obj = object.__new__(cls)
obj.vertices = [vertex.name for vertex in vertices]
for vertex in vertices:
Expand Down
82 changes: 73 additions & 9 deletions pydatastructs/graphs/algorithms.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"""
from collections import deque
from concurrent.futures import ThreadPoolExecutor
from pydatastructs.utils import GraphEdge
from pydatastructs.utils.misc_util import _comp
from pydatastructs.utils.misc_util import (
_comp, raise_if_backend_is_not_python, Backend)
from pydatastructs.miscellaneous_data_structures import (
DisjointSetForest, PriorityQueue)
from pydatastructs.graphs.graph import Graph
Expand Down Expand Up @@ -51,6 +51,10 @@ def breadth_first_search(
current node and the node next to current node.
The rest of the arguments are optional and you can
provide your own stuff there.
backend: pydatastructs.Backend
The backend to be used.
Optional, by default, the best available
backend is used.

Note
====
Expand All @@ -75,6 +79,8 @@ def breadth_first_search(
>>> G.add_edge(V2.name, V3.name)
>>> breadth_first_search(G, V1.name, f, V3.name)
"""
raise_if_backend_is_not_python(
breadth_first_search, kwargs.get('backend', Backend.PYTHON))
import pydatastructs.graphs.algorithms as algorithms
func = "_breadth_first_search_" + graph._impl
if not hasattr(algorithms, func):
Expand Down Expand Up @@ -133,6 +139,10 @@ def breadth_first_search_parallel(
current node and the node next to current node.
The rest of the arguments are optional and you can
provide your own stuff there.
backend: pydatastructs.Backend
The backend to be used.
Optional, by default, the best available
backend is used.

Note
====
Expand All @@ -157,6 +167,8 @@ def breadth_first_search_parallel(
>>> G.add_edge(V2.name, V3.name)
>>> breadth_first_search_parallel(G, V1.name, 3, f, V3.name)
"""
raise_if_backend_is_not_python(
breadth_first_search_parallel, kwargs.get('backend', Backend.PYTHON))
import pydatastructs.graphs.algorithms as algorithms
func = "_breadth_first_search_parallel_" + graph._impl
if not hasattr(algorithms, func):
Expand Down Expand Up @@ -256,7 +268,7 @@ def _minimum_spanning_tree_prim_adjacency_list(graph):
e[w] = vw
return mst

def minimum_spanning_tree(graph, algorithm):
def minimum_spanning_tree(graph, algorithm, **kwargs):
"""
Computes a minimum spanning tree for the given
graph and algorithm.
Expand All @@ -276,6 +288,10 @@ def minimum_spanning_tree(graph, algorithm):
'kruskal' -> Kruskal's algorithm as given in [1].

'prim' -> Prim's algorithm as given in [2].
backend: pydatastructs.Backend
The backend to be used.
Optional, by default, the best available
backend is used.

Returns
=======
Expand Down Expand Up @@ -312,6 +328,8 @@ def minimum_spanning_tree(graph, algorithm):
should be used only for such graphs. Using with other
types of graphs may lead to unwanted results.
"""
raise_if_backend_is_not_python(
minimum_spanning_tree, kwargs.get('backend', Backend.PYTHON))
import pydatastructs.graphs.algorithms as algorithms
func = "_minimum_spanning_tree_" + algorithm + "_" + graph._impl
if not hasattr(algorithms, func):
Expand Down Expand Up @@ -390,7 +408,7 @@ def _minimum_spanning_tree_parallel_prim_adjacency_list(graph, num_threads):

return mst

def minimum_spanning_tree_parallel(graph, algorithm, num_threads):
def minimum_spanning_tree_parallel(graph, algorithm, num_threads, **kwargs):
"""
Computes a minimum spanning tree for the given
graph and algorithm using the given number of threads.
Expand All @@ -412,6 +430,10 @@ def minimum_spanning_tree_parallel(graph, algorithm, num_threads):
'prim' -> Prim's algorithm as given in [2].
num_threads: int
The number of threads to be used.
backend: pydatastructs.Backend
The backend to be used.
Optional, by default, the best available
backend is used.

Returns
=======
Expand Down Expand Up @@ -448,6 +470,8 @@ def minimum_spanning_tree_parallel(graph, algorithm, num_threads):
should be used only for such graphs. Using with other
types of graphs will lead to unwanted results.
"""
raise_if_backend_is_not_python(
minimum_spanning_tree_parallel, kwargs.get('backend', Backend.PYTHON))
import pydatastructs.graphs.algorithms as algorithms
func = "_minimum_spanning_tree_parallel_" + algorithm + "_" + graph._impl
if not hasattr(algorithms, func):
Expand Down Expand Up @@ -505,7 +529,7 @@ def _strongly_connected_components_kosaraju_adjacency_list(graph):
_strongly_connected_components_kosaraju_adjacency_matrix = \
_strongly_connected_components_kosaraju_adjacency_list

def strongly_connected_components(graph, algorithm):
def strongly_connected_components(graph, algorithm, **kwargs):
"""
Computes strongly connected components for the given
graph and algorithm.
Expand All @@ -523,6 +547,10 @@ def strongly_connected_components(graph, algorithm):
supported,

'kosaraju' -> Kosaraju's algorithm as given in [1].
backend: pydatastructs.Backend
The backend to be used.
Optional, by default, the best available
backend is used.

Returns
=======
Expand Down Expand Up @@ -550,6 +578,8 @@ def strongly_connected_components(graph, algorithm):
.. [1] https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm

"""
raise_if_backend_is_not_python(
strongly_connected_components, kwargs.get('backend', Backend.PYTHON))
import pydatastructs.graphs.algorithms as algorithms
func = "_strongly_connected_components_" + algorithm + "_" + graph._impl
if not hasattr(algorithms, func):
Expand Down Expand Up @@ -583,6 +613,10 @@ def depth_first_search(
current node and the node next to current node.
The rest of the arguments are optional and you can
provide your own stuff there.
backend: pydatastructs.Backend
The backend to be used.
Optional, by default, the best available
backend is used.

Note
====
Expand Down Expand Up @@ -612,6 +646,8 @@ def depth_first_search(

.. [1] https://en.wikipedia.org/wiki/Depth-first_search
"""
raise_if_backend_is_not_python(
depth_first_search, kwargs.get('backend', Backend.PYTHON))
import pydatastructs.graphs.algorithms as algorithms
func = "_depth_first_search_" + graph._impl
if not hasattr(algorithms, func):
Expand Down Expand Up @@ -646,7 +682,8 @@ def _depth_first_search_adjacency_list(
_depth_first_search_adjacency_matrix = _depth_first_search_adjacency_list

def shortest_paths(graph: Graph, algorithm: str,
source: str, target: str="") -> tuple:
source: str, target: str="",
**kwargs) -> tuple:
"""
Finds shortest paths in the given graph from a given source.

Expand All @@ -668,6 +705,10 @@ def shortest_paths(graph: Graph, algorithm: str,
The name of the target node.
Optional, by default, all pair shortest paths
are returned.
backend: pydatastructs.Backend
The backend to be used.
Optional, by default, the best available
backend is used.

Returns
=======
Expand Down Expand Up @@ -701,6 +742,8 @@ def shortest_paths(graph: Graph, algorithm: str,
.. [1] https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm
.. [2] https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
"""
raise_if_backend_is_not_python(
shortest_paths, kwargs.get('backend', Backend.PYTHON))
import pydatastructs.graphs.algorithms as algorithms
func = "_" + algorithm + "_" + graph._impl
if not hasattr(algorithms, func):
Expand Down Expand Up @@ -767,7 +810,8 @@ def _dijkstra_adjacency_list(graph: Graph, start: str, target: str):

_dijkstra_adjacency_matrix = _dijkstra_adjacency_list

def all_pair_shortest_paths(graph: Graph, algorithm: str) -> tuple:
def all_pair_shortest_paths(graph: Graph, algorithm: str,
**kwargs) -> tuple:
"""
Finds shortest paths between all pairs of vertices in the given graph.

Expand All @@ -781,6 +825,10 @@ def all_pair_shortest_paths(graph: Graph, algorithm: str) -> tuple:
are implemented,

'floyd_warshall' -> Floyd Warshall algorithm as given in [1].
backend: pydatastructs.Backend
The backend to be used.
Optional, by default, the best available
backend is used.

Returns
=======
Expand Down Expand Up @@ -810,6 +858,8 @@ def all_pair_shortest_paths(graph: Graph, algorithm: str) -> tuple:

.. [1] https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm
"""
raise_if_backend_is_not_python(
all_pair_shortest_paths, kwargs.get('backend', Backend.PYTHON))
import pydatastructs.graphs.algorithms as algorithms
func = "_" + algorithm + "_" + graph._impl
if not hasattr(algorithms, func):
Expand Down Expand Up @@ -849,7 +899,8 @@ def _floyd_warshall_adjacency_list(graph: Graph):

_floyd_warshall_adjacency_matrix = _floyd_warshall_adjacency_list

def topological_sort(graph: Graph, algorithm: str) -> list:
def topological_sort(graph: Graph, algorithm: str,
**kwargs) -> list:
"""
Performs topological sort on the given graph using given algorithm.

Expand All @@ -863,6 +914,10 @@ def topological_sort(graph: Graph, algorithm: str) -> list:
Currently, following are supported,

'kahn' -> Kahn's algorithm as given in [1].
backend: pydatastructs.Backend
The backend to be used.
Optional, by default, the best available
backend is used.

Returns
=======
Expand All @@ -886,6 +941,8 @@ def topological_sort(graph: Graph, algorithm: str) -> list:

.. [1] https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm
"""
raise_if_backend_is_not_python(
topological_sort, kwargs.get('backend', Backend.PYTHON))
import pydatastructs.graphs.algorithms as algorithms
func = "_" + algorithm + "_" + graph._impl
if not hasattr(algorithms, func):
Expand Down Expand Up @@ -920,7 +977,8 @@ def _kahn_adjacency_list(graph: Graph) -> list:
raise ValueError("Graph is not acyclic.")
return L

def topological_sort_parallel(graph: Graph, algorithm: str, num_threads: int) -> list:
def topological_sort_parallel(graph: Graph, algorithm: str, num_threads: int,
**kwargs) -> list:
"""
Performs topological sort on the given graph using given algorithm using
given number of threads.
Expand All @@ -937,6 +995,10 @@ def topological_sort_parallel(graph: Graph, algorithm: str, num_threads: int) ->
'kahn' -> Kahn's algorithm as given in [1].
num_threads: int
The maximum number of threads to be used.
backend: pydatastructs.Backend
The backend to be used.
Optional, by default, the best available
backend is used.

Returns
=======
Expand All @@ -960,6 +1022,8 @@ def topological_sort_parallel(graph: Graph, algorithm: str, num_threads: int) ->

.. [1] https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm
"""
raise_if_backend_is_not_python(
topological_sort_parallel, kwargs.get('backend', Backend.PYTHON))
import pydatastructs.graphs.algorithms as algorithms
func = "_" + algorithm + "_" + graph._impl + '_parallel'
if not hasattr(algorithms, func):
Expand Down
8 changes: 8 additions & 0 deletions pydatastructs/graphs/graph.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

from pydatastructs.utils.misc_util import Backend, raise_if_backend_is_not_python

__all__ = [
'Graph'
]
Expand All @@ -24,6 +26,10 @@ class Graph(object):
vertices: GraphNode(s)
For AdjacencyList implementation vertices
can be passed for initializing the graph.
backend: pydatastructs.Backend
The backend to be used.
Optional, by default, the best available
backend is used.

Examples
========
Expand Down Expand Up @@ -64,6 +70,8 @@ class Graph(object):
__slots__ = ['_impl']

def __new__(cls, *args, **kwargs):
raise_if_backend_is_not_python(
cls, kwargs.get('backend', Backend.PYTHON))
default_impl = args[0]._impl if args else 'adjacency_list'
implementation = kwargs.get('implementation', default_impl)
if implementation == 'adjacency_list':
Expand Down
Loading