Skip to content

Commit eb9c9ca

Browse files
authored
Merge pull request #756 from igraph/feat/realize_bipartite_degree_sequence
feat: Realize_Bipartite_Degree_Sequence()
2 parents bbf8bde + d4c8fe0 commit eb9c9ca

File tree

2 files changed

+147
-0
lines changed

2 files changed

+147
-0
lines changed

src/_igraph/graphobject.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2975,6 +2975,61 @@ PyObject *igraphmodule_Graph_Realize_Degree_Sequence(PyTypeObject *type,
29752975
}
29762976

29772977

2978+
/** \ingroup python_interface_graph
2979+
* \brief Generates a graph with a specified degree sequence
2980+
* \return a reference to the newly generated Python igraph object
2981+
* \sa igraph_realize_bipartite_degree_sequence
2982+
*/
2983+
PyObject *igraphmodule_Graph_Realize_Bipartite_Degree_Sequence(PyTypeObject *type,
2984+
PyObject *args, PyObject *kwds) {
2985+
2986+
igraph_vector_int_t degrees1, degrees2;
2987+
igraph_edge_type_sw_t allowed_edge_types = IGRAPH_SIMPLE_SW;
2988+
igraph_realize_degseq_t method = IGRAPH_REALIZE_DEGSEQ_SMALLEST;
2989+
PyObject *degrees1_o, *degrees2_o;
2990+
PyObject *edge_types_o = Py_None, *method_o = Py_None;
2991+
igraphmodule_GraphObject *self;
2992+
igraph_t g;
2993+
2994+
static char *kwlist[] = { "degrees1", "degrees2", "allowed_edge_types", "method", NULL };
2995+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist,
2996+
&degrees1_o, &degrees2_o, &edge_types_o, &method_o))
2997+
return NULL;
2998+
2999+
/* allowed edge types */
3000+
if (igraphmodule_PyObject_to_edge_type_sw_t(edge_types_o, &allowed_edge_types))
3001+
return NULL;
3002+
3003+
/* methods */
3004+
if (igraphmodule_PyObject_to_realize_degseq_t(method_o, &method))
3005+
return NULL;
3006+
3007+
/* First degree vector */
3008+
if (igraphmodule_PyObject_to_vector_int_t(degrees1_o, &degrees1))
3009+
return NULL;
3010+
3011+
/* Second degree vector */
3012+
if (igraphmodule_PyObject_to_vector_int_t(degrees2_o, &degrees2)) {
3013+
igraph_vector_int_destroy(&degrees1);
3014+
return NULL;
3015+
}
3016+
3017+
if (igraph_realize_bipartite_degree_sequence(&g, &degrees1, &degrees2, allowed_edge_types, method)) {
3018+
igraph_vector_int_destroy(&degrees1);
3019+
igraph_vector_int_destroy(&degrees2);
3020+
igraphmodule_handle_igraph_error();
3021+
return NULL;
3022+
}
3023+
3024+
igraph_vector_int_destroy(&degrees1);
3025+
igraph_vector_int_destroy(&degrees2);
3026+
3027+
CREATE_GRAPH_FROM_TYPE(self, g, type);
3028+
3029+
return (PyObject *) self;
3030+
}
3031+
3032+
29783033
/** \ingroup python_interface_graph
29793034
* \brief Generates a graph based on vertex types and connection preferences
29803035
* \return a reference to the newly generated Python igraph object
@@ -14247,6 +14302,37 @@ struct PyMethodDef igraphmodule_Graph_methods[] = {
1424714302
" See Horvát and Modes (2021) for details.\n"
1424814303
},
1424914304

14305+
{"Realize_Bipartite_Degree_Sequence", (PyCFunction) igraphmodule_Graph_Realize_Bipartite_Degree_Sequence,
14306+
METH_VARARGS | METH_CLASS | METH_KEYWORDS,
14307+
"Realize_Bipartite_Degree_Sequence(degrees1, degrees2, allowed_edge_types=\"simple\", method=\"smallest\")\n--\n\n"
14308+
"Generates a bipartite graph from the degree sequences of its partitions.\n"
14309+
"\n"
14310+
"This method implements a Havel-Hakimi style graph construction for biparite\n"
14311+
"graphs. In each step, the algorithm picks two vertices in a deterministic\n"
14312+
"manner and connects them. The way the vertices are picked is defined by the\n"
14313+
"C{method} parameter. The allowed edge types (i.e. whether multi-edges are allowed)\n"
14314+
"are specified in the C{allowed_edge_types} parameter. Self-loops are never created,\n"
14315+
"since a graph with self-loops is not bipartite.\n"
14316+
"\n"
14317+
"@param degrees1: the degrees of the first partition.\n"
14318+
"@param degrees2: the degrees of the second partition.\n"
14319+
"@param allowed_edge_types: controls whether multi-edges are allowed\n"
14320+
" during the generation process. Possible values are:\n"
14321+
"\n"
14322+
" - C{\"simple\"}: simple graphs (no multi-edges)\n"
14323+
" - C{\"multi\"}: multi-edges allowed\n"
14324+
"\n"
14325+
"@param method: controls how the vertices are selected during the generation\n"
14326+
" process. Possible values are:\n"
14327+
"\n"
14328+
" - C{smallest}: The vertex with smallest remaining degree first.\n"
14329+
" - C{largest}: The vertex with the largest remaining degree first.\n"
14330+
" - C{index}: The vertices are selected in order of their index.\n"
14331+
"\n"
14332+
" The smallest C{smallest} method is guaranteed to produce a connected graph,\n"
14333+
" if one exists."
14334+
},
14335+
1425014336
// interface to igraph_ring
1425114337
{"Ring", (PyCFunction) igraphmodule_Graph_Ring,
1425214338
METH_VARARGS | METH_CLASS | METH_KEYWORDS,

tests/test_generators.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,67 @@ def testRealizeDegreeSequence(self):
249249
self.assertFalse(g.is_directed())
250250
self.assertTrue(g.degree() == degrees)
251251

252+
def testRealizeBipartiteDegreeSequence(self):
253+
deg1 = [2, 2]
254+
deg2 = [1, 1, 2]
255+
g = Graph.Realize_Bipartite_Degree_Sequence(
256+
deg1,
257+
deg2,
258+
"simple",
259+
"smallest",
260+
)
261+
self.assertFalse(g.is_directed())
262+
self.assertTrue(g.is_connected())
263+
self.assertTrue(g.degree() == deg1 + deg2)
264+
265+
g = Graph.Realize_Bipartite_Degree_Sequence(
266+
deg1,
267+
deg2,
268+
"simple",
269+
"largest",
270+
)
271+
self.assertFalse(g.is_directed())
272+
self.assertTrue(g.degree() == deg1 + deg2)
273+
274+
g = Graph.Realize_Bipartite_Degree_Sequence(
275+
deg1,
276+
deg2,
277+
"simple",
278+
"index",
279+
)
280+
self.assertFalse(g.is_directed())
281+
self.assertTrue(g.degree() == deg1 + deg2)
282+
283+
deg1 = [3, 1, 1]
284+
deg2 = [2, 3]
285+
self.assertRaises(
286+
InternalError,
287+
Graph.Realize_Bipartite_Degree_Sequence,
288+
deg1,
289+
deg2,
290+
"simple",
291+
"smallest",
292+
)
293+
294+
self.assertRaises(
295+
InternalError,
296+
Graph.Realize_Bipartite_Degree_Sequence,
297+
deg1,
298+
deg2,
299+
"simple",
300+
"index",
301+
)
302+
303+
g = Graph.Realize_Bipartite_Degree_Sequence(
304+
deg1,
305+
deg2,
306+
"multi",
307+
"smallest",
308+
)
309+
self.assertFalse(g.is_directed())
310+
self.assertTrue(g.is_connected())
311+
self.assertTrue(g.degree() == deg1 + deg2)
312+
252313
def testKautz(self):
253314
g = Graph.Kautz(2, 2)
254315
deg_in = g.degree(mode="in")

0 commit comments

Comments
 (0)