diff --git a/pydatastructs/utils/_backend/cpp/AdjacencyListGraphNode.hpp b/pydatastructs/utils/_backend/cpp/AdjacencyListGraphNode.hpp new file mode 100644 index 000000000..8aa42f66a --- /dev/null +++ b/pydatastructs/utils/_backend/cpp/AdjacencyListGraphNode.hpp @@ -0,0 +1,231 @@ +#ifndef ADJACENCY_LIST_GRAPH_NODE_HPP +#define ADJACENCY_LIST_GRAPH_NODE_HPP + +#define PY_SSIZE_T_CLEAN +#include +#include +#include +#include "GraphNode.hpp" + +extern PyTypeObject AdjacencyListGraphNodeType; + +typedef struct { + PyObject_HEAD + std::string name; + PyObject* data; + std::unordered_map adjacent; +} AdjacencyListGraphNode; + +static void AdjacencyListGraphNode_dealloc(AdjacencyListGraphNode* self) { + Py_XDECREF(self->data); + for (auto& pair : self->adjacent) { + Py_XDECREF(pair.second); + } + self->adjacent.clear(); + Py_TYPE(self)->tp_free(reinterpret_cast(self)); +} + +static PyObject* AdjacencyListGraphNode_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { + AdjacencyListGraphNode* self = PyObject_New(AdjacencyListGraphNode, &AdjacencyListGraphNodeType); + if (!self) return NULL; + new (&self->adjacent) std::unordered_map(); + new (&self->name) std::string(); + + static char* kwlist[] = { "name", "data", "adjacency_list", NULL }; + const char* name; + PyObject* data = Py_None; + PyObject* adjacency_list = Py_None; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|OO", kwlist, &name, &data, &adjacency_list)) { + PyErr_SetString(PyExc_ValueError, "Invalid arguments: Expected (str, object, list)"); + return NULL; + } + + self->name = std::string(name); + Py_INCREF(data); + self->data = data; + + if (PyList_Check(adjacency_list)) { + Py_ssize_t size = PyList_Size(adjacency_list); + for (Py_ssize_t i = 0; i < size; i++) { + PyObject* node = PyList_GetItem(adjacency_list, i); + + + if (PyType_Ready(&AdjacencyListGraphNodeType) < 0) { + PyErr_SetString(PyExc_RuntimeError, "Failed to initialize AdjacencyListGraphNodeType"); + return NULL; + } + + if (!PyObject_IsInstance(node, (PyObject*)&AdjacencyListGraphNodeType)) { + PyErr_SetString(PyExc_TypeError, "Adjacency list must contain only AdjacencyListGraphNode instances"); + return NULL; + } + + const char* adj_name = (reinterpret_cast(node))->name.c_str(); + std::string str = std::string(adj_name); + Py_INCREF(node); + self->adjacent[str] = node; + } + } + + return reinterpret_cast(self); +} + +static PyObject* AdjacencyListGraphNode_add_adjacent_node(AdjacencyListGraphNode* self, PyObject* args) { + const char* name; + PyObject* data = Py_None; + if (!PyArg_ParseTuple(args, "s|O", &name, &data)) { + PyErr_SetString(PyExc_ValueError, "Invalid arguments: Expected (str, object)"); + return NULL; + } + + if (self->adjacent.find(name) == self->adjacent.end()) { + PyObject* new_node = PyObject_CallFunction(reinterpret_cast(&AdjacencyListGraphNodeType), "sO", name, data); + if (!new_node) { + PyErr_SetString(PyExc_RuntimeError, "Failed to create adjacent node"); + return NULL; + } + self->adjacent[name] = new_node; + Py_INCREF(new_node); + } else { + Py_XDECREF(self->adjacent[name]); + Py_INCREF(data); + self->adjacent[name] = data; + } + Py_RETURN_NONE; +} + +static PyObject* AdjacencyListGraphNode_remove_adjacent_node(AdjacencyListGraphNode* self, PyObject* args) { + const char* name; + if (!PyArg_ParseTuple(args, "s", &name)) { + PyErr_SetString(PyExc_ValueError, "Invalid arguments: Expected (str)"); + return NULL; + } + + auto it = self->adjacent.find(name); + if (it != self->adjacent.end()) { + Py_XDECREF(it->second); + self->adjacent.erase(it); + Py_RETURN_NONE; + } else { + PyErr_SetString(PyExc_ValueError, "Node is not adjacent"); + return NULL; + } +} + +static PyObject* AdjacencyListGraphNode_get_name(AdjacencyListGraphNode* self, void* closure) { + return PyUnicode_FromString(self->name.c_str()); +} + +static int AdjacencyListGraphNode_set_name(AdjacencyListGraphNode* self, PyObject* value, void* closure) { + if (!PyUnicode_Check(value)) { + PyErr_SetString(PyExc_TypeError, "name must be a string"); + return -1; + } + self->name = PyUnicode_AsUTF8(value); + return 0; +} + +static PyObject* AdjacencyListGraphNode_get_data(AdjacencyListGraphNode* self, void* closure) { + Py_INCREF(self->data); + return self->data; +} + +static int AdjacencyListGraphNode_set_data(AdjacencyListGraphNode* self, PyObject* value, void* closure) { + Py_XDECREF(self->data); + Py_INCREF(value); + self->data = value; + return 0; +} + +static PyObject* AdjacencyListGraphNode_get_adjacent(AdjacencyListGraphNode* self, void* closure) { + PyObject* py_dict = PyDict_New(); + if (!py_dict) return NULL; + + for (const auto& pair : self->adjacent) { + PyDict_SetItemString(py_dict, pair.first.c_str(), pair.second); + } + + return py_dict; +} + +static int AdjacencyListGraphNode_set_adjacent(AdjacencyListGraphNode* self, PyObject* value, void* closure) { + if (!PyDict_Check(value)) { + PyErr_SetString(PyExc_TypeError, "adjacent must be a dictionary"); + return -1; + } + + self->adjacent.clear(); + + PyObject *key, *val; + Py_ssize_t pos = 0; + while (PyDict_Next(value, &pos, &key, &val)) { + if (!PyUnicode_Check(key) || !PyObject_IsInstance(val, (PyObject*)&AdjacencyListGraphNodeType)) { + PyErr_SetString(PyExc_TypeError, "Keys must be strings and values must be AdjacencyListGraphNode instances"); + return -1; + } + + const char* key_str = PyUnicode_AsUTF8(key); + Py_INCREF(val); + self->adjacent[key_str] = val; + } + + return 0; +} + +static PyGetSetDef AdjacencyListGraphNode_getsetters[] = { + {"name", (getter)AdjacencyListGraphNode_get_name, (setter)AdjacencyListGraphNode_set_name, "Get or set node name", NULL}, + {"data", (getter)AdjacencyListGraphNode_get_data, (setter)AdjacencyListGraphNode_set_data, "Get or set node data", NULL}, + {"adjacent", (getter)AdjacencyListGraphNode_get_adjacent, (setter)AdjacencyListGraphNode_set_adjacent, "Get or set adjacent nodes", NULL}, + {NULL} +}; + + +static PyMethodDef AdjacencyListGraphNode_methods[] = { + {"add_adjacent_node", (PyCFunction)AdjacencyListGraphNode_add_adjacent_node, METH_VARARGS, "Add adjacent node"}, + {"remove_adjacent_node", (PyCFunction)AdjacencyListGraphNode_remove_adjacent_node, METH_VARARGS, "Remove adjacent node"}, + {NULL} +}; + +PyTypeObject AdjacencyListGraphNodeType = { + /* tp_name */ PyVarObject_HEAD_INIT(NULL, 0) "AdjacencyListGraphNode", + /* tp_basicsize */ sizeof(AdjacencyListGraphNode), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)AdjacencyListGraphNode_dealloc, + /* tp_print */ 0, + /* tp_getattr */ 0, + /* tp_setattr */ 0, + /* tp_reserved */ 0, + /* tp_repr */ 0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ 0, + /* tp_call */ 0, + /* tp_str */ 0, + /* tp_getattro */ 0, + /* tp_setattro */ 0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + /* tp_doc */ "Node Data Structure for an Adjacency List Graph", + /* tp_traverse */ 0, + /* tp_clear */ 0, + /* tp_richcompare */ 0, + /* tp_weaklistoffset */ 0, + /* tp_iter */ 0, + /* tp_iternext */ 0, + /* tp_methods */ AdjacencyListGraphNode_methods, + /* tp_members */ 0, + /* tp_getset */ AdjacencyListGraphNode_getsetters, + /* tp_base */ &GraphNodeType, + /* tp_dict */ 0, + /* tp_descr_get */ 0, + /* tp_descr_set */ 0, + /* tp_dictoffset */ 0, + /* tp_init */ 0, + /* tp_alloc */ 0, + /* tp_new */ AdjacencyListGraphNode_new, +}; + + +#endif diff --git a/pydatastructs/utils/_backend/cpp/AdjacencyMatrixGraphNode.hpp b/pydatastructs/utils/_backend/cpp/AdjacencyMatrixGraphNode.hpp new file mode 100644 index 000000000..8c35754f6 --- /dev/null +++ b/pydatastructs/utils/_backend/cpp/AdjacencyMatrixGraphNode.hpp @@ -0,0 +1,69 @@ +#ifndef ADJACENCY_MATRIX_GRAPH_NODE_HPP +#define ADJACENCY_MATRIX_GRAPH_NODE_HPP + +#define PY_SSIZE_T_CLEAN +#include +#include +#include "GraphNode.hpp" + +typedef struct { + GraphNode super; +} AdjacencyMatrixGraphNode; + +static void AdjacencyMatrixGraphNode_dealloc(AdjacencyMatrixGraphNode* self){ + Py_XDECREF(self->super.data); + Py_TYPE(self)->tp_free(reinterpret_cast(self)); +} + +static PyObject* AdjacencyMatrixGraphNode_new(PyTypeObject* type, PyObject* args, PyObject* kwds){ + PyObject* base_obj = GraphNode_new(type, args, kwds); + if (!base_obj) { + return NULL; + } + + AdjacencyMatrixGraphNode* self = reinterpret_cast(base_obj); + + return reinterpret_cast(self); +} + +PyTypeObject AdjacencyMatrixGraphNodeType = { + /* tp_name */ PyVarObject_HEAD_INIT(NULL, 0) "AdjacencyMatrixGraphNode", + /* tp_basicsize */ sizeof(AdjacencyMatrixGraphNode), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)AdjacencyMatrixGraphNode_dealloc, + /* tp_print */ 0, + /* tp_getattr */ 0, + /* tp_setattr */ 0, + /* tp_reserved */ 0, + /* tp_repr */ 0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ 0, + /* tp_call */ 0, + /* tp_str */ 0, + /* tp_getattro */ 0, + /* tp_setattro */ 0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + /* tp_doc */ "Node Data Structure for an Adjacency Matrix Graph", + /* tp_traverse */ 0, + /* tp_clear */ 0, + /* tp_richcompare */ 0, + /* tp_weaklistoffset */ 0, + /* tp_iter */ 0, + /* tp_iternext */ 0, + /* tp_methods */ 0, + /* tp_members */ 0, + /* tp_getset */ 0, + /* tp_base */ &GraphNodeType, + /* tp_dict */ 0, + /* tp_descr_get */ 0, + /* tp_descr_set */ 0, + /* tp_dictoffset */ 0, + /* tp_init */ 0, + /* tp_alloc */ 0, + /* tp_new */ AdjacencyMatrixGraphNode_new, +}; + +#endif diff --git a/pydatastructs/utils/_backend/cpp/GraphEdge.hpp b/pydatastructs/utils/_backend/cpp/GraphEdge.hpp new file mode 100644 index 000000000..1ef21008a --- /dev/null +++ b/pydatastructs/utils/_backend/cpp/GraphEdge.hpp @@ -0,0 +1,107 @@ +#ifndef GRAPH_EDGE_HPP +#define GRAPH_EDGE_HPP + +#define PY_SSIZE_T_CLEAN +#include +#include +#include "GraphNode.hpp" + +extern PyTypeObject GraphEdgeType; + +typedef struct { + PyObject_HEAD + PyObject* source; + PyObject* target; + PyObject* value; +} GraphEdge; + +static void GraphEdge_dealloc(GraphEdge* self) { + Py_XDECREF(self->source); + Py_XDECREF(self->target); + Py_XDECREF(self->value); + Py_TYPE(self)->tp_free(reinterpret_cast(self)); +} + +static PyObject* GraphEdge_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { + GraphEdge* self; + self = reinterpret_cast(type->tp_alloc(type, 0)); + if (!self) return NULL; + + static char* kwlist[] = {"node1", "node2", "value", NULL}; + PyObject* node1; + PyObject* node2; + PyObject* value = Py_None; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O", kwlist, &node1, &node2, &value)) { + PyErr_SetString(PyExc_ValueError, "Invalid arguments: Expected (GraphNode, GraphNode, optional value)"); + return NULL; + } + + Py_INCREF(node1); + Py_INCREF(node2); + Py_INCREF(value); + + self->source = node1; + self->target = node2; + self->value = value; + + return reinterpret_cast(self); +} + +static PyObject* GraphEdge_str(GraphEdge* self) { + PyObject* source_name = PyObject_GetAttrString(self->source, "name"); + PyObject* target_name = PyObject_GetAttrString(self->target, "name"); + + if (!source_name || !target_name) { + PyErr_SetString(PyExc_AttributeError, "Both nodes must have a 'name' attribute."); + return NULL; + } + + PyObject* str_repr = PyUnicode_FromFormat("('%U', '%U')", source_name, target_name); + + Py_XDECREF(source_name); + Py_XDECREF(target_name); + return str_repr; +} + +PyTypeObject GraphEdgeType = { + /* tp_name */ PyVarObject_HEAD_INIT(NULL, 0) "GraphEdge", + /* tp_basicsize */ sizeof(GraphEdge), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)GraphEdge_dealloc, + /* tp_print */ 0, + /* tp_getattr */ 0, + /* tp_setattr */ 0, + /* tp_reserved */ 0, + /* tp_repr */ 0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ 0, + /* tp_call */ 0, + /* tp_str */ (reprfunc)GraphEdge_str, + /* tp_getattro */ 0, + /* tp_setattro */ 0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + /* tp_doc */ "Data Structure for a Graph Edge", + /* tp_traverse */ 0, + /* tp_clear */ 0, + /* tp_richcompare */ 0, + /* tp_weaklistoffset */ 0, + /* tp_iter */ 0, + /* tp_iternext */ 0, + /* tp_methods */ 0, + /* tp_members */ 0, + /* tp_getset */ 0, + /* tp_base */ 0, + /* tp_dict */ 0, + /* tp_descr_get */ 0, + /* tp_descr_set */ 0, + /* tp_dictoffset */ 0, + /* tp_init */ 0, + /* tp_alloc */ 0, + /* tp_new */ GraphEdge_new, +}; + +#endif diff --git a/pydatastructs/utils/_backend/cpp/GraphNode.hpp b/pydatastructs/utils/_backend/cpp/GraphNode.hpp new file mode 100644 index 000000000..0df90e6f3 --- /dev/null +++ b/pydatastructs/utils/_backend/cpp/GraphNode.hpp @@ -0,0 +1,123 @@ +#ifndef GRAPH_NODE_HPP +#define GRAPH_NODE_HPP + +#define PY_SSIZE_T_CLEAN +#include +#include + +typedef struct { + PyObject_HEAD + std::string name; + PyObject* data; +} GraphNode; + +static void GraphNode_dealloc(GraphNode* self){ + Py_XDECREF(self->data); + Py_TYPE(self)->tp_free(reinterpret_cast(self)); +} + +static PyObject* GraphNode_new(PyTypeObject* type, PyObject* args, PyObject* kwds){ + GraphNode* self; + self = reinterpret_cast(type->tp_alloc(type,0)); + new (&self->name) std::string(); + if (!self) return NULL; + + static char* kwlist[] = { "name", "data", NULL }; + const char* name; + PyObject* data = Py_None; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|O", kwlist, &name, &data)) { + PyErr_SetString(PyExc_ValueError, "Invalid arguments: Expected (str, object)"); + return NULL; + } + + self->name = std::string(name); + Py_INCREF(data); + self->data = data; + + return reinterpret_cast(self); +} + +static PyObject* GraphNode_str(GraphNode* self) { + return PyUnicode_FromString(("('" + self->name + "', " + PyUnicode_AsUTF8(PyObject_Str(self->data)) + ")").c_str()); +} + +static PyObject* GraphNode_get(GraphNode* self, void *closure) { + if (closure == (void*)"name") { + return PyUnicode_FromString(self->name.c_str()); + } + if (closure == (void*)"data") { + Py_INCREF(self->data); + return self->data; + } + Py_RETURN_NONE; +} + +static int GraphNode_set(GraphNode* self, PyObject *value, void *closure) { + if (value == NULL) { + PyErr_SetString(PyExc_ValueError, "value is NULL"); + return -1; + } + + if (closure == (void*)"name") { + if (!PyUnicode_Check(value)) { + PyErr_SetString(PyExc_TypeError, "value to be set must be a string"); + return -1; + } + self->name = PyUnicode_AsUTF8(value); + } + else if (closure == (void*)"data") { + PyObject *tmp = self->data; + Py_INCREF(value); + self->data = value; + Py_DECREF(tmp); + } + else { + PyErr_SetString(PyExc_AttributeError, "Unknown attribute"); + return -1; + } + + return 0; +} + +static PyTypeObject GraphNodeType = { + /* tp_name */ PyVarObject_HEAD_INIT(NULL, 0) "GraphNode", + /* tp_basicsize */ sizeof(GraphNode), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor) GraphNode_dealloc, + /* tp_print */ 0, + /* tp_getattr */ 0, + /* tp_setattr */ 0, + /* tp_reserved */ 0, + /* tp_repr */ 0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ 0, + /* tp_call */ 0, + /* tp_str */ (reprfunc) GraphNode_str, + /* tp_getattro */ 0, + /* tp_setattro */ 0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + /* tp_doc */ 0, + /* tp_traverse */ 0, + /* tp_clear */ 0, + /* tp_richcompare */ 0, + /* tp_weaklistoffset */ 0, + /* tp_iter */ 0, + /* tp_iternext */ 0, + /* tp_methods */ 0, + /* tp_members */ 0, + /* tp_getset */ 0, + /* tp_base */ &PyBaseObject_Type, + /* tp_dict */ 0, + /* tp_descr_get */ 0, + /* tp_descr_set */ 0, + /* tp_dictoffset */ 0, + /* tp_init */ 0, + /* tp_alloc */ 0, + /* tp_new */ GraphNode_new, +}; + +#endif diff --git a/pydatastructs/utils/_backend/cpp/graph_utils.cpp b/pydatastructs/utils/_backend/cpp/graph_utils.cpp new file mode 100644 index 000000000..c00c83b9d --- /dev/null +++ b/pydatastructs/utils/_backend/cpp/graph_utils.cpp @@ -0,0 +1,44 @@ +#include +#include "GraphNode.hpp" +#include "AdjacencyListGraphNode.hpp" +#include "AdjacencyMatrixGraphNode.hpp" +#include "GraphEdge.hpp" + +static struct PyModuleDef graph_utils_struct = { + PyModuleDef_HEAD_INIT, + "_graph_utils", + 0, + -1, + NULL, +}; + +PyMODINIT_FUNC PyInit__graph_utils(void) { + Py_Initialize(); + PyObject *utils = PyModule_Create(&graph_utils_struct); + + if (PyType_Ready(&GraphNodeType) < 0) { + return NULL; + } + Py_INCREF(&GraphNodeType); + PyModule_AddObject(utils, "GraphNode", reinterpret_cast(&GraphNodeType)); + + if (PyType_Ready(&AdjacencyMatrixGraphNodeType) < 0) { + return NULL; + } + Py_INCREF(&AdjacencyMatrixGraphNodeType); + PyModule_AddObject(utils, "AdjacencyMatrixGraphNode", reinterpret_cast(&AdjacencyMatrixGraphNodeType)); + + if (PyType_Ready(&AdjacencyListGraphNodeType) < 0) { + return NULL; + } + Py_INCREF(&AdjacencyListGraphNodeType); + PyModule_AddObject(utils, "AdjacencyListGraphNode", reinterpret_cast(&AdjacencyListGraphNodeType)); + + if (PyType_Ready(&GraphEdgeType) < 0) { + return NULL; + } + Py_INCREF(&GraphEdgeType); + PyModule_AddObject(utils, "GraphEdge", reinterpret_cast(&GraphEdgeType)); + + return utils; +} diff --git a/pydatastructs/utils/_extensions.py b/pydatastructs/utils/_extensions.py index e7f531827..f8b71aff0 100644 --- a/pydatastructs/utils/_extensions.py +++ b/pydatastructs/utils/_extensions.py @@ -11,7 +11,11 @@ nodes = '.'.join([project, module, backend, cpp, '_nodes']) nodes_sources = ['/'.join([project, module, backend, cpp, 'nodes.cpp'])] +graph_utils = '.'.join([project, module, backend, cpp, '_graph_utils']) +graph_utils_sources = ['/'.join([project, module, backend, cpp, + 'graph_utils.cpp'])] extensions = [ - Extension(nodes, sources=nodes_sources) + Extension(nodes, sources=nodes_sources), + Extension(graph_utils, sources = graph_utils_sources) ] diff --git a/pydatastructs/utils/misc_util.py b/pydatastructs/utils/misc_util.py index 4c7ca7faa..6af85b581 100644 --- a/pydatastructs/utils/misc_util.py +++ b/pydatastructs/utils/misc_util.py @@ -1,6 +1,6 @@ import math, pydatastructs from enum import Enum -from pydatastructs.utils._backend.cpp import _nodes +from pydatastructs.utils._backend.cpp import _nodes, _graph_utils __all__ = [ 'TreeNode', @@ -396,19 +396,21 @@ def methods(cls): return ['__new__', 'add_adjacent_node', 'remove_adjacent_node'] - def __new__(cls, name, data=None, adjacency_list=None, + def __new__(cls, name, data=None, adjacency_list=[], **kwargs): - raise_if_backend_is_not_python( - cls, kwargs.get('backend', Backend.PYTHON)) - obj = GraphNode.__new__(cls) - obj.name, obj.data = str(name), data - obj._impl = 'adjacency_list' - if adjacency_list is not None: - for node in adjacency_list: - obj.__setattr__(node.name, node) - obj.adjacent = adjacency_list if adjacency_list is not None \ - else [] - return obj + backend = kwargs.get('backend', Backend.PYTHON) + if backend == Backend.PYTHON: + obj = GraphNode.__new__(cls) + obj.name, obj.data = str(name), data + obj._impl = 'adjacency_list' + if len(adjacency_list) > 0: + for node in adjacency_list: + obj.__setattr__(node.name, node) + obj.adjacent = adjacency_list if len(adjacency_list) > 0 \ + else [] + return obj + else: + return _graph_utils.AdjacencyListGraphNode(name, data, adjacency_list) def add_adjacent_node(self, name, data=None): """ @@ -457,13 +459,15 @@ def methods(cls): def __new__(cls, name, data=None, **kwargs): - raise_if_backend_is_not_python( - cls, kwargs.get('backend', Backend.PYTHON)) - obj = GraphNode.__new__(cls) - obj.name, obj.data, obj.is_connected = \ - str(name), data, None - obj._impl = 'adjacency_matrix' - return obj + backend = kwargs.get('backend', Backend.PYTHON) + if backend == Backend.PYTHON: + obj = GraphNode.__new__(cls) + obj.name, obj.data, obj.is_connected = \ + str(name), data, None + obj._impl = 'adjacency_matrix' + return obj + else: + return _graph_utils.AdjacencyMatrixGraphNode(str(name), data) class GraphEdge(object): """ @@ -487,12 +491,14 @@ def methods(cls): def __new__(cls, node1, node2, value=None, **kwargs): - raise_if_backend_is_not_python( - cls, kwargs.get('backend', Backend.PYTHON)) - obj = object.__new__(cls) - obj.source, obj.target = node1, node2 - obj.value = value - return obj + backend = kwargs.get('backend', Backend.PYTHON) + if backend == Backend.PYTHON: + obj = object.__new__(cls) + obj.source, obj.target = node1, node2 + obj.value = value + return obj + else: + return _graph_utils.GraphEdge(node1, node2, value) def __str__(self): return str((self.source.name, self.target.name)) diff --git a/pydatastructs/utils/tests/test_code_quality.py b/pydatastructs/utils/tests/test_code_quality.py index eafa80be1..84d419523 100644 --- a/pydatastructs/utils/tests/test_code_quality.py +++ b/pydatastructs/utils/tests/test_code_quality.py @@ -208,7 +208,10 @@ def test_backend_argument_message(): backend_implemented = [ pyds.OneDimensionalArray, pyds.DynamicOneDimensionalArray, - pyds.quick_sort + pyds.quick_sort, + pyds.AdjacencyListGraphNode, + pyds.AdjacencyMatrixGraphNode, + pyds.GraphEdge ] def call_and_raise(api, pos_args_count=0): diff --git a/pydatastructs/utils/tests/test_misc_util.py b/pydatastructs/utils/tests/test_misc_util.py index 0e253c9a2..d779731c9 100644 --- a/pydatastructs/utils/tests/test_misc_util.py +++ b/pydatastructs/utils/tests/test_misc_util.py @@ -22,9 +22,27 @@ def test_AdjacencyListGraphNode(): assert g.g_1.data == 4 assert str(g) == "('g', 0)" + h_1 = AdjacencyListGraphNode('h_1', 1, backend = Backend.CPP) + h_2 = AdjacencyListGraphNode('h_2', 2, backend = Backend.CPP) + assert str(h_1) == "('h_1', 1)" + h = AdjacencyListGraphNode('h', 0, adjacency_list = [h_1, h_2], backend = Backend.CPP) + h.add_adjacent_node('h_3', 3) + assert h.adjacent['h_1'].name == 'h_1' + assert h.adjacent['h_2'].name == 'h_2' + assert h.adjacent['h_3'].name == 'h_3' + h.remove_adjacent_node('h_3') + assert 'h_3' not in h.adjacent + assert raises(ValueError, lambda: h.remove_adjacent_node('h_3')) + h.add_adjacent_node('h_1', 4) + assert h.adjacent['h_1'] == 4 + assert str(h) == "('h', 0)" + def test_AdjacencyMatrixGraphNode(): g = AdjacencyMatrixGraphNode("1", 3) + g2 = AdjacencyMatrixGraphNode("1", 3, backend = Backend.CPP) assert str(g) == "('1', 3)" + assert str(g2) == "('1', 3)" + def test_GraphEdge(): g_1 = AdjacencyListGraphNode('g_1', 1) @@ -32,6 +50,11 @@ def test_GraphEdge(): e = GraphEdge(g_1, g_2, value=2) assert str(e) == "('g_1', 'g_2')" + h_1 = AdjacencyListGraphNode('h_1', 1, backend = Backend.CPP) + h_2 = AdjacencyListGraphNode('h_2', 2, backend = Backend.CPP) + e2 = GraphEdge(h_1, h_2, value = 2, backend = Backend.CPP) + assert str(e2) == "('h_1', 'h_2')" + def test_BinomialTreeNode(): b = BinomialTreeNode(1,1) b.add_children(*[BinomialTreeNode(i,i) for i in range(2,10)]) diff --git a/scripts/build/dummy_submodules_data.py b/scripts/build/dummy_submodules_data.py index ea3d63228..115d080a2 100644 --- a/scripts/build/dummy_submodules_data.py +++ b/scripts/build/dummy_submodules_data.py @@ -6,4 +6,4 @@ cpp = 'cpp' -dummy_submodules_list = [('_arrays.py', '_algorithms.py'), ('_stack.py',), ('_nodes.py',), ('_trees.py',)] +dummy_submodules_list = [('_arrays.py', '_algorithms.py'), ('_stack.py',), ('_nodes.py', '_graph_utils.py',), ('_trees.py',)]