Skip to content

Commit 49174dc

Browse files
Prerak SinghPrerak Singh
authored andcommitted
added llvm backend for adjacency list graphs
1 parent a28aad8 commit 49174dc

File tree

11 files changed

+1846
-42
lines changed

11 files changed

+1846
-42
lines changed

pydatastructs/graphs/_backend/cpp/AdjacencyList.hpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -340,9 +340,6 @@ static PyObject* AdjacencyListGraph_add_edge(AdjacencyListGraph* self, PyObject*
340340
Py_RETURN_NONE;
341341
}
342342

343-
344-
345-
346343
static PyMethodDef AdjacencyListGraph_methods[] = {
347344
{"add_vertex", (PyCFunction)AdjacencyListGraph_add_vertex, METH_VARARGS, "Add a vertex to the graph"},
348345
{"add_edge", (PyCFunction)AdjacencyListGraph_add_edge, METH_VARARGS, "Add an edge to the graph"},
Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
1+
#include <Python.h>
2+
#include <string>
3+
#include <cstring>
4+
#include <stdexcept>
5+
#include <memory>
6+
#include <unordered_map>
7+
8+
extern PyTypeObject AdjacencyListGraphLLVMType;
9+
10+
typedef void* (*GraphInitFunc)();
11+
typedef int (*AddVertexFunc)(void*, const char*, int);
12+
typedef int (*AddEdgeFunc)(void*, const char*, int, const char*, int, double);
13+
typedef int (*IsAdjacentFunc)(void*, const char*, int, const char*, int);
14+
typedef int (*RemoveVertexFunc)(void*, const char*, int);
15+
typedef int (*RemoveEdgeFunc)(void*, const char*, int, const char*, int);
16+
typedef void (*GraphCleanupFunc)(void*);
17+
18+
static GraphInitFunc llvm_graph_init = nullptr;
19+
static AddVertexFunc llvm_add_vertex = nullptr;
20+
static AddEdgeFunc llvm_add_edge = nullptr;
21+
static IsAdjacentFunc llvm_is_adjacent = nullptr;
22+
static RemoveVertexFunc llvm_remove_vertex = nullptr;
23+
static RemoveEdgeFunc llvm_remove_edge = nullptr;
24+
static GraphCleanupFunc llvm_graph_cleanup = nullptr;
25+
26+
static void* llvm_execution_engine = nullptr;
27+
static bool llvm_backend_initialized = false;
28+
29+
typedef struct {
30+
PyObject_HEAD
31+
void* llvm_graph_ptr;
32+
bool is_valid;
33+
} AdjacencyListGraphLLVM;
34+
35+
static int safe_strlen(const char* str) {
36+
return str ? static_cast<int>(strlen(str)) : 0;
37+
}
38+
39+
static PyObject* initialize_llvm_backend(PyObject* self, PyObject* args) {
40+
PyObject* func_dict;
41+
PyObject* ee_obj;
42+
43+
if (!PyArg_ParseTuple(args, "OO", &func_dict, &ee_obj)) {
44+
return nullptr;
45+
}
46+
47+
if (!PyDict_Check(func_dict)) {
48+
PyErr_SetString(PyExc_TypeError, "First argument must be a dictionary");
49+
return nullptr;
50+
}
51+
52+
llvm_execution_engine = PyLong_AsVoidPtr(ee_obj);
53+
if (PyErr_Occurred()) {
54+
PyErr_SetString(PyExc_ValueError, "Invalid execution engine object");
55+
return nullptr;
56+
}
57+
58+
PyObject* init_ptr = PyDict_GetItemString(func_dict, "graph_init");
59+
PyObject* add_vertex_ptr = PyDict_GetItemString(func_dict, "add_vertex");
60+
PyObject* add_edge_ptr = PyDict_GetItemString(func_dict, "add_edge");
61+
PyObject* is_adjacent_ptr = PyDict_GetItemString(func_dict, "is_adjacent");
62+
PyObject* remove_vertex_ptr = PyDict_GetItemString(func_dict, "remove_vertex");
63+
PyObject* remove_edge_ptr = PyDict_GetItemString(func_dict, "remove_edge");
64+
PyObject* cleanup_ptr = PyDict_GetItemString(func_dict, "graph_cleanup");
65+
66+
if (!init_ptr || !add_vertex_ptr || !add_edge_ptr || !is_adjacent_ptr ||
67+
!remove_vertex_ptr || !remove_edge_ptr || !cleanup_ptr) {
68+
PyErr_SetString(PyExc_ValueError, "Missing required function pointers in dictionary");
69+
return nullptr;
70+
}
71+
72+
llvm_graph_init = (GraphInitFunc)PyLong_AsVoidPtr(init_ptr);
73+
llvm_add_vertex = (AddVertexFunc)PyLong_AsVoidPtr(add_vertex_ptr);
74+
llvm_add_edge = (AddEdgeFunc)PyLong_AsVoidPtr(add_edge_ptr);
75+
llvm_is_adjacent = (IsAdjacentFunc)PyLong_AsVoidPtr(is_adjacent_ptr);
76+
llvm_remove_vertex = (RemoveVertexFunc)PyLong_AsVoidPtr(remove_vertex_ptr);
77+
llvm_remove_edge = (RemoveEdgeFunc)PyLong_AsVoidPtr(remove_edge_ptr);
78+
llvm_graph_cleanup = (GraphCleanupFunc)PyLong_AsVoidPtr(cleanup_ptr);
79+
80+
if (PyErr_Occurred()) {
81+
PyErr_SetString(PyExc_ValueError, "Failed to convert function pointers");
82+
return nullptr;
83+
}
84+
85+
if (!llvm_graph_init || !llvm_add_vertex || !llvm_add_edge ||
86+
!llvm_is_adjacent || !llvm_remove_vertex || !llvm_remove_edge ||
87+
!llvm_graph_cleanup) {
88+
PyErr_SetString(PyExc_ValueError, "One or more function pointers are null");
89+
return nullptr;
90+
}
91+
92+
llvm_backend_initialized = true;
93+
94+
Py_RETURN_NONE;
95+
}
96+
97+
static bool check_llvm_backend() {
98+
return llvm_backend_initialized && llvm_graph_init && llvm_add_vertex &&
99+
llvm_add_edge && llvm_is_adjacent && llvm_remove_vertex &&
100+
llvm_remove_edge && llvm_graph_cleanup;
101+
}
102+
103+
static void AdjacencyListGraphLLVM_dealloc(AdjacencyListGraphLLVM* self) {
104+
if (self->is_valid && self->llvm_graph_ptr && llvm_graph_cleanup) {
105+
llvm_graph_cleanup(self->llvm_graph_ptr);
106+
self->llvm_graph_ptr = nullptr;
107+
self->is_valid = false;
108+
}
109+
Py_TYPE(self)->tp_free((PyObject*)self);
110+
}
111+
112+
static PyObject* AdjacencyListGraphLLVM_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
113+
AdjacencyListGraphLLVM* self = (AdjacencyListGraphLLVM*)type->tp_alloc(type, 0);
114+
if (!self) {
115+
return nullptr;
116+
}
117+
118+
self->llvm_graph_ptr = nullptr;
119+
self->is_valid = false;
120+
121+
return (PyObject*)self;
122+
}
123+
124+
static int AdjacencyListGraphLLVM_init(AdjacencyListGraphLLVM* self, PyObject* args, PyObject* kwds) {
125+
if (!check_llvm_backend()) {
126+
PyErr_SetString(PyExc_RuntimeError,
127+
"LLVM backend not initialized. Call initialize_llvm_backend() first.");
128+
return -1;
129+
}
130+
131+
self->llvm_graph_ptr = llvm_graph_init();
132+
if (!self->llvm_graph_ptr) {
133+
PyErr_SetString(PyExc_RuntimeError, "Failed to initialize LLVM graph");
134+
return -1;
135+
}
136+
137+
self->is_valid = true;
138+
return 0;
139+
}
140+
141+
static PyObject* AdjacencyListGraphLLVM_add_vertex(AdjacencyListGraphLLVM* self, PyObject* args) {
142+
const char* name;
143+
144+
if (!PyArg_ParseTuple(args, "s", &name)) {
145+
return nullptr;
146+
}
147+
148+
if (!self->is_valid || !self->llvm_graph_ptr) {
149+
PyErr_SetString(PyExc_RuntimeError, "Invalid graph object");
150+
return nullptr;
151+
}
152+
153+
if (!check_llvm_backend()) {
154+
PyErr_SetString(PyExc_RuntimeError, "LLVM backend not properly initialized");
155+
return nullptr;
156+
}
157+
158+
int name_len = safe_strlen(name);
159+
int result = llvm_add_vertex(self->llvm_graph_ptr, name, name_len);
160+
161+
if (result != 0) {
162+
if (result == -1) {
163+
PyErr_SetString(PyExc_ValueError, "Vertex with this name already exists");
164+
} else {
165+
PyErr_Format(PyExc_RuntimeError, "Failed to add vertex (error code: %d)", result);
166+
}
167+
return nullptr;
168+
}
169+
170+
Py_RETURN_NONE;
171+
}
172+
173+
static PyObject* AdjacencyListGraphLLVM_add_edge(AdjacencyListGraphLLVM* self, PyObject* args) {
174+
const char* source;
175+
const char* target;
176+
double weight = 1.0;
177+
178+
if (!PyArg_ParseTuple(args, "ss|d", &source, &target, &weight)) {
179+
return nullptr;
180+
}
181+
182+
if (!self->is_valid || !self->llvm_graph_ptr) {
183+
PyErr_SetString(PyExc_RuntimeError, "Invalid graph object");
184+
return nullptr;
185+
}
186+
187+
if (!check_llvm_backend()) {
188+
PyErr_SetString(PyExc_RuntimeError, "LLVM backend not properly initialized");
189+
return nullptr;
190+
}
191+
192+
int src_len = safe_strlen(source);
193+
int tgt_len = safe_strlen(target);
194+
int result = llvm_add_edge(self->llvm_graph_ptr, source, src_len, target, tgt_len, weight);
195+
196+
if (result != 0) {
197+
if (result == -1) {
198+
PyErr_SetString(PyExc_ValueError, "Source vertex not found");
199+
} else if (result == -2) {
200+
PyErr_SetString(PyExc_ValueError, "Target vertex not found");
201+
} else {
202+
PyErr_Format(PyExc_RuntimeError, "Failed to add edge (error code: %d)", result);
203+
}
204+
return nullptr;
205+
}
206+
207+
Py_RETURN_NONE;
208+
}
209+
210+
static PyObject* AdjacencyListGraphLLVM_is_adjacent(AdjacencyListGraphLLVM* self, PyObject* args) {
211+
const char* node1;
212+
const char* node2;
213+
214+
if (!PyArg_ParseTuple(args, "ss", &node1, &node2)) {
215+
return nullptr;
216+
}
217+
218+
if (!self->is_valid || !self->llvm_graph_ptr) {
219+
PyErr_SetString(PyExc_RuntimeError, "Invalid graph object");
220+
return nullptr;
221+
}
222+
223+
if (!check_llvm_backend()) {
224+
PyErr_SetString(PyExc_RuntimeError, "LLVM backend not properly initialized");
225+
return nullptr;
226+
}
227+
228+
int node1_len = safe_strlen(node1);
229+
int node2_len = safe_strlen(node2);
230+
int result = llvm_is_adjacent(self->llvm_graph_ptr, node1, node1_len, node2, node2_len);
231+
232+
if (result == 1) {
233+
Py_RETURN_TRUE;
234+
} else {
235+
Py_RETURN_FALSE;
236+
}
237+
}
238+
239+
static PyObject* AdjacencyListGraphLLVM_remove_vertex(AdjacencyListGraphLLVM* self, PyObject* args) {
240+
const char* name;
241+
242+
if (!PyArg_ParseTuple(args, "s", &name)) {
243+
return nullptr;
244+
}
245+
246+
if (!self->is_valid || !self->llvm_graph_ptr) {
247+
PyErr_SetString(PyExc_RuntimeError, "Invalid graph object");
248+
return nullptr;
249+
}
250+
251+
if (!check_llvm_backend()) {
252+
PyErr_SetString(PyExc_RuntimeError, "LLVM backend not properly initialized");
253+
return nullptr;
254+
}
255+
256+
int name_len = safe_strlen(name);
257+
int result = llvm_remove_vertex(self->llvm_graph_ptr, name, name_len);
258+
259+
if (result != 0) {
260+
if (result == -1) {
261+
PyErr_SetString(PyExc_ValueError, "Vertex not found");
262+
} else {
263+
PyErr_Format(PyExc_RuntimeError, "Failed to remove vertex (error code: %d)", result);
264+
}
265+
return nullptr;
266+
}
267+
268+
Py_RETURN_NONE;
269+
}
270+
271+
static PyObject* AdjacencyListGraphLLVM_remove_edge(AdjacencyListGraphLLVM* self, PyObject* args) {
272+
const char* source;
273+
const char* target;
274+
275+
if (!PyArg_ParseTuple(args, "ss", &source, &target)) {
276+
return nullptr;
277+
}
278+
279+
if (!self->is_valid || !self->llvm_graph_ptr) {
280+
PyErr_SetString(PyExc_RuntimeError, "Invalid graph object");
281+
return nullptr;
282+
}
283+
284+
if (!check_llvm_backend()) {
285+
PyErr_SetString(PyExc_RuntimeError, "LLVM backend not properly initialized");
286+
return nullptr;
287+
}
288+
289+
int src_len = safe_strlen(source);
290+
int tgt_len = safe_strlen(target);
291+
int result = llvm_remove_edge(self->llvm_graph_ptr, source, src_len, target, tgt_len);
292+
293+
if (result != 0) {
294+
if (result == -1) {
295+
PyErr_SetString(PyExc_ValueError, "Source vertex not found");
296+
} else if (result == -2) {
297+
PyErr_SetString(PyExc_ValueError, "Target vertex not found");
298+
} else {
299+
PyErr_Format(PyExc_RuntimeError, "Failed to remove edge (error code: %d)", result);
300+
}
301+
return nullptr;
302+
}
303+
304+
Py_RETURN_NONE;
305+
}
306+
307+
static PyMethodDef AdjacencyListGraphLLVM_methods[] = {
308+
{"add_vertex", (PyCFunction)AdjacencyListGraphLLVM_add_vertex, METH_VARARGS,
309+
"Add a vertex to the graph"},
310+
{"add_edge", (PyCFunction)AdjacencyListGraphLLVM_add_edge, METH_VARARGS,
311+
"Add an edge to the graph"},
312+
{"is_adjacent", (PyCFunction)AdjacencyListGraphLLVM_is_adjacent, METH_VARARGS,
313+
"Check if two vertices are adjacent"},
314+
{"remove_vertex", (PyCFunction)AdjacencyListGraphLLVM_remove_vertex, METH_VARARGS,
315+
"Remove a vertex from the graph"},
316+
{"remove_edge", (PyCFunction)AdjacencyListGraphLLVM_remove_edge, METH_VARARGS,
317+
"Remove an edge from the graph"},
318+
{nullptr}
319+
};
320+
321+
PyTypeObject AdjacencyListGraphLLVMType = {
322+
PyVarObject_HEAD_INIT(nullptr, 0)
323+
"llvm_graph.AdjacencyListGraphLLVM", // tp_name
324+
sizeof(AdjacencyListGraphLLVM), // tp_basicsize
325+
0, // tp_itemsize
326+
(destructor)AdjacencyListGraphLLVM_dealloc, // tp_dealloc
327+
0, // tp_vectorcall_offset
328+
0, // tp_getattr
329+
0, // tp_setattr
330+
0, // tp_as_async
331+
0, // tp_repr
332+
0, // tp_as_number
333+
0, // tp_as_sequence
334+
0, // tp_as_mapping
335+
0, // tp_hash
336+
0, // tp_call
337+
0, // tp_str
338+
0, // tp_getattro
339+
0, // tp_setattro
340+
0, // tp_as_buffer
341+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags
342+
"LLVM-backed adjacency list graph", // tp_doc
343+
0, // tp_traverse
344+
0, // tp_clear
345+
0, // tp_richcompare
346+
0, // tp_weaklistoffset
347+
0, // tp_iter
348+
0, // tp_iternext
349+
AdjacencyListGraphLLVM_methods, // tp_methods
350+
0, // tp_members
351+
0, // tp_getset
352+
0, // tp_base
353+
0, // tp_dict
354+
0, // tp_descr_get
355+
0, // tp_descr_set
356+
0, // tp_dictoffset
357+
(initproc)AdjacencyListGraphLLVM_init, // tp_init
358+
0, // tp_alloc
359+
AdjacencyListGraphLLVM_new, // tp_new
360+
};

0 commit comments

Comments
 (0)