Skip to content

Commit

Permalink
Trac #31590: Allow passing command to plantri
Browse files Browse the repository at this point in the history
Allow passing command to plantri, similar
to what `graphs.nauty_geng` allows for nauty.

As suggested in #31571.

URL: https://trac.sagemath.org/31590
Reported by: slelievre
Ticket author(s): David Coudert
Reviewer(s): Matthias Koeppe
  • Loading branch information
Release Manager committed Mar 8, 2022
2 parents 6376bce + 37a5b40 commit 314d846
Showing 1 changed file with 191 additions and 39 deletions.
230 changes: 191 additions & 39 deletions src/sage/graphs/graph_generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -1391,6 +1391,191 @@ def fusenes(self, hexagon_count, benzenoids=False):
for G in graphs._read_planar_code(sp.stdout):
yield(G)

def plantri_gen(self, options=""):
r"""
Iterator over planar graphs created using the ``plantri`` generator.
``plantri`` is a (optional) program that generates certain types of
graphs that are embedded on the sphere. It outputs exactly one member of
each isomorphism class, using an amount of memory almost independent of
the number of graphs produced. Isomorphisms are defined with respect to
the embeddings, so in some cases outputs may be isomorphic as abstract
graphs.
This method allows for passing command directly to ``plantry``,
similarly to method :meth:`nauty_geng`, provide that the output format
is not changed.
INPUT:
- ``options`` -- string (default: ``""``); a string passed to
``plantri`` as if it was run at a system command line. At a minimum,
you *must* pass the number of vertices you desire. Sage expects the
output of plantri to be in "planar code" format, so do not set an
option to change this default or results will be unpredictable.
The possible options are::
n : the number of vertices (the only compulsory parameter).
This number must be in range `3\cdots 64`.
It can also be given as "nd", where the suffix "d" means
"dual", in which case it is converted by adding 4 then
dividing by 2, i.e., `(28+4)/2 = 16`. In the case of
triangulations, this calculation yields the number of
faces, which is the number of vertices in the dual cubic
graph.
-d : output the dual instead of the original graph.
Note that it is applied only at the output stage. All
other switches refer to the original graph before the dual
is taken.
-o : Normally, one member of each isomorphism class is written.
If this switch is given, one member of each O-P
isomorphism class is written.
-V : output only graphs with non-trivial group. If -o is
given the O-P group is used, the full group otherwise.
-m<int> : lower bound on the minimum degree. The default is -m3.
In the dual graph, this means a lower bound on the minimum
face size.
-c<int> : lower bound on the connectivity. The default is -c3.
-x : when used in combination with -cN, the connectivity must
be exactly N rather than at least N.
-e : used to specify bounds on the number of edges.
There are four possible forms:
-e<int> exactly <int> edges
-e:<int> at most <int> edges
-e<int>: at least <int> edges
-e<int>:<int> between <int> and <int> edges
-f<int> : upper bound on the size of a face, and so on the maximum
degree of the dual.
-b but not -p : select eulerian triangulations, where "eulerian"
means that every vertex has even degree.
This parameter can be used in combination with
parameters -c and -x.
-p but not -b : select general planar simple graphs.
This parameter can be used in combination with
parameters -m, -c, -x, -e and -f.
-bp or -pb : select general planar simple bipartite graphs.
This parameter can be used in combination with
parameters -m, -c, -x, -e and -f, except -c4, -m4,
-m5 and -f3.
-P<int> : select triangulations of a disk. These are embedded simple
graphs with a distinguished "outer" face. The outer face
can be of any size (here called the disk size) but the
other faces must be triangles. The argument <int> to -P
is the disk size. If no argument (or 0) is given, all disk
sizes are permitted.
This parameter can be used in combination with
parameters -m, -c, and -x.
-q : select simple quadrangulations. These are planar simple
graphs for which every face has length 4.
This parameter can be used in combination with parameters
-c and -m.
-A : select Appolonian networks. These are simple planar
triangulations that can be formed starting with `K_4` then
repeatedly dividing a face into three by addition of a new
vertex. They all have minimum degree and connectivity
equal to 3.
res/mod : only generate subset res out of subsets 0..mod-1.
The set of objects is divided into mod disjoint classes
and only the res-th class is generated.
If -b, -q, -p, -P and -A are absent, the graphs found are triangulations
only restricted by connectivity and minimum degree. In this case,
there is the possibility of connectivity lower than 3.
Other options listed in the ``plantri`` guide might cause unpredictable
behavior, in particular those changing the output format of ``plantri``
as they will confuse the creation of a Sage graph.
OUTPUT:
An iterator which yields the graphs generated by ``plantri`` as Sage
:class:`~sage.graphs.graph.Graph`.
.. SEEALSO::
- :meth:`planar_graphs` -- iterator over connected planar graphs
using the ``plantri`` generator
- :meth:`triangulations` -- iterator over connected planar
triangulations using the ``plantri`` generator
- :meth:`quadrangulations` -- iterator over connected planar
quadrangulations using the ``plantri`` generator
EXAMPLES:
The generator can be used to construct graphs for testing, one at a time
(usually inside a loop). Or it can be used to create an entire list all
at once if there is sufficient memory to contain it::
sage: gen = graphs.plantri_gen("6") # optional plantri
sage: next(gen) # optional plantri
Graph on 6 vertices
sage: next(gen) # optional plantri
Graph on 6 vertices
sage: next(gen) # optional plantri
Traceback (most recent call last):
...
StopIteration
An overview of the number of quadrangulations on up to 12 vertices. This
agrees with :oeis:`A113201`::
sage: for i in range(4,13): # optional plantri
....: cmd = '-qm2c2 {}'.format(i) # optional plantri
....: L = len(list(graphs.plantri_gen(cmd))) # optional plantri
....: print("{:2d} {:3d}".format(i, L)) # optional plantri
4 1
5 1
6 2
7 3
8 9
9 18
10 62
11 198
12 803
TESTS:
Wrong input, ``"-c 3"`` instead of ``"-c3"``::
sage: list(graphs.plantri_gen("6 -c3")) # optional plantri
[Graph on 6 vertices, Graph on 6 vertices]
sage: list(graphs.plantri_gen("6 -c 3")) # optional plantri
Traceback (most recent call last):
...
AttributeError: invalid options '6 -c 3'
"""
from sage.features.graph_generators import Plantri
Plantri().require()

sp = subprocess.Popen('plantri {}'.format(options), shell=True,
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, close_fds=True,
encoding='latin-1')

sp.stdout.reconfigure(newline='')

try:
for G in graphs._read_planar_code(sp.stdout):
yield(G)
except AssertionError:
raise AttributeError("invalid options '{}'".format(options))

def planar_graphs(self, order, minimum_degree=None,
minimum_connectivity=None,
exact_connectivity=False,
Expand Down Expand Up @@ -1618,10 +1803,7 @@ def planar_graphs(self, order, minimum_degree=None,
yield(G)
return

from sage.features.graph_generators import Plantri
Plantri().require()

cmd = 'plantri -p{}m{}c{}{}{} {} {} {}'
cmd = '-p{}m{}c{}{}{} {} {} {}'
command = cmd.format('b' if only_bipartite else '',
minimum_degree,
minimum_connectivity,
Expand All @@ -1630,15 +1812,7 @@ def planar_graphs(self, order, minimum_degree=None,
edges, faces,
order)

sp = subprocess.Popen(command, shell=True,
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, close_fds=True,
encoding='latin-1')

sp.stdout.reconfigure(newline='')

for G in graphs._read_planar_code(sp.stdout):
yield(G)
yield from graphs.plantri_gen(command)

def triangulations(self, order, minimum_degree=None, minimum_connectivity=None,
exact_connectivity=False, only_eulerian=False, dual=False):
Expand Down Expand Up @@ -1804,26 +1978,15 @@ def triangulations(self, order, minimum_degree=None, minimum_connectivity=None,
if only_eulerian and order < 6:
return

from sage.features.graph_generators import Plantri
Plantri().require()

cmd = 'plantri -{}m{}c{}{}{} {}'
cmd = '-{}m{}c{}{}{} {}'
command = cmd.format('b' if only_eulerian else '',
minimum_degree,
minimum_connectivity,
'x' if exact_connectivity else '',
'd' if dual else '',
order)

sp = subprocess.Popen(command, shell=True,
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, close_fds=True,
encoding='latin-1')

sp.stdout.reconfigure(newline='')

for G in graphs._read_planar_code(sp.stdout):
yield(G)
yield from graphs.plantri_gen(command)

def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None,
no_nonfacial_quadrangles=False, dual=False):
Expand Down Expand Up @@ -1950,24 +2113,13 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None
# for plantri -q the option -c4 means 3-connected with no non-facial quadrangles
minimum_connectivity = 4

from sage.features.graph_generators import Plantri
Plantri().require()

cmd = 'plantri -qm{}c{}{} {}'
cmd = '-qm{}c{}{} {}'
command = cmd.format(minimum_degree,
minimum_connectivity,
'd' if dual else '',
order)

sp = subprocess.Popen(command, shell=True,
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, close_fds=True,
encoding='latin-1')

sp.stdout.reconfigure(newline='')

for G in graphs._read_planar_code(sp.stdout):
yield(G)
yield from graphs.plantri_gen(command)

###########################################################################
# Basic Graphs
Expand Down

0 comments on commit 314d846

Please sign in to comment.