Skip to content

Commit

Permalink
split graph code into own file
Browse files Browse the repository at this point in the history
  • Loading branch information
nimrodVarga committed Oct 25, 2023
1 parent b3ce4a3 commit c63d6f0
Showing 1 changed file with 148 additions and 0 deletions.
148 changes: 148 additions & 0 deletions include/graph.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
typedef struct{
PyObject_HEAD
Index node_count;
Index* pre_neighbor_offsets;
PyArrayObject* edge_list;
} Graph;


static void Graph_dealloc(Graph* graph){
free(graph->pre_neighbor_offsets);
Py_DECREF(graph->edge_list);
Py_TYPE(graph)->tp_free((PyObject*)graph);
}


static PyObject* Graph_new(PyTypeObject* type, PyObject* args, PyObject* kwds){
Index node_count;
PyArrayObject* edges;

// ASSUMPTION: edge list should be sorted in the second argument or destination

if(!PyArg_ParseTuple(args, "OI", (PyObject**)&edges, &node_count)){
puts("no new graph without correct args");
return NULL;
}



// TODO: check edges.dtype in {np.uint32, np.uint64}


Graph* graph = (Graph*) type->tp_alloc(type, 0);

if(graph!=NULL){
// NOTE: if ASSUMPTION in count_neighbors is wrong, memory needs to be zeroed out before starting counting
graph->pre_neighbor_offsets = (Index*)malloc((node_count+1)*sizeof(Index));



ASSERT(graph->pre_neighbor_offsets);
}

return (PyObject*)graph;
}










static int Graph_init(Graph* graph, PyObject* args, PyObject* kwds){
PyArrayObject* edges;
Index node_count;

// ASSUMPTION: edge list should be sorted in the second argument

if(!PyArg_ParseTuple(args, "OI", (PyObject**)&edges, &node_count)){
puts("couldn't parse edge list");
return -1;
}

graph->node_count = node_count;

graph->edge_list = edges;

Py_INCREF(edges);





// TODO: MT or GPU
for(Index i=0; i<graph->node_count+1; i++){
graph->pre_neighbor_offsets[i]=0;
}

Index edge_count = (Index)PyArray_DIM(edges, 1);

for(Index e=0; e<edge_count; e++){
Node dst_node = *((Node*)PyArray_GETPTR2(edges, 1, e));
graph->pre_neighbor_offsets[Node_To_Index(dst_node)+1]++;
}

neighbor_counts_to_offsets(graph->node_count, graph->pre_neighbor_offsets);

return 0;
}

static Node src_node_at(Graph* g, Index i){
ASSERT(i < g->pre_neighbor_offsets[g->node_count]);

return *((Node*)PyArray_GETPTR2(g->edge_list, 0, i));
}

static Node dst_node_at(Graph* g, Index i){
ASSERT(i < g->pre_neighbor_offsets[g->node_count]);
return *((Node*)PyArray_GETPTR2(g->edge_list, 1, i));
}

static PyObject* Graph_print(Graph* graph, PyObject *Py_UNUSED(ignored)){
for(Index i=0;i<graph->node_count;i++){
Index c = graph->pre_neighbor_offsets[i+1]-graph->pre_neighbor_offsets[i];
Index o=graph->pre_neighbor_offsets[i];

for(Index ii=0; ii<c; ii++)
printf("(%u, %u), ", src_node_at(graph, o+ii), dst_node_at(graph, o+ii));
}
printf("\n");

Py_RETURN_NONE;
}

static PyObject* Graph_preneighborhood_count(Graph* graph, PyObject* args){
Node n;
if(!PyArg_ParseTuple(args, "I", &n)){
printf("If you don't provide proper arguments, you can't have any neighbor sampling.\nHow can you have any neighbor sampling if you don't provide proper arguments?\n");
return NULL;
}

Index pre_neighbor_count = graph->pre_neighbor_offsets[Node_To_Index(n)+1]-graph->pre_neighbor_offsets[Node_To_Index(n)];

return PyLong_FromIndex(pre_neighbor_count);
}



static PyMethodDef Graph_methods[] = {
{"print", (PyCFunction)Graph_print, METH_NOARGS, "print the graph"},
{"preneighborhood_count", (PyCFunction)Graph_preneighborhood_count, METH_VARARGS, "get the size of the pre-neighborhood of a node within the graph"},
{NULL}
};

static PyTypeObject GraphType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "GMSamplers.Graph",
.tp_doc = PyDoc_STR("GMSamplers graph"),
.tp_basicsize = sizeof(Graph),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_new = Graph_new,
.tp_init = (initproc) Graph_init,
.tp_dealloc = (destructor) Graph_dealloc,
.tp_methods = Graph_methods
};

0 comments on commit c63d6f0

Please sign in to comment.