Skip to content

Commit bd19340

Browse files
author
Release Manager
committed
sagemathgh-41245: Add Caterpillar() graph generator
The [caterpillar trees](https://en.wikipedia.org/wiki/Caterpillar_tree) form a well-known graph class but they currently cannot be easily generated using Sage despite the fact that a caterpillar can be succinctly described using its left-to-right degree sequence or (even more succinctly) its [spine](https://arxiv.org/pdf/1810.11744). This PR adds a function to generate a caterpillar given its spine, e.g. `Caterpillar([2,1,1])` is [this graph](https://houseofgraphs.org/graphs/54336) named $T_{4,3,3}$ after its degree sequence. I opted to use spines instead of degree sequences because it leads to a simpler implementation. I think it's worth to have this in Sage because it allows users to quickly obtain some common graphs by simply entering a list of numbers. While the graph generation process is fairly trivial, [producing nice embeddings of caterpillars](https://stackoverflow.com/questions/68791175/how-to-draw- caterpillar-trees-in-networkx-beautifully) is not. ### 📝 Checklist - [x] I have created tests covering the changes. - [ ] I have updated the documentation and checked the documentation preview. URL: sagemath#41245 Reported by: Lennard Hofmann Reviewer(s): David Coudert
2 parents c2ee396 + e3dff82 commit bd19340

File tree

4 files changed

+101
-3
lines changed

4 files changed

+101
-3
lines changed

build/pkgs/configure/checksums.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
tarball=configure-VERSION.tar.gz
2-
sha1=8b7aba4104cd77481cfc7602c26382d1788ecd2e
3-
sha256=99c4fd081a93073924a90d7b8af2dbb6fe23d8b4602115511fa1399080667023
2+
sha1=ea19ffc13676a6e4cc14c10fe88e624189463476
3+
sha256=148e79af8007b4bfe47bfad90db4bc048302e0e8382218496f0642ba105d10c4
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
4119163dc533ea5446f33e48918fd637a8e856e2
1+
78052986eb87204ac91589ff1019367598f7d014

src/sage/graphs/generators/trees.pyx

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,102 @@ def FibonacciTree(n):
274274
return T
275275

276276

277+
def Caterpillar(spine):
278+
r"""
279+
Return the caterpillar tree with given spine sequence.
280+
281+
A caterpillar tree consists of leaves attached to a path (the "spine").
282+
283+
INPUT:
284+
285+
- ``spine`` -- list of nonnegative integers in the form
286+
`[a_1, a_2, \dots, a_n]`, where `a_i` is the number of leaves adjacent
287+
to the `i`-th vertex on the spine (except for the first and last vertex,
288+
which have `a_1 + 1` and `a_n + 1` leaf-neighbors, respectively)
289+
290+
OUTPUT:
291+
292+
A caterpillar tree of diameter `n+1` on `n + 2 + \sum_{i=1}^n a_i` vertices,
293+
`n` of which are not leaves.
294+
295+
PLOTTING: Upon construction, the position dictionary is filled to override
296+
the spring-layout algorithm if the returned graph does not have too many
297+
vertices. The spine vertices are positioned on a straight line together
298+
with two leaves at its ends. Every edge in the drawing has unit length.
299+
300+
EXAMPLES:
301+
302+
Caterpillars with all-zero spine sequence are paths::
303+
304+
sage: graphs.Caterpillar([]).is_isomorphic(graphs.PathGraph(2))
305+
True
306+
sage: graphs.Caterpillar([0]).is_isomorphic(graphs.PathGraph(3))
307+
True
308+
sage: graphs.Caterpillar([0, 0]).is_isomorphic(graphs.PathGraph(4))
309+
True
310+
311+
Caterpillars with singleton spine are stars::
312+
313+
sage: graphs.Caterpillar([1]).is_isomorphic(graphs.StarGraph(3))
314+
True
315+
sage: graphs.Caterpillar([2]).is_isomorphic(graphs.StarGraph(4))
316+
True
317+
sage: graphs.Caterpillar([3]).is_isomorphic(graphs.StarGraph(5))
318+
True
319+
320+
Distinct spine sequences can yield isomorphic caterpillars::
321+
322+
sage: graphs.Caterpillar([1,1,2]).is_isomorphic(graphs.Caterpillar([2,1,1]))
323+
True
324+
325+
TESTS:
326+
327+
Generated graphs have diameter ``len(spine) + 1``::
328+
329+
sage: graphs.Caterpillar([7]).diameter()
330+
2
331+
sage: graphs.Caterpillar([2,2,2,2]).diameter()
332+
5
333+
sage: graphs.Caterpillar([0,1,1,0]).diameter()
334+
5
335+
"""
336+
spine = list(spine)
337+
cdef int spine_len = len(spine)
338+
cdef int n_vertices = spine_len + 2 + sum(spine)
339+
T = Graph(n_vertices, name=f"Caterpillar({','.join(map(str, spine))})")
340+
341+
# add spine
342+
for i in range(spine_len - 1):
343+
T._backend.add_edge(i, i + 1, None, False)
344+
345+
# add a leaf at both ends of the spine
346+
T._backend.add_edge(spine_len + 1, 0, None, False)
347+
if spine:
348+
T._backend.add_edge(spine_len - 1, spine_len, None, False)
349+
350+
# add leaves
351+
cdef int v = spine_len + 2
352+
for i, d in enumerate(spine):
353+
for j in range(d):
354+
T._backend.add_edge(i, v + j, None, False)
355+
v += d
356+
357+
# add embedding
358+
cdef int max_leaves = max(spine, default=0)
359+
if (spine_len < 10 and max_leaves < 3) or (spine_len < 6 and max_leaves < 7):
360+
T._pos = {spine_len + 1: (-1, 0), spine_len: (spine_len, 0)}
361+
radius = 0.3
362+
v = spine_len + 2
363+
for x, d in enumerate(spine):
364+
T._pos[x] = (x, 0)
365+
mid = v + d // 2
366+
T._line_embedding(range(v, mid), first=(x - radius, 1), last=(x + radius, 1))
367+
T._line_embedding(range(mid, v + d), first=(x - radius, -1), last=(x + radius, -1))
368+
v += d
369+
370+
return T
371+
372+
277373
def RandomLobster(n, p, q, seed=None):
278374
r"""
279375
Return a random lobster.

src/sage/graphs/graph_generators.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ def wrap_name(x):
383383
__append_to_doc(
384384
["BalancedTree",
385385
"FibonacciTree",
386+
"Caterpillar",
386387
"RandomLobster",
387388
"RandomTree",
388389
"RandomTreePowerlaw",
@@ -2880,6 +2881,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None
28802881
BalancedTree = staticmethod(gen_trees.BalancedTree)
28812882
FibonacciTree = staticmethod(gen_trees.FibonacciTree)
28822883
nauty_gentreeg = staticmethod(gen_trees.nauty_gentreeg)
2884+
Caterpillar = staticmethod(gen_trees.Caterpillar)
28832885
RandomLobster = staticmethod(gen_trees.RandomLobster)
28842886
RandomTreePowerlaw = staticmethod(gen_trees.RandomTreePowerlaw)
28852887
RandomTree = staticmethod(gen_trees.RandomTree)

0 commit comments

Comments
 (0)