Skip to content

Commit bd626ef

Browse files
author
Release Manager
committed
gh-39518: line_graph for multigraphs We generalize the `line_graph` method to graphs possibly with multiple edges and/or loops. It is useful in order to compute the line-graph of a multigraph but also to generate the edge-colorings of a multigraph from the `all_graph_coloring` method. ### 📝 Checklist - [x] The title is concise and informative. - [x] The description explains in detail what this PR is about. - [ ] I have linked a relevant issue or discussion. - [x] I have created tests covering the changes. - [x] I have updated the documentation and checked the documentation preview. URL: #39518 Reported by: Fabien Vignes-Tourneret Reviewer(s): David Coudert, Frédéric Chapoton
2 parents 72c6188 + 34bece3 commit bd626ef

File tree

1 file changed

+79
-13
lines changed

1 file changed

+79
-13
lines changed

src/sage/graphs/line_graph.pyx

Lines changed: 79 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ Functions
127127
---------
128128
"""
129129

130+
from sage.structure.element cimport parent
131+
130132

131133
def is_line_graph(g, certificate=False):
132134
r"""
@@ -263,21 +265,32 @@ def is_line_graph(g, certificate=False):
263265
return True
264266
265267
266-
def line_graph(g, labels=True):
268+
def line_graph(g, labels=True, return_labels=False):
267269
"""
268-
Return the line graph of the (di)graph ``g``.
270+
Return the line graph of the (di)graph ``g`` (multiedges and loops allowed).
269271
270272
INPUT:
271273
272274
- ``labels`` -- boolean (default: ``True``); whether edge labels should be
273275
taken in consideration. If ``labels=True``, the vertices of the line graph
274-
will be triples ``(u,v,label)``, and pairs of vertices otherwise.
275-
276-
The line graph of an undirected graph G is an undirected graph H such that
277-
the vertices of H are the edges of G and two vertices e and f of H are
276+
will be triples ``(u,v,label)``, and pairs of vertices otherwise. In case
277+
of multiple edges, the vertices of the line graph will be triples
278+
``(u,v,an integer)``.
279+
280+
- ``return_labels`` -- boolean (default: ``False``); whether edge labels should
281+
be stored or not. If g has multiple edges and if ``return_labels=True``, the
282+
method returns a list the first element of which is the line-graph of g
283+
and the second element is a dictionary {vertex of the line-graph: former
284+
corresponding edge with its original label}. If ``return_labels=False``,
285+
the method returns only the line-graph.
286+
287+
The line graph of an undirected graph G is an undirected simple graph H such
288+
that the vertices of H are the edges of G and two vertices e and f of H are
278289
adjacent if e and f share a common vertex in G. In other words, an edge in H
279290
represents a path of length 2 in G.
280291
292+
Loops are not adjacent to themselves.
293+
281294
The line graph of a directed graph G is a directed graph H such that the
282295
vertices of H are the edges of G and two vertices e and f of H are adjacent
283296
if e and f share a common vertex in G and the terminal vertex of e is the
@@ -311,7 +324,7 @@ def line_graph(g, labels=True):
311324
(1, 2, None),
312325
(1, 3, None),
313326
(2, 3, None)]
314-
sage: h.am() # needs sage.modules
327+
sage: h.am() # needs sage.modules
315328
[0 1 1 1 1 0]
316329
[1 0 1 1 0 1]
317330
[1 1 0 0 1 1]
@@ -321,7 +334,7 @@ def line_graph(g, labels=True):
321334
sage: h2 = g.line_graph(labels=False)
322335
sage: h2.vertices(sort=True)
323336
[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
324-
sage: h2.am() == h.am() # needs sage.modules
337+
sage: h2.am() == h.am() # needs sage.modules
325338
True
326339
sage: g = DiGraph([[1..4], lambda i,j: i < j])
327340
sage: h = g.line_graph()
@@ -338,6 +351,40 @@ def line_graph(g, labels=True):
338351
((1, 3, None), (3, 4, None), None),
339352
((2, 3, None), (3, 4, None), None)]
340353
354+
Examples with multiple edges::
355+
356+
sage: L = Graph([(0,1),(0,1),(1,2)],multiedges=True).line_graph()
357+
sage: L.edges()
358+
[((0, 1, 0), (0, 1, 1), None), ((0, 1, 1), (1, 2, 2), None),
359+
((0, 1, 0), (1, 2, 2), None)]
360+
sage: G = Graph([(0,1),(0,1,'a'),(0,1,'b'),(0,2),(1,2,'c')],
361+
....: multiedges=True)
362+
sage: L = G.line_graph(False,True)
363+
sage: L[0].edges()
364+
[((0, 1, 1), (0, 1, 2), None), ((0, 1, 0), (0, 1, 2), None),
365+
((0, 1, 2), (0, 2, 3), None), ((0, 1, 2), (1, 2, 4), None), ((0, 1, 0),
366+
(0, 1, 1), None), ((0, 1, 1), (0, 2, 3), None), ((0, 1, 1),
367+
(1, 2, 4), None), ((0, 1, 0), (0, 2, 3), None), ((0, 1, 0),
368+
(1, 2, 4), None), ((0, 2, 3), (1, 2, 4), None)]
369+
sage: L[1]
370+
{(0, 1, 0): (0, 1, None),
371+
(0, 1, 1): (0, 1, 'b'),
372+
(0, 1, 2): (0, 1, 'a'),
373+
(0, 2, 3): (0, 2, None),
374+
(1, 2, 4): (1, 2, 'c')}
375+
sage: g = DiGraph([(0,1),(0,1),(1,2)],multiedges=True)
376+
sage: g.line_graph().edges()
377+
[((0, 1, 1), (1, 2, 2), None), ((0, 1, 0), (1, 2, 2), None)]
378+
379+
An example with a loop::
380+
381+
sage: g = Graph([(0,0),(0,1),(0,2),(1,2)],multiedges=True,loops=True)
382+
sage: L = g.line_graph()
383+
sage: L.edges()
384+
[((0, 0, None), (0, 1, None), None), ((0, 0, None), (0, 2, None), None),
385+
((0, 1, None), (0, 2, None), None), ((0, 1, None), (1, 2, None), None),
386+
((0, 2, None), (1, 2, None), None)]
387+
341388
TESTS:
342389
343390
:issue:`13787`::
@@ -351,17 +398,31 @@ def line_graph(g, labels=True):
351398
"""
352399
cdef dict conflicts = {}
353400
cdef list elist = []
401+
cdef dict origlabels_dic = {} # stores original labels of edges in case of multiple edges
402+
403+
multiple = g.has_multiple_edges()
404+
405+
if multiple:
406+
# As the edges of g are the vertices of its line graph, we need to distinguish between the mutliple edges of g.
407+
# To this aim, we assign to each edge of g an integer label (between 0 and g.size() - 1) and set labels to True
408+
# in order to keep these labels during the construction of the line graph.
409+
labels = True
410+
origlabels_dic = {(u, v, id): (u, v, label)
411+
for id, (u, v, label) in enumerate(g.edge_iterator())}
412+
g = parent(g)([g, origlabels_dic.keys()], format='vertices_and_edges', multiedges=True)
354413
355-
g._scream_if_not_simple()
356414
if g._directed:
357415
from sage.graphs.digraph import DiGraph
358416
G = DiGraph()
359417
G.add_vertices(g.edge_iterator(labels=labels))
360418
for v in g:
361419
# Connect appropriate incident edges of the vertex v
362420
G.add_edges((e, f) for e in g.incoming_edge_iterator(v, labels=labels)
363-
for f in g.outgoing_edge_iterator(v, labels=labels))
364-
return G
421+
for f in g.outgoing_edge_iterator(v, labels=labels))
422+
if return_labels and multiple:
423+
return [G, origlabels_dic]
424+
else:
425+
return G
365426
366427
from sage.graphs.graph import Graph
367428
G = Graph()
@@ -393,7 +454,7 @@ def line_graph(g, labels=True):
393454
elist = []
394455
395456
# Add the edge to the list, according to hashes, as previously
396-
for e in g.edge_iterator(v, labels=labels):
457+
for e in g.edge_iterator(v, labels=labels): # iterates over the edges incident to v
397458
if hash(e[0]) < hash(e[1]):
398459
elist.append(e)
399460
elif hash(e[0]) > hash(e[1]):
@@ -402,12 +463,17 @@ def line_graph(g, labels=True):
402463
elist.append(conflicts[e])
403464
404465
# All pairs of elements in elist are edges of the line graph
466+
# if g has multiple edges, some pairs appear more than once but as G is defined as simple,
467+
# the corresponding edges are not added as multiedges (as it should be).
405468
while elist:
406469
x = elist.pop()
407470
for y in elist:
408471
G.add_edge(x, y)
409472
410-
return G
473+
if return_labels and multiple:
474+
return [G, origlabels_dic]
475+
else:
476+
return G
411477
412478
413479
def root_graph(g, verbose=False):

0 commit comments

Comments
 (0)