Skip to content

Commit

Permalink
Implement simple cycle search Python binding
Browse files Browse the repository at this point in the history
  • Loading branch information
GenieTim committed Nov 13, 2024
1 parent 912f7d9 commit e67d9c4
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Dropped support for Python 3.8 as it has now reached its end of life.

- The C core of igraph was updated to version 0.10.15.
- Added `Graph.simple_cycles()` to find simple cycles in the graph.

## [0.11.8] - 2024-10-25

Expand Down
77 changes: 77 additions & 0 deletions src/_igraph/graphobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -7813,6 +7813,65 @@ PyObject *igraphmodule_Graph_minimum_cycle_basis(
return result_o;
}


PyObject *igraphmodule_Graph_simple_cycles(
igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds
) {
PyObject *mode_o = Py_None;
PyObject *output_o = Py_None;
PyObject *min_cycle_length_o = Py_None;
PyObject *max_cycle_length_o = Py_None;

// argument defaults: no cycle limits
igraph_integer_t mode = IGRAPH_OUT;
igraph_integer_t min_cycle_length = -1;
igraph_integer_t max_cycle_length = -1;
igraph_bool_t use_edges = false;

static char *kwlist[] = { "mode", "min_cycle_length", "max_cycle_length", "output" NULL };

if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO", kwlist, &mode_o, &min_cycle_length_o, &max_cycle_length_o, &output_o))
return NULL;

if (mode_o != Py_None && igraphmodule_PyObject_to_integer_t(mode_o, &mode))
return NULL;

if (min_cycle_length_o != Py_None && igraphmodule_PyObject_to_integer_t(min_cycle_length_o, &min_cycle_length))
return NULL;

if (max_cycle_length_o != Py_None && igraphmodule_PyObject_to_integer_t(max_cycle_length_o, &max_cycle_length))
return NULL;

if (igraphmodule_PyObject_to_vpath_or_epath(output_o, &use_edges))
return NULL;

igraph_vector_int_list_t vertices;
igraph_vector_int_list_init(&vertices, 0);
igraph_vector_int_list_t edges;
igraph_vector_int_list_init(&edges, 0);

if (igraph_simple_cycles(
&self->g, use_edges ? NULL : &vertices, use_edges ? &edges : NULL, mode, min_cycle_length, max_cycle_length
)) {
igraph_vector_int_list_destroy(&vertices);
igraph_vector_int_list_destroy(&edges);
igraphmodule_handle_igraph_error();
return NULL;
}

PyObject *result_o;

if (use_edges) {
result_o = igraphmodule_vector_int_list_t_to_PyList_of_tuples(&edges);
} else {
result_vertices_o = igraphmodule_vector_int_list_t_to_PyList_of_tuples(&vertices);
}
igraph_vector_int_list_destroy(&edges);
igraph_vector_int_list_destroy(&vertices);

return result_o;
}

/**********************************************************************
* Graph layout algorithms *
**********************************************************************/
Expand Down Expand Up @@ -16563,6 +16622,24 @@ struct PyMethodDef igraphmodule_Graph_methods[] = {
" no guarantees are given about the ordering of edge IDs within cycles.\n"
"@return: the cycle basis as a list of tuples containing edge IDs"
},
{"simple_cycles", (PyCFunction) igraphmodule_Graph_simple_cycles,
METH_VARARGS | METH_KEYWORDS,
"simple_cycles(mode=None, min_cycle_length=0, max_cycle_length=-1, output=\"vpath\")\n--\n\n"
"Finds simple cycles in a graph\n\n"
"@param mode: for directed graphs, specifies how the edge directions\n"
" should be taken into account. C{\"all\"} means that the edge directions\n"
" must be ignored, C{\"out\"} means that the edges must be oriented away\n"
" from the root, C{\"in\"} means that the edges must be oriented\n"
" towards the root. Ignored for undirected graphs.\n"
"@param min_cycle_length: the minimum number of vertices in a cycle\n"
" for it to be returned.\n"
"@param max_cycle_length: the maximum number of vertices in a cycle\n"
" for it to be considered.\n"
"@param output: determines what should be returned. If this is\n"
" C{\"vpath\"}, a list of tuples of vertex IDs will be returned. If this is\n"
" C{\"epath\"}, edge IDs are returned instead of vertex IDs.\n"
"@return: see the documentation of the C{output} parameter.\n"
},

/********************/
/* LAYOUT FUNCTIONS */
Expand Down
19 changes: 19 additions & 0 deletions tests/test_cycles.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,25 @@ def test_fundamental_cycles(self):
]
assert cycles == [[6, 7, 10], [8, 9, 10]]

def test_simple_cycles(self):
g = Graph(
[
(0, 1),
(1, 2),
(2, 0),
(0, 0),
(0, 3),
(3, 4),
(4, 5),
(5, 0),
]
)

vertices = g.simple_cycles(output="vpath")
edges = g.simple_cycles(output="epath")
assert len(vertices) == 3
assert len(edges) == 3

def test_minimum_cycle_basis(self):
g = Graph(
[
Expand Down

0 comments on commit e67d9c4

Please sign in to comment.