From b2b04ecfda44f29a0becbe0b0f94ae0710738070 Mon Sep 17 00:00:00 2001 From: arvinder004 Date: Wed, 2 Apr 2025 15:55:06 +0530 Subject: [PATCH 1/5] added c++ backend for bfs algorithm --- pydatastructs/graphs/__init__.py | 8 ++- pydatastructs/graphs/_backend/__init__.py | 17 +++++ .../graphs/_backend/cpp/algorithms.cpp | 21 ++++++ pydatastructs/graphs/_backend/cpp/bfs.hpp | 64 +++++++++++++++++++ pydatastructs/graphs/_extensions.py | 17 +++++ pydatastructs/graphs/algorithms.py | 19 +++++- pydatastructs/graphs/tests/test_algorithms.py | 19 +++++- setup.py | 2 + 8 files changed, 163 insertions(+), 4 deletions(-) create mode 100644 pydatastructs/graphs/_backend/__init__.py create mode 100644 pydatastructs/graphs/_backend/cpp/algorithms.cpp create mode 100644 pydatastructs/graphs/_backend/cpp/bfs.hpp create mode 100644 pydatastructs/graphs/_extensions.py diff --git a/pydatastructs/graphs/__init__.py b/pydatastructs/graphs/__init__.py index 21e0a5f35..502fd7dcb 100644 --- a/pydatastructs/graphs/__init__.py +++ b/pydatastructs/graphs/__init__.py @@ -1,6 +1,9 @@ __all__ = [] -from . import graph +from . import ( + graph, + _extensions +) from .graph import ( Graph ) @@ -22,7 +25,8 @@ topological_sort, topological_sort_parallel, max_flow, - find_bridges + find_bridges, + bfs ) __all__.extend(algorithms.__all__) diff --git a/pydatastructs/graphs/_backend/__init__.py b/pydatastructs/graphs/_backend/__init__.py new file mode 100644 index 000000000..6707a0307 --- /dev/null +++ b/pydatastructs/graphs/_backend/__init__.py @@ -0,0 +1,17 @@ +from setuptools import setup, Extension +import sysconfig + +bfs_dfs_module = Extension( + '_bfs_dfs', # Module name + sources=['src/cpp/bfs_dfs.cpp'], + include_dirs=[sysconfig.get_path('include')], + extra_compile_args=['-std=c++11'], +) + +setup( + name='my_pydatastructs', + version='0.1', + package_dir={'': 'src/python'}, + py_modules=['graph_algorithms'], + ext_modules=[bfs_dfs_module], +) \ No newline at end of file diff --git a/pydatastructs/graphs/_backend/cpp/algorithms.cpp b/pydatastructs/graphs/_backend/cpp/algorithms.cpp new file mode 100644 index 000000000..7064bf677 --- /dev/null +++ b/pydatastructs/graphs/_backend/cpp/algorithms.cpp @@ -0,0 +1,21 @@ +#include +#include "bfs.hpp" + +static PyMethodDef bfs_PyMethodDef[] = { + {"bfs", (PyCFunction)bfs, METH_VARARGS | METH_KEYWORDS, "Breadth-First Search"}, + {NULL, NULL, 0, NULL} +}; + +static struct PyModuleDef bfs_module = { + PyModuleDef_HEAD_INIT, + "bfs", + "BFS algorithms module", + -1, + bfs_PyMethodDef +}; + +PyMODINIT_FUNC PyInit_bfs(void) { + PyObject *module = PyModule_Create(&bfs_module); + if (module == NULL) return NULL; + return module; +} \ No newline at end of file diff --git a/pydatastructs/graphs/_backend/cpp/bfs.hpp b/pydatastructs/graphs/_backend/cpp/bfs.hpp new file mode 100644 index 000000000..5cc92bdeb --- /dev/null +++ b/pydatastructs/graphs/_backend/cpp/bfs.hpp @@ -0,0 +1,64 @@ +#ifndef BFS_HPP +#define BFS_HPP + +#define PY_SSIZE_T_CLEAN +#include +#include +#include + +struct Graph { + PyObject* adj_list; +}; + +static PyObject* bfs_impl(PyObject* graph, PyObject* start_vertex, PyObject* visited = NULL) { + if (!PyDict_Check(graph)) { + PyErr_SetString(PyExc_TypeError, "Graph must be a dictionary"); + return NULL; + } + + std::queue q; + PyObject* visited_dict = visited ? visited : PyDict_New(); + + q.push(start_vertex); + PyDict_SetItem(visited_dict, start_vertex, Py_True); + + PyObject* result = PyList_New(0); + + while (!q.empty()) { + PyObject* vertex = q.front(); + q.pop(); + + PyList_Append(result, vertex); + + PyObject* neighbors = PyDict_GetItem(graph, vertex); + if (neighbors && PyList_Check(neighbors)) { + Py_ssize_t size = PyList_Size(neighbors); + for (Py_ssize_t i = 0; i < size; i++) { + PyObject* neighbor = PyList_GetItem(neighbors, i); + if (!PyDict_Contains(visited_dict, neighbor)) { + q.push(neighbor); + PyDict_SetItem(visited_dict, neighbor, Py_True); + } + } + } + } + + if (!visited) Py_DECREF(visited_dict); + return result; +} + +static PyObject* bfs(PyObject* self, PyObject* args, PyObject* kwds) { + PyObject *graph = NULL, *start_vertex = NULL; + static char *kwlist[] = {"graph", "start_vertex", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &graph, &start_vertex)) { + return NULL; + } + + PyObject* result = bfs_impl(graph, start_vertex); + if (result == NULL) return NULL; + Py_INCREF(result); + return result; +} + +#endif diff --git a/pydatastructs/graphs/_extensions.py b/pydatastructs/graphs/_extensions.py new file mode 100644 index 000000000..c98ecc83a --- /dev/null +++ b/pydatastructs/graphs/_extensions.py @@ -0,0 +1,17 @@ +from setuptools import Extension +import sysconfig + +project = 'pydatastructs' + +module = 'graphs' + +backend = "_backend" + +cpp = 'cpp' + +bfs = '.'.join([project, module, backend, cpp, '_bfs']) +bfs_sources = ['/'.join([project, module, backend, cpp, 'algorithms.cpp'])] + +extensions = [ + Extension(bfs, sources=bfs_sources) +] \ No newline at end of file diff --git a/pydatastructs/graphs/algorithms.py b/pydatastructs/graphs/algorithms.py index 334f522c5..a8de9c639 100644 --- a/pydatastructs/graphs/algorithms.py +++ b/pydatastructs/graphs/algorithms.py @@ -11,6 +11,7 @@ from pydatastructs.graphs.graph import Graph from pydatastructs.linear_data_structures.algorithms import merge_sort_parallel from pydatastructs import PriorityQueue +from pydatastructs.graphs._backend.cpp._bfs import bfs as _bfs_cpp __all__ = [ 'breadth_first_search', @@ -24,7 +25,8 @@ 'topological_sort', 'topological_sort_parallel', 'max_flow', - 'find_bridges' + 'find_bridges', + 'bfs' ] Stack = Queue = deque @@ -1368,3 +1370,18 @@ def dfs(u): bridges.append((b, a)) bridges.sort() return bridges + +def bfs(graph, start_vertex, backend=Backend.PYTHON): + if backend == Backend.CPP: + return _bfs_cpp(graph, start_vertex) + from collections import deque + visited = set() + q = deque([start_vertex]) + result = [] + while q: + vertex = q.popleft() + if vertex not in visited: + visited.add(vertex) + result.append(vertex) + q.extend(graph.get(vertex, [])) + return result \ No newline at end of file diff --git a/pydatastructs/graphs/tests/test_algorithms.py b/pydatastructs/graphs/tests/test_algorithms.py index 553377f34..f077108b9 100644 --- a/pydatastructs/graphs/tests/test_algorithms.py +++ b/pydatastructs/graphs/tests/test_algorithms.py @@ -2,7 +2,7 @@ breadth_first_search_parallel, minimum_spanning_tree, minimum_spanning_tree_parallel, strongly_connected_components, depth_first_search, shortest_paths,all_pair_shortest_paths, topological_sort, -topological_sort_parallel, max_flow, find_bridges) +topological_sort_parallel, max_flow, find_bridges, bfs, Backend) from pydatastructs.utils.raises_util import raises def test_breadth_first_search(): @@ -504,3 +504,20 @@ def _test_find_bridges(ds): _test_find_bridges("List") _test_find_bridges("Matrix") + +def test_bfs(): + graph = { + 0: [1, 2], + 1: [0, 3], + 2: [0], + 3: [1] + } + start_vertex = 0 + expected = [0, 1, 2, 3] + + result_python = bfs(graph, start_vertex, backend=Backend.PYTHON) + assert result_python == expected + + result_cpp = bfs(graph, start_vertex, backend=Backend.CPP) + result_cpp_list = [x for x in result_cpp] + assert result_cpp_list == expected \ No newline at end of file diff --git a/setup.py b/setup.py index 60c4ec36d..bbe3faef0 100644 --- a/setup.py +++ b/setup.py @@ -3,6 +3,7 @@ from pydatastructs import linear_data_structures from pydatastructs import miscellaneous_data_structures from pydatastructs import trees +from pydatastructs import graphs with open("README.md", "r") as fh: long_description = fh.read() @@ -13,6 +14,7 @@ extensions.extend(linear_data_structures._extensions.extensions) extensions.extend(miscellaneous_data_structures._extensions.extensions) extensions.extend(trees._extensions.extensions) +extensions.extend(graphs._extensions.extensions) setuptools.setup( name="cz-pydatastructs", From 2ace503f392473cdd7e9204335ac01eac105541c Mon Sep 17 00:00:00 2001 From: arvinder004 Date: Wed, 2 Apr 2025 16:12:56 +0530 Subject: [PATCH 2/5] updated some files --- pydatastructs/graphs/_backend/__init__.py | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 pydatastructs/graphs/_backend/__init__.py diff --git a/pydatastructs/graphs/_backend/__init__.py b/pydatastructs/graphs/_backend/__init__.py deleted file mode 100644 index 6707a0307..000000000 --- a/pydatastructs/graphs/_backend/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -from setuptools import setup, Extension -import sysconfig - -bfs_dfs_module = Extension( - '_bfs_dfs', # Module name - sources=['src/cpp/bfs_dfs.cpp'], - include_dirs=[sysconfig.get_path('include')], - extra_compile_args=['-std=c++11'], -) - -setup( - name='my_pydatastructs', - version='0.1', - package_dir={'': 'src/python'}, - py_modules=['graph_algorithms'], - ext_modules=[bfs_dfs_module], -) \ No newline at end of file From b88ad69747454e4b8b9854af44b36617249f1ec3 Mon Sep 17 00:00:00 2001 From: arvinder004 Date: Wed, 2 Apr 2025 16:22:12 +0530 Subject: [PATCH 3/5] resolved errors --- pydatastructs/graphs/_backend/cpp/algorithms.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pydatastructs/graphs/_backend/cpp/algorithms.cpp b/pydatastructs/graphs/_backend/cpp/algorithms.cpp index 7064bf677..051f56bf6 100644 --- a/pydatastructs/graphs/_backend/cpp/algorithms.cpp +++ b/pydatastructs/graphs/_backend/cpp/algorithms.cpp @@ -8,13 +8,13 @@ static PyMethodDef bfs_PyMethodDef[] = { static struct PyModuleDef bfs_module = { PyModuleDef_HEAD_INIT, - "bfs", + "_bfs", "BFS algorithms module", -1, bfs_PyMethodDef }; -PyMODINIT_FUNC PyInit_bfs(void) { +PyMODINIT_FUNC PyInit__bfs(void) { PyObject *module = PyModule_Create(&bfs_module); if (module == NULL) return NULL; return module; From 0b0555e2a3a014c5dfe40aaf5529ce15e7e2a3ce Mon Sep 17 00:00:00 2001 From: arvinder004 Date: Wed, 2 Apr 2025 16:38:39 +0530 Subject: [PATCH 4/5] resolved error --- pydatastructs/graphs/_backend/cpp/algorithms.cpp | 2 +- pydatastructs/graphs/_extensions.py | 2 +- pydatastructs/graphs/algorithms.py | 2 +- pydatastructs/graphs/tests/test_algorithms.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pydatastructs/graphs/_backend/cpp/algorithms.cpp b/pydatastructs/graphs/_backend/cpp/algorithms.cpp index 051f56bf6..d3d9a0aa4 100644 --- a/pydatastructs/graphs/_backend/cpp/algorithms.cpp +++ b/pydatastructs/graphs/_backend/cpp/algorithms.cpp @@ -18,4 +18,4 @@ PyMODINIT_FUNC PyInit__bfs(void) { PyObject *module = PyModule_Create(&bfs_module); if (module == NULL) return NULL; return module; -} \ No newline at end of file +} diff --git a/pydatastructs/graphs/_extensions.py b/pydatastructs/graphs/_extensions.py index c98ecc83a..6c87aa16a 100644 --- a/pydatastructs/graphs/_extensions.py +++ b/pydatastructs/graphs/_extensions.py @@ -14,4 +14,4 @@ extensions = [ Extension(bfs, sources=bfs_sources) -] \ No newline at end of file +] diff --git a/pydatastructs/graphs/algorithms.py b/pydatastructs/graphs/algorithms.py index a8de9c639..7f48d37c7 100644 --- a/pydatastructs/graphs/algorithms.py +++ b/pydatastructs/graphs/algorithms.py @@ -1384,4 +1384,4 @@ def bfs(graph, start_vertex, backend=Backend.PYTHON): visited.add(vertex) result.append(vertex) q.extend(graph.get(vertex, [])) - return result \ No newline at end of file + return result diff --git a/pydatastructs/graphs/tests/test_algorithms.py b/pydatastructs/graphs/tests/test_algorithms.py index f077108b9..d12baad8c 100644 --- a/pydatastructs/graphs/tests/test_algorithms.py +++ b/pydatastructs/graphs/tests/test_algorithms.py @@ -520,4 +520,4 @@ def test_bfs(): result_cpp = bfs(graph, start_vertex, backend=Backend.CPP) result_cpp_list = [x for x in result_cpp] - assert result_cpp_list == expected \ No newline at end of file + assert result_cpp_list == expected From 210291f6db09cb16d5533343399238730cc10438 Mon Sep 17 00:00:00 2001 From: arvinder004 Date: Wed, 2 Apr 2025 16:53:36 +0530 Subject: [PATCH 5/5] resolved errors --- docs/source/pydatastructs/graphs/algorithms.rst | 4 +++- pydatastructs/graphs/__init__.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/source/pydatastructs/graphs/algorithms.rst b/docs/source/pydatastructs/graphs/algorithms.rst index c508421ab..180eb5be2 100644 --- a/docs/source/pydatastructs/graphs/algorithms.rst +++ b/docs/source/pydatastructs/graphs/algorithms.rst @@ -21,4 +21,6 @@ Algorithms .. autofunction:: pydatastructs.topological_sort_parallel -.. autofunction:: pydatastructs.find_bridges \ No newline at end of file +.. autofunction:: pydatastructs.find_bridges + +.. autofunction:: pydatastructs.bfs \ No newline at end of file diff --git a/pydatastructs/graphs/__init__.py b/pydatastructs/graphs/__init__.py index 502fd7dcb..7cb74df81 100644 --- a/pydatastructs/graphs/__init__.py +++ b/pydatastructs/graphs/__init__.py @@ -1,4 +1,6 @@ -__all__ = [] +__all__ = [ + 'bfs' + ] from . import ( graph,