Skip to content

Commit c72a133

Browse files
Prerak SinghPrerak Singh
authored andcommitted
Adjacency Matrix Graph backend added
1 parent 68cee46 commit c72a133

13 files changed

+340
-39
lines changed

pydatastructs/graphs/_backend/cpp/AdjacencyList.hpp

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ extern PyTypeObject AdjacencyListGraphType;
1414
typedef struct {
1515

1616
PyObject_HEAD
17+
PyObject* dict;
1718
std::vector<AdjacencyListGraphNode *> nodes;
1819
std::unordered_map<std::string, GraphEdge*> edges;
1920
std::unordered_map<std::string, AdjacencyListGraphNode*> node_map;
@@ -37,7 +38,7 @@ static void AdjacencyListGraph_dealloc(AdjacencyListGraph* self) {
3738
}
3839

3940
static PyObject* AdjacencyListGraph_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
40-
AdjacencyListGraph* self = (AdjacencyListGraph*)type->tp_alloc(type, 0);
41+
AdjacencyListGraph* self = reinterpret_cast<AdjacencyListGraph*>(type->tp_alloc(type, 0));
4142
if (!self)
4243
return NULL;
4344

@@ -54,27 +55,36 @@ static PyObject* AdjacencyListGraph_new(PyTypeObject* type, PyObject* args, PyOb
5455
self->nodes.~vector();
5556
self->edges.~unordered_map();
5657
self->node_map.~unordered_map();
57-
Py_TYPE(self)->tp_free((PyObject*)self);
58+
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
5859
return NULL;
5960
}
6061

61-
AdjacencyListGraphNode* node = (AdjacencyListGraphNode*)node_obj;
62+
AdjacencyListGraphNode* node = reinterpret_cast<AdjacencyListGraphNode*>(node_obj);
6263

6364
if (self->node_map.find(node->name) != self->node_map.end()) {
6465
PyErr_Format(PyExc_ValueError, "Duplicate node with name '%s'", node->name.c_str());
6566

6667
self->nodes.~vector();
6768
self->edges.~unordered_map();
6869
self->node_map.~unordered_map();
69-
Py_TYPE(self)->tp_free((PyObject*)self);
70+
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
7071
return NULL;
7172
}
7273

7374
Py_INCREF(node);
7475
self->nodes.push_back(node);
7576
self->node_map[node->name] = node;
7677
}
77-
return (PyObject*)self;
78+
PyObject* impl_str = PyUnicode_FromString("adjacency_list");
79+
80+
if (PyObject_SetAttrString(reinterpret_cast<PyObject*>(self), "_impl", impl_str) < 0) {
81+
Py_DECREF(impl_str);
82+
PyErr_SetString(PyExc_RuntimeError, "Failed to set _impl attribute");
83+
return NULL;
84+
}
85+
86+
Py_DECREF(impl_str);
87+
return reinterpret_cast<PyObject*>(self);
7888
}
7989

8090
static std::string make_edge_key(const std::string& source, const std::string& target) {
@@ -94,7 +104,7 @@ static PyObject* AdjacencyListGraph_add_vertex(AdjacencyListGraph* self, PyObjec
94104
return NULL;
95105
}
96106

97-
AdjacencyListGraphNode* node = (AdjacencyListGraphNode*)node_obj;
107+
AdjacencyListGraphNode* node = reinterpret_cast<AdjacencyListGraphNode*>(node_obj);
98108

99109
if (self->node_map.find(node->name) != self->node_map.end()) {
100110
PyErr_SetString(PyExc_ValueError, "Node with this name already exists");
@@ -228,7 +238,7 @@ static PyObject* AdjacencyListGraph_get_edge(AdjacencyListGraph* self, PyObject*
228238
auto it = self->edges.find(key);
229239
if (it != self->edges.end()) {
230240
Py_INCREF(it->second);
231-
return (PyObject*)it->second;
241+
return reinterpret_cast<PyObject*>(it->second);
232242
}
233243

234244
Py_RETURN_NONE;
@@ -293,10 +303,10 @@ static PyObject* AdjacencyListGraph_add_edge(AdjacencyListGraph* self, PyObject*
293303
AdjacencyListGraphNode* source_node = self->node_map[source];
294304
AdjacencyListGraphNode* target_node = self->node_map[target];
295305

296-
PyObject* edge_args = PyTuple_Pack(3, (PyObject*)source_node, (PyObject*)target_node, value);
306+
PyObject* edge_args = PyTuple_Pack(3, reinterpret_cast<PyObject*>(source_node), reinterpret_cast<PyObject*>(target_node), value);
297307
if (!edge_args) return NULL;
298308

299-
PyObject* edge_obj = PyObject_CallObject((PyObject*)&GraphEdgeType, edge_args);
309+
PyObject* edge_obj = PyObject_CallObject(reinterpret_cast<PyObject*>(&GraphEdgeType), edge_args);
300310
Py_DECREF(edge_args);
301311

302312
if (!edge_obj)
@@ -307,15 +317,15 @@ static PyObject* AdjacencyListGraph_add_edge(AdjacencyListGraph* self, PyObject*
307317
auto it = self->edges.find(key);
308318
if (it != self->edges.end()) {
309319
Py_XDECREF(it->second);
310-
it->second = (GraphEdge*)edge_obj;
320+
it->second = reinterpret_cast<GraphEdge*>(edge_obj);
311321
} else {
312-
self->edges[key] = (GraphEdge*)edge_obj;
322+
self->edges[key] = reinterpret_cast<GraphEdge*>(edge_obj);
313323
}
314324

315325
auto adj_it = source_node->adjacent.find(target);
316326
if (adj_it == source_node->adjacent.end()) {
317327
Py_INCREF(target_node);
318-
source_node->adjacent[target] = (PyObject*)target_node;
328+
source_node->adjacent[target] = reinterpret_cast<PyObject*>(target_node);
319329
}
320330

321331
Py_RETURN_NONE;
@@ -349,7 +359,7 @@ PyTypeObject AdjacencyListGraphType = {
349359
.tp_doc = "Adjacency List Graph data structure",
350360
.tp_methods = AdjacencyListGraph_methods,
351361
.tp_new = AdjacencyListGraph_new,
362+
.tp_dictoffset = offsetof(AdjacencyListGraph, dict)
352363
};
353364

354365
#endif
355-
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
#ifndef ADJACENCY_MATRIX_GRAPH_HPP
2+
#define ADJACENCY_MATRIX_GRAPH_HPP
3+
4+
#define PY_SSIZE_T_CLEAN
5+
#include <Python.h>
6+
#include <vector>
7+
#include <unordered_map>
8+
#include <string>
9+
#include "AdjacencyMatrixGraphNode.hpp"
10+
#include "GraphEdge.hpp"
11+
#include "GraphNode.hpp"
12+
13+
extern PyTypeObject AdjacencyMatrixGraphNodeType;
14+
15+
typedef struct {
16+
PyObject_HEAD
17+
PyObject* dict;
18+
std::vector<AdjacencyMatrixGraphNode *> nodes;
19+
std::unordered_map<std::string, std::unordered_map<std::string,bool> > matrix;
20+
std::unordered_map<std::string, AdjacencyMatrixGraphNode *> node_map;
21+
std::unordered_map<std::string, GraphEdge *> edge_weights;
22+
} AdjacencyMatrixGraph;
23+
24+
static void AdjacencyMatrixGraph_dealloc(AdjacencyMatrixGraph* self)
25+
{
26+
for (auto& pair : self->edge_weights) {
27+
Py_XDECREF(pair.second);
28+
}
29+
self->edge_weights.clear();
30+
31+
for (AdjacencyMatrixGraphNode* node : self->nodes) {
32+
Py_XDECREF(node);
33+
}
34+
self->nodes.clear();
35+
36+
self->node_map.clear();
37+
self->matrix.clear();
38+
39+
Py_XDECREF(self->dict);
40+
41+
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
42+
}
43+
44+
static PyObject* AdjacencyMatrixGraph_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
45+
{
46+
AdjacencyMatrixGraph* self;
47+
self = reinterpret_cast<AdjacencyMatrixGraph*>(type->tp_alloc(type, 0));
48+
if (self != NULL) {
49+
new (&self->nodes) std::vector<AdjacencyMatrixGraphNode*>();
50+
new (&self->node_map) std::unordered_map<std::string, AdjacencyMatrixGraphNode*>();
51+
new (&self->matrix) std::unordered_map<std::string, std::unordered_map<std::string, bool> >();
52+
new (&self->edge_weights) std::unordered_map<std::string, GraphEdge*>();
53+
54+
PyObject* vertices;
55+
if (!PyArg_ParseTuple(args, "O", &vertices)) {
56+
Py_DECREF(self);
57+
return NULL;
58+
}
59+
60+
if (!PyTuple_Check(vertices)) {
61+
PyErr_SetString(PyExc_TypeError, "Expected a tuple of vertices");
62+
Py_DECREF(self);
63+
return NULL;
64+
}
65+
66+
Py_ssize_t len = PyTuple_Size(vertices);
67+
for (Py_ssize_t i = 0; i < len; ++i) {
68+
PyObject* item = PyTuple_GetItem(vertices, i);
69+
if (!PyObject_TypeCheck(item, &AdjacencyMatrixGraphNodeType)) {
70+
PyErr_SetString(PyExc_TypeError, "All elements must be AdjacencyMatrixGraphNode instances");
71+
Py_DECREF(self);
72+
return NULL;
73+
}
74+
Py_INCREF(item);
75+
AdjacencyMatrixGraphNode* node = reinterpret_cast<AdjacencyMatrixGraphNode*>(item);
76+
std::string name =(reinterpret_cast<GraphNode*>(node))->name;
77+
self->nodes.push_back(node);
78+
self->node_map[name] = node;
79+
self->matrix[name] = std::unordered_map<std::string, bool>();
80+
}
81+
82+
PyObject* impl_str = PyUnicode_FromString("adjacency_matrix");
83+
84+
if (PyObject_SetAttrString(reinterpret_cast<PyObject*>(self), "_impl", impl_str) < 0) {
85+
Py_DECREF(impl_str);
86+
PyErr_SetString(PyExc_RuntimeError, "Failed to set _impl attribute");
87+
return NULL;
88+
}
89+
90+
Py_DECREF(impl_str);
91+
}
92+
return reinterpret_cast<PyObject*>(self);
93+
}
94+
95+
static PyObject* AdjacencyMatrixGraph_add_edge(AdjacencyMatrixGraph* self, PyObject* args, PyObject* kwds)
96+
{
97+
const char *source, *target;
98+
PyObject *cost_obj = Py_None;
99+
static char* kwlist[] = {"source", "target", "cost", NULL};
100+
101+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss|O", kwlist, &source, &target, &cost_obj))
102+
return NULL;
103+
104+
std::string src(source);
105+
std::string dst(target);
106+
107+
if (self->matrix.find(src) == self->matrix.end() ||
108+
self->matrix.find(dst) == self->matrix.end()) {
109+
PyErr_Format(PyExc_ValueError, "Vertex %s or %s not in graph", source, target);
110+
return NULL;
111+
}
112+
113+
self->matrix[src][dst] = true;
114+
115+
if (cost_obj != Py_None) {
116+
double cost = PyFloat_AsDouble(cost_obj);
117+
if (PyErr_Occurred()) return NULL;
118+
119+
PyObject* cost_pyfloat = PyFloat_FromDouble(cost);
120+
if (!cost_pyfloat) return NULL;
121+
122+
PyObject* args = Py_BuildValue("OOO", reinterpret_cast<PyObject*>(self->node_map[src]), reinterpret_cast<PyObject*>(self->node_map[dst]), cost_pyfloat);
123+
Py_DECREF(cost_pyfloat);
124+
if (!args) return NULL;
125+
126+
PyObject* edge_obj = PyObject_CallObject(reinterpret_cast<PyObject*>(&GraphEdgeType), args);
127+
Py_DECREF(args);
128+
if (!edge_obj) return NULL;
129+
130+
self->edge_weights[src + "_" + dst] = reinterpret_cast<GraphEdge*>(edge_obj);
131+
}
132+
133+
Py_RETURN_NONE;
134+
}
135+
136+
static PyObject* AdjacencyMatrixGraph_remove_edge(AdjacencyMatrixGraph* self, PyObject* args)
137+
{
138+
const char *source, *target;
139+
if (!PyArg_ParseTuple(args, "ss", &source, &target))
140+
return NULL;
141+
142+
std::string src(source);
143+
std::string dst(target);
144+
145+
if (self->matrix.find(src) != self->matrix.end()) {
146+
self->matrix[src][dst] = false;
147+
self->edge_weights.erase(src + "_" + dst);
148+
}
149+
150+
Py_RETURN_NONE;
151+
}
152+
153+
static PyObject* AdjacencyMatrixGraph_neighbors(AdjacencyMatrixGraph* self, PyObject* args)
154+
{
155+
const char *node;
156+
if (!PyArg_ParseTuple(args, "s", &node)) {
157+
return NULL;
158+
}
159+
160+
std::string key(node);
161+
if (self->matrix.find(key) == self->matrix.end()) {
162+
PyErr_SetString(PyExc_KeyError, "Node not found");
163+
return NULL;
164+
}
165+
166+
PyObject* list = PyList_New(0);
167+
for (const auto& pair : self->matrix[key]) {
168+
if (pair.second) {
169+
const std::string& neighbor_name = pair.first;
170+
AdjacencyMatrixGraphNode* neighbor = self->node_map[neighbor_name];
171+
Py_INCREF(neighbor);
172+
PyList_Append(list, (PyObject*)neighbor);
173+
}
174+
}
175+
return list;
176+
}
177+
178+
179+
static PyObject* AdjacencyMatrixGraph_num_vertices(AdjacencyMatrixGraph* self, PyObject* Py_UNUSED(ignored))
180+
{
181+
return PyLong_FromSize_t(self->nodes.size());
182+
}
183+
184+
static PyObject* AdjacencyMatrixGraph_num_edges(AdjacencyMatrixGraph* self, PyObject* Py_UNUSED(ignored))
185+
{
186+
size_t count = 0;
187+
for (const auto& row : self->matrix) {
188+
for (const auto& entry : row.second) {
189+
if (entry.second)
190+
count++;
191+
}
192+
}
193+
return PyLong_FromSize_t(count);
194+
}
195+
196+
static PyObject* AdjacencyMatrixGraph_get_edge(AdjacencyMatrixGraph* self, PyObject* args)
197+
{
198+
const char *source, *target;
199+
if (!PyArg_ParseTuple(args, "ss", &source, &target))
200+
return NULL;
201+
202+
std::string key = std::string(source) + "_" + std::string(target);
203+
auto it = self->edge_weights.find(key);
204+
if (it != self->edge_weights.end()) {
205+
return reinterpret_cast<PyObject*>(it->second);
206+
}
207+
Py_RETURN_NONE;
208+
}
209+
210+
static PyObject* AdjacencyMatrixGraph_is_adjacent(AdjacencyMatrixGraph* self, PyObject* args)
211+
{
212+
const char *source, *target;
213+
if (!PyArg_ParseTuple(args, "ss", &source, &target))
214+
return NULL;
215+
216+
std::string src(source);
217+
std::string dst(target);
218+
if (self->matrix.find(src) == self->matrix.end())
219+
Py_RETURN_FALSE;
220+
221+
auto& row = self->matrix[src];
222+
if (row.find(dst) != row.end() && row[dst])
223+
Py_RETURN_TRUE;
224+
225+
Py_RETURN_FALSE;
226+
}
227+
228+
static PyMethodDef AdjacencyMatrixGraph_methods[] = {
229+
{"add_edge", (PyCFunction)AdjacencyMatrixGraph_add_edge, METH_VARARGS | METH_KEYWORDS, "Add an edge between two nodes."},
230+
{"remove_edge", (PyCFunction)AdjacencyMatrixGraph_remove_edge, METH_VARARGS, "Remove an edge between two nodes."},
231+
{"neighbors", (PyCFunction)AdjacencyMatrixGraph_neighbors, METH_VARARGS, "Return neighbors of a node."},
232+
{"num_vertices", (PyCFunction)AdjacencyMatrixGraph_num_vertices, METH_NOARGS, "Return number of vertices."},
233+
{"num_edges", (PyCFunction)AdjacencyMatrixGraph_num_edges, METH_NOARGS, "Return number of edges."},
234+
{"get_edge", (PyCFunction)AdjacencyMatrixGraph_get_edge, METH_VARARGS, "Return the edge object between two nodes if exists."},
235+
{"is_adjacent", (PyCFunction)AdjacencyMatrixGraph_is_adjacent, METH_VARARGS, "Check if there is an edge between two nodes."},
236+
{NULL}
237+
};
238+
239+
PyTypeObject AdjacencyMatrixGraphType = {
240+
PyVarObject_HEAD_INIT(NULL, 0)
241+
.tp_name = "_graph.AdjacencyMatrixGraph",
242+
.tp_basicsize = sizeof(AdjacencyMatrixGraph),
243+
.tp_itemsize = 0,
244+
.tp_dealloc = (destructor)AdjacencyMatrixGraph_dealloc,
245+
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
246+
.tp_doc = "Adjacency matrix graph",
247+
.tp_methods = AdjacencyMatrixGraph_methods,
248+
.tp_new = AdjacencyMatrixGraph_new,
249+
.tp_dictoffset = offsetof(AdjacencyMatrixGraph, dict),
250+
};
251+
252+
#endif

0 commit comments

Comments
 (0)