Skip to content

Commit a6c3526

Browse files
author
Release Manager
committed
gh-39753: move tree generation methods to `src/sage/graphs/generators/trees.pyx` Fixes #39180 . This PR implements the changes proposed in #39180 (comment): * move `src/sage/graphs/trees.pyx` and `src/sage/graphs/trees.pxd` to `src/sage/graphs/generators/`. * move to this file all methods for generating trees, including `graphs.trees()`, `graphs.nauty_gentreeg()`, `graphs.BalancedTree()`, `graphs.FibonacciTree()`, `graphs.RandomTree()`, `graphs.RandomTreePowerlaw()`, `graphs.RandomLobster()`, etc. * add some documentation and examples showing the different ways to generate trees at the top of module `trees.pyx`. * ensure that all internal links to moved methods are properly updated. ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> - [x] The title is concise and informative. - [x] The description explains in detail what this PR is about. - [x] 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. ### ⌛ Dependencies <!-- List all open PRs that this PR logically depends on. For example, --> <!-- - #12345: short description why this is a dependency --> <!-- - #34567: ... --> URL: #39753 Reported by: David Coudert Reviewer(s): David Coudert, Frédéric Chapoton
2 parents ad2bb30 + 14c12f8 commit a6c3526

File tree

9 files changed

+954
-845
lines changed

9 files changed

+954
-845
lines changed

src/doc/en/reference/graphs/index.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,17 @@ Constructors and databases
2828
sage/graphs/graph_generators_pyx
2929
sage/graphs/graph_database
3030
sage/graphs/strongly_regular_db
31-
sage/graphs/generators/distance_regular
32-
sage/graphs/generators/classical_geometries
33-
sage/graphs/generators/families
3431
sage/graphs/generators/basic
3532
sage/graphs/generators/chessboard
33+
sage/graphs/generators/classical_geometries
34+
sage/graphs/generators/degree_sequence
35+
sage/graphs/generators/distance_regular
36+
sage/graphs/generators/families
3637
sage/graphs/generators/intersection
3738
sage/graphs/generators/platonic_solids
3839
sage/graphs/generators/random
3940
sage/graphs/generators/smallgraphs
41+
sage/graphs/generators/trees
4042
sage/graphs/generators/world_map
4143

4244
sage/graphs/isgci
@@ -86,7 +88,6 @@ Libraries of algorithms
8688
sage/graphs/line_graph
8789
sage/graphs/spanning_tree
8890
sage/graphs/pq_trees
89-
sage/graphs/trees
9091
sage/graphs/matching
9192
sage/graphs/matchpoly
9293
sage/graphs/genus

src/sage/graphs/generators/families.py

Lines changed: 1 addition & 336 deletions
Original file line numberDiff line numberDiff line change
@@ -495,107 +495,6 @@ def HammingGraph(n, q, X=None):
495495
return g
496496

497497

498-
def BalancedTree(r, h):
499-
r"""
500-
Return the perfectly balanced tree of height `h \geq 1`,
501-
whose root has degree `r \geq 2`.
502-
503-
The number of vertices of this graph is
504-
`1 + r + r^2 + \cdots + r^h`, that is,
505-
`\frac{r^{h+1} - 1}{r - 1}`. The number of edges is one
506-
less than the number of vertices.
507-
508-
INPUT:
509-
510-
- ``r`` -- positive integer `\geq 2`; the degree of the root node
511-
512-
- ``h`` -- positive integer `\geq 1`; the height of the balanced tree
513-
514-
OUTPUT:
515-
516-
The perfectly balanced tree of height `h \geq 1` and whose root has
517-
degree `r \geq 2`.
518-
519-
EXAMPLES:
520-
521-
A balanced tree whose root node has degree `r = 2`, and of height
522-
`h = 1`, has order 3 and size 2::
523-
524-
sage: G = graphs.BalancedTree(2, 1); G
525-
Balanced tree: Graph on 3 vertices
526-
sage: G.order()
527-
3
528-
sage: G.size()
529-
2
530-
sage: r = 2; h = 1
531-
sage: v = 1 + r
532-
sage: v; v - 1
533-
3
534-
2
535-
536-
Plot a balanced tree of height 5, whose root node has degree `r = 3`::
537-
538-
sage: G = graphs.BalancedTree(3, 5)
539-
sage: G.plot() # long time # needs sage.plot
540-
Graphics object consisting of 728 graphics primitives
541-
542-
A tree is bipartite. If its vertex set is finite, then it is planar. ::
543-
544-
sage: # needs networkx
545-
sage: r = randint(2, 5); h = randint(1, 7)
546-
sage: T = graphs.BalancedTree(r, h)
547-
sage: T.is_bipartite()
548-
True
549-
sage: T.is_planar()
550-
True
551-
sage: v = (r^(h + 1) - 1) / (r - 1)
552-
sage: T.order() == v
553-
True
554-
sage: T.size() == v - 1
555-
True
556-
557-
TESTS:
558-
559-
Normally we would only consider balanced trees whose root node
560-
has degree `r \geq 2`, but the construction degenerates
561-
gracefully::
562-
563-
sage: graphs.BalancedTree(1, 10)
564-
Balanced tree: Graph on 11 vertices
565-
566-
Similarly, we usually want the tree must have height `h \geq 1`
567-
but the algorithm also degenerates gracefully here::
568-
569-
sage: graphs.BalancedTree(3, 0)
570-
Balanced tree: Graph on 1 vertex
571-
572-
The construction is the same as the one of networkx::
573-
574-
sage: # needs networkx
575-
sage: import networkx
576-
sage: r = randint(2, 4); h = randint(1, 5)
577-
sage: T = graphs.BalancedTree(r, h)
578-
sage: N = Graph(networkx.balanced_tree(r, h), name="Balanced tree")
579-
sage: T.is_isomorphic(N)
580-
True
581-
"""
582-
# Compute the number of vertices per level of the tree
583-
order = [r**l for l in range(h + 1)]
584-
# Compute the first index of the vertices of a level
585-
begin = [0]
586-
begin.extend(begin[-1] + val for val in order)
587-
# The number of vertices of the tree is the first index of level h + 1
588-
T = Graph(begin[-1], name="Balanced tree")
589-
590-
# Add edges of the r-ary tree
591-
for level in range(h):
592-
start = begin[level + 1]
593-
for u in range(begin[level], begin[level + 1]):
594-
T.add_edges((u, v) for v in range(start, start + r))
595-
start += r
596-
return T
597-
598-
599498
def BarbellGraph(n1, n2):
600499
r"""
601500
Return a barbell graph with `2 n_1 + n_2` nodes.
@@ -1612,68 +1511,6 @@ def FuzzyBallGraph(partition, q):
16121511
return g
16131512

16141513

1615-
def FibonacciTree(n):
1616-
r"""
1617-
Return the graph of the Fibonacci Tree `F_{i}` of order `n`.
1618-
1619-
The Fibonacci tree `F_{i}` is recursively defined as the tree
1620-
with a root vertex and two attached child trees `F_{i-1}` and
1621-
`F_{i-2}`, where `F_{1}` is just one vertex and `F_{0}` is empty.
1622-
1623-
INPUT:
1624-
1625-
- ``n`` -- the recursion depth of the Fibonacci Tree
1626-
1627-
EXAMPLES::
1628-
1629-
sage: g = graphs.FibonacciTree(3) # needs sage.libs.pari
1630-
sage: g.is_tree() # needs sage.libs.pari
1631-
True
1632-
1633-
::
1634-
1635-
sage: l1 = [ len(graphs.FibonacciTree(_)) + 1 for _ in range(6) ] # needs sage.libs.pari
1636-
sage: l2 = list(fibonacci_sequence(2,8)) # needs sage.libs.pari
1637-
sage: l1 == l2 # needs sage.libs.pari
1638-
True
1639-
1640-
AUTHORS:
1641-
1642-
- Harald Schilly and Yann Laigle-Chapuy (2010-03-25)
1643-
"""
1644-
T = Graph(name=f"Fibonacci-Tree-{n}")
1645-
if n == 1:
1646-
T.add_vertex(0)
1647-
if n < 2:
1648-
return T
1649-
1650-
from sage.combinat.combinat import fibonacci_sequence
1651-
F = list(fibonacci_sequence(n + 2))
1652-
s = 1.618 ** (n / 1.618 - 1.618)
1653-
pos = {}
1654-
1655-
def fib(level, node, y):
1656-
pos[node] = (node, y)
1657-
if level < 2:
1658-
return
1659-
level -= 1
1660-
y -= s
1661-
diff = F[level]
1662-
T.add_edge(node, node - diff)
1663-
if level == 1: # only one child
1664-
pos[node - diff] = (node, y)
1665-
return
1666-
T.add_edge(node, node + diff)
1667-
fib(level, node - diff, y)
1668-
fib(level - 1, node + diff, y)
1669-
1670-
T.add_vertices(range(sum(F[:-1])))
1671-
fib(n, F[n + 1] - 1, 0)
1672-
T.set_pos(pos)
1673-
1674-
return T
1675-
1676-
16771514
def GeneralizedPetersenGraph(n, k):
16781515
r"""
16791516
Return a generalized Petersen graph with `2n` nodes. The variables
@@ -3631,179 +3468,6 @@ def WindmillGraph(k, n):
36313468
return G
36323469

36333470

3634-
def trees(vertices):
3635-
r"""
3636-
Return a generator of the distinct trees on a fixed number of vertices.
3637-
3638-
INPUT:
3639-
3640-
- ``vertices`` -- the size of the trees created
3641-
3642-
OUTPUT:
3643-
3644-
A generator which creates an exhaustive, duplicate-free listing
3645-
of the connected free (unlabeled) trees with ``vertices`` number
3646-
of vertices. A tree is a graph with no cycles.
3647-
3648-
ALGORITHM:
3649-
3650-
Uses an algorithm that generates each new tree
3651-
in constant time. See the documentation for, and implementation
3652-
of, the :mod:`sage.graphs.trees` module, including a citation.
3653-
3654-
EXAMPLES:
3655-
3656-
We create an iterator, then loop over its elements. ::
3657-
3658-
sage: tree_iterator = graphs.trees(7)
3659-
sage: for T in tree_iterator:
3660-
....: print(T.degree_sequence())
3661-
[2, 2, 2, 2, 2, 1, 1]
3662-
[3, 2, 2, 2, 1, 1, 1]
3663-
[3, 2, 2, 2, 1, 1, 1]
3664-
[4, 2, 2, 1, 1, 1, 1]
3665-
[3, 3, 2, 1, 1, 1, 1]
3666-
[3, 3, 2, 1, 1, 1, 1]
3667-
[4, 3, 1, 1, 1, 1, 1]
3668-
[3, 2, 2, 2, 1, 1, 1]
3669-
[4, 2, 2, 1, 1, 1, 1]
3670-
[5, 2, 1, 1, 1, 1, 1]
3671-
[6, 1, 1, 1, 1, 1, 1]
3672-
3673-
The number of trees on the first few vertex counts.
3674-
This is sequence A000055 in Sloane's OEIS. ::
3675-
3676-
sage: [len(list(graphs.trees(i))) for i in range(0, 15)]
3677-
[1, 1, 1, 1, 2, 3, 6, 11, 23, 47, 106, 235, 551, 1301, 3159]
3678-
"""
3679-
from sage.graphs.trees import TreeIterator
3680-
return iter(TreeIterator(vertices))
3681-
3682-
3683-
def nauty_gentreeg(options='', debug=False):
3684-
r"""
3685-
Return a generator which creates non-isomorphic trees from nauty's gentreeg
3686-
program.
3687-
3688-
INPUT:
3689-
3690-
- ``options`` -- string (default: ``""``); a string passed to ``gentreeg``
3691-
as if it was run at a system command line. At a minimum, you *must* pass
3692-
the number of vertices you desire. Sage expects the graphs to be in
3693-
nauty's "sparse6" format, do not set an option to change this default or
3694-
results will be unpredictable.
3695-
3696-
- ``debug`` -- boolean (default: ``False``); if ``True`` the first line of
3697-
``gentreeg``'s output to standard error is captured and the first call to
3698-
the generator's ``next()`` function will return this line as a string. A
3699-
line leading with ">A" indicates a successful initiation of the program
3700-
with some information on the arguments, while a line beginning with ">E"
3701-
indicates an error with the input.
3702-
3703-
The possible options, obtained as output of ``gentreeg -help``::
3704-
3705-
n : the number of vertices. Must be in range 1..128
3706-
res/mod : only generate subset res out of subsets 0..mod-1
3707-
-D<int> : an upper bound for the maximum degree
3708-
-Z<int>:<int> : bounds on the diameter
3709-
-q : suppress auxiliary output
3710-
3711-
Options which cause ``gentreeg`` to use an output format different than the
3712-
sparse6 format are not listed above (-p, -l, -u) as they will confuse the
3713-
creation of a Sage graph. The res/mod option can be useful when using the
3714-
output in a routine run several times in parallel.
3715-
3716-
OUTPUT:
3717-
3718-
A generator which will produce the graphs as Sage graphs. These will be
3719-
simple graphs: no loops, no multiple edges, no directed edges.
3720-
3721-
.. SEEALSO::
3722-
3723-
:meth:`trees` -- another generator of trees
3724-
3725-
EXAMPLES:
3726-
3727-
The generator can be used to construct trees for testing, one at a time
3728-
(usually inside a loop). Or it can be used to create an entire list all at
3729-
once if there is sufficient memory to contain it::
3730-
3731-
sage: gen = graphs.nauty_gentreeg("4")
3732-
sage: next(gen)
3733-
Graph on 4 vertices
3734-
sage: next(gen)
3735-
Graph on 4 vertices
3736-
sage: next(gen)
3737-
Traceback (most recent call last):
3738-
...
3739-
StopIteration
3740-
3741-
The number of trees on the first few vertex counts. This agrees with
3742-
:oeis:`A000055`::
3743-
3744-
sage: [len(list(graphs.nauty_gentreeg(str(i)))) for i in range(1, 15)]
3745-
[1, 1, 1, 2, 3, 6, 11, 23, 47, 106, 235, 551, 1301, 3159]
3746-
3747-
The ``debug`` switch can be used to examine ``gentreeg``'s reaction to the
3748-
input in the ``options`` string. We illustrate success. (A failure will be
3749-
a string beginning with ">E".) Passing the "-q" switch to ``gentreeg`` will
3750-
suppress the indicator of a successful initiation, and so the first returned
3751-
value might be an empty string if ``debug`` is ``True``::
3752-
3753-
sage: gen = graphs.nauty_gentreeg("4", debug=True)
3754-
sage: print(next(gen))
3755-
>A ...gentreeg ...
3756-
sage: gen = graphs.nauty_gentreeg("4 -q", debug=True)
3757-
sage: next(gen)
3758-
''
3759-
3760-
TESTS:
3761-
3762-
The number `n` of vertices must be in range 1..128::
3763-
3764-
sage: list(graphs.nauty_gentreeg("0", debug=False))
3765-
Traceback (most recent call last):
3766-
...
3767-
ValueError: wrong format of parameter options
3768-
sage: list(graphs.nauty_gentreeg("0", debug=True))
3769-
['>E gentreeg: n must be in the range 1..128\n']
3770-
sage: list(graphs.nauty_gentreeg("200", debug=True))
3771-
['>E gentreeg: n must be in the range 1..128\n']
3772-
3773-
Wrong input::
3774-
3775-
sage: list(graphs.nauty_gentreeg("3 -x", debug=False))
3776-
Traceback (most recent call last):
3777-
...
3778-
ValueError: wrong format of parameter options
3779-
sage: list(graphs.nauty_gentreeg("3 -x", debug=True))
3780-
['>E Usage: ...gentreeg [-D#] [-Z#:#] [-ulps] [-q] n... [res/mod] ...
3781-
sage: list(graphs.nauty_gentreeg("3", debug=True))
3782-
['>A ...gentreeg ...\n', Graph on 3 vertices]
3783-
"""
3784-
import shlex
3785-
from sage.features.nauty import NautyExecutable
3786-
gen_path = NautyExecutable("gentreeg").absolute_filename()
3787-
sp = subprocess.Popen(shlex.quote(gen_path) + " {0}".format(options), shell=True,
3788-
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
3789-
stderr=subprocess.PIPE, close_fds=True,
3790-
encoding='latin-1')
3791-
msg = sp.stderr.readline()
3792-
if debug:
3793-
yield msg
3794-
elif msg.startswith('>E'):
3795-
raise ValueError('wrong format of parameter options')
3796-
gen = sp.stdout
3797-
while True:
3798-
try:
3799-
s = next(gen)
3800-
except StopIteration:
3801-
# Exhausted list of graphs from nauty geng
3802-
return
3803-
G = Graph(s[:-1], format='sparse6', loops=False, multiedges=False)
3804-
yield G
3805-
3806-
38073471
def RingedTree(k, vertex_labels=True):
38083472
r"""
38093473
Return the ringed tree on k-levels.
@@ -3852,6 +3516,7 @@ def RingedTree(k, vertex_labels=True):
38523516
raise ValueError('The number of levels must be >= 1.')
38533517

38543518
# Creating the Balanced tree, which contains most edges already
3519+
from sage.graphs.generators.trees import BalancedTree
38553520
g = BalancedTree(2, k - 1)
38563521
g.name('Ringed Tree on ' + str(k) + ' levels')
38573522

0 commit comments

Comments
 (0)